git: 9front

Download patch

ref: d9ce50dbc7feb8d303a311402d4856dce05c44e7
parent: c27e21add7f4f1432216d0519ba2e1eafc91f12b
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Sun Feb 10 19:09:41 EST 2013

etheriwl: automatic channel scanning, transmission handling, promisc mode

the channel= plan9.ini parameter isnt needed anymore as we now
hop the channels to scan for beacons. the status is also indicated
with the link led :-)

handle all these flags on packet transmission like RTS for big
packets and sending data packets to the AP instead of broadcasting
everything.

properly setup bss hardware node table and filtering. now promisc
mode is only used when requested.

handle deauth message from ap.

increase node table to 32 entries.

--- a/sys/src/9/pc/etheriwl.c
+++ b/sys/src/9/pc/etheriwl.c
@@ -222,6 +222,28 @@
 	SchedTransTblOff5000	= 0x7e0,
 };
 
+enum {
+	FilterPromisc		= 1<<0,
+	FilterCtl		= 1<<1,
+	FilterMulticast		= 1<<2,
+	FilterNoDecrypt		= 1<<3,
+	FilterBSS		= 1<<5,
+	FilterBeacon		= 1<<6,
+};
+
+enum {
+	RFlag24Ghz		= 1<<0,
+	RFlagCCK		= 1<<1,
+	RFlagAuto		= 1<<2,
+	RFlagShSlot		= 1<<4,
+	RFlagShPreamble		= 1<<5,
+	RFlagNoDiversity	= 1<<7,
+	RFlagAntennaA		= 1<<8,
+	RFlagAntennaB		= 1<<9,
+	RFlagTSF		= 1<<15,
+	RFlagCTSToSelf		= 1<<30,
+};
+
 typedef struct FWInfo FWInfo;
 typedef struct FWImage FWImage;
 typedef struct FWSect FWSect;
@@ -303,7 +325,14 @@
 	u32int *nic;
 	uchar *kwpage;
 
+	/* assigned node ids in hardware node table or -1 if unassigned */
+	int bcastnodeid;
+	int bssnodeid;
+
+	/* current receiver settings */
 	int channel;
+	int prom;
+	int aid;
 
 	RXQ rx;
 	TXQ tx[20];
@@ -1019,12 +1048,11 @@
 	q = &ctlr->tx[qid];
 	while(q->n >= Ntx){
 		iunlock(ctlr);
-		eqlock(q);
-		if(waserror()){
-			qunlock(q);
-			nexterror();
+		qlock(q);
+		if(!waserror()){
+			tsleep(q, txqready, q, 10);
+			poperror();
 		}
-		tsleep(q, txqready, q, 10);
 		qunlock(q);
 		ilock(ctlr);
 	}
@@ -1067,7 +1095,31 @@
 	iunlock(ctlr);
 }
 
+static int
+txqempty(void *arg)
+{
+	TXQ *q = arg;
+	return q->n == 0;
+}
+
 static void
+flushq(Ctlr *ctlr, uint qid)
+{
+	TXQ *q;
+
+	q = &ctlr->tx[qid];
+	while(q->n > 0){
+		qlock(q);
+		if(!waserror()){
+			tsleep(q, txqempty, q, 10);
+			poperror();
+		}
+		qunlock(q);
+	}
+}
+
+
+static void
 cmd(Ctlr *ctlr, uint code, uchar *data, int size)
 {
 	qcmd(ctlr, 4, code, data, size, nil);
@@ -1074,6 +1126,12 @@
 }
 
 static void
+flushcmd(Ctlr *ctlr)
+{
+	flushq(ctlr, 4);
+}
+
+static void
 setled(Ctlr *ctlr, int which, int on, int off)
 {
 	uchar c[8];
@@ -1099,9 +1157,6 @@
 	char *err;
 	int i, q;
 
-	/* main led turn on! (verify that firmware processes commands) */
-	setled(ctlr, 2, 0, 1);
-
 	if((err = niclock(ctlr)) != nil)
 		error(err);
 
@@ -1228,12 +1283,42 @@
 {
 	uchar c[Tcmdsize], *p;
 	Ctlr *ctlr;
+	uchar *bssid;
+	int filter, flags;
 
 	ctlr = edev->ctlr;
+	bssid = edev->bcast;
+	filter = FilterMulticast | FilterBeacon;
+	if(ctlr->prom)
+		filter |= FilterPromisc;
+	if(bss != nil){
+		ctlr->channel = bss->channel;
+		if(bss->aid != 0){
+			bssid = bss->bssid;
+			filter |= FilterBSS;
+			filter &= ~FilterBeacon;
+			ctlr->aid = bss->aid;
+
+			ctlr->bssnodeid = -1;
+		} else {
+			filter &= ~FilterBSS;
+			filter |= FilterBeacon;
+			ctlr->aid = 0;
+	
+			ctlr->bcastnodeid = -1;
+		}
+	} else {
+		ctlr->bcastnodeid = -1;
+		ctlr->bssnodeid = -1;
+	}
+	flags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto;
+
+	if(0) print("rxon: bssid %E, aid %x, channel %d, filter %x, flags %x\n",
+		bssid, ctlr->aid, ctlr->channel, filter, flags);
+
 	memset(p = c, 0, sizeof(c));
 	memmove(p, edev->ea, 6); p += 8;	/* myaddr */
-	memmove(p, (bss != nil) ? bss->bssid : edev->bcast, 6);
-	p += 8;					/* bssid */
+	memmove(p, bssid, 6); p += 8;		/* bssid */
 	memmove(p, edev->ea, 6); p += 8;	/* wlap */
 	*p++ = 3;				/* mode (STA) */
 	*p++ = 0;				/* air (?) */
@@ -1242,14 +1327,13 @@
 	p += 2;
 	*p++ = 0xff;				/* ofdm mask (not yet negotiated) */
 	*p++ = 0x0f;				/* cck mask (not yet negotiated) */
-	if(bss != nil)
-		put16(p, bss->aid & ~0xc000);
+	put16(p, ctlr->aid & 0x3fff);
 	p += 2;					/* aid */
-	put32(p, (1<<15)|(1<<30)|(1<<0));	/* flags (TSF | CTS_TO_SELF | 24GHZ) */
+	put32(p, flags);
 	p += 4;
-	put32(p, 8|4|1);			/* filter (NODECRYPT|MULTICAST|PROMISC) */
+	put32(p, filter);
 	p += 4;
-	*p++ = bss != nil ? bss->channel : ctlr->channel;
+	*p++ = ctlr->channel;
 	p++;					/* reserved */
 	*p++ = 0xff;				/* ht single mask */
 	*p++ = 0xff;				/* ht dual mask */
@@ -1260,10 +1344,14 @@
 		p += 2;				/* reserved */
 	}
 	cmd(ctlr, 16, c, p - c);
-
-	addnode(ctlr, (ctlr->type != Type4965) ? 15 : 31, edev->bcast);
-	if(bss != nil)
-		addnode(ctlr, 0, bss->bssid);
+	if(ctlr->bcastnodeid == -1){
+		ctlr->bcastnodeid = (ctlr->type != Type4965) ? 15 : 31;
+		addnode(ctlr, ctlr->bcastnodeid, edev->bcast);
+	}
+	if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){
+		ctlr->bssnodeid = 0;
+		addnode(ctlr, ctlr->bssnodeid, bss->bssid);
+	}
 }
 
 static struct ratetab {
@@ -1271,10 +1359,10 @@
 	uchar	plcp;
 	uchar	flags;
 } ratetab[] = {
-	{   2,  10, 1<<1 },
-	{   4,  20, 1<<1 },
-	{  11,  55, 1<<1 },
-	{  22, 110, 1<<1 },
+	{   2,  10, RFlagCCK },
+	{   4,  20, RFlagCCK },
+	{  11,  55, RFlagCCK },
+	{  22, 110, RFlagCCK },
 	{  12, 0xd, 0 },
 	{  18, 0xf, 0 },
 	{  24, 0x5, 0 },
@@ -1286,32 +1374,76 @@
 	{ 120, 0x3, 0 }
 };
 
+enum {
+	TFlagNeedProtection	= 1<<0,
+	TFlagNeedRTS		= 1<<1,
+	TFlagNeedCTS		= 1<<2,
+	TFlagNeedACK		= 1<<3,
+	TFlagLinkq		= 1<<4,
+	TFlagImmBa		= 1<<6,
+	TFlagFullTxOp		= 1<<7,
+	TFlagBtDis		= 1<<12,
+	TFlagAutoSeq		= 1<<13,
+	TFlagMoreFrag		= 1<<14,
+	TFlagInsertTs		= 1<<16,
+	TFlagNeedPadding	= 1<<20,
+};
+
 static void
-transmit(Wifi *wifi, Wnode *, Block *b)
+transmit(Wifi *wifi, Wnode *wn, Block *b)
 {
 	uchar c[Tcmdsize], *p;
+	Ether *edev;
 	Ctlr *ctlr;
+	Wifipkt *w;
+	int flags, nodeid, rate;
 
-	ctlr = wifi->ether->ctlr;
+	w = (Wifipkt*)b->rp;
+	edev = wifi->ether;
+	ctlr = edev->ctlr;
 
+	qlock(ctlr);
+	if(wn != nil && (wn->aid != ctlr->aid || wn->channel != ctlr->channel))
+		rxon(edev, wn);
+
+	rate = 0;
+	flags = 0;
+	nodeid = ctlr->bcastnodeid;
+	if((w->a1[0] & 1) == 0){
+		flags |= TFlagNeedACK;
+
+		if(BLEN(b) > 512-4)
+			flags |= TFlagNeedRTS;
+
+		if((w->fc[0] & 0x0c) == 0x08 &&	ctlr->bssnodeid != -1){
+			nodeid = ctlr->bssnodeid;
+			rate = 2; /* BUG: hardcode 11Mbit */
+		}
+
+		if(flags & (TFlagNeedRTS|TFlagNeedCTS)){
+			if(ctlr->type != Type4965){
+				flags &= ~(TFlagNeedRTS|TFlagNeedCTS);
+				flags |= TFlagNeedProtection;
+			} else
+				flags |= TFlagFullTxOp;
+		}
+	}
+	qunlock(ctlr);
+
 	memset(p = c, 0, sizeof(c));
 	put16(p, BLEN(b));
 	p += 2;
 	p += 2;		/* lnext */
-	put32(p, 0);	/* flags */
+	put32(p, flags);
 	p += 4;
 	put32(p, 0);
 	p += 4;		/* scratch */
 
-	/* BUG: hardcode 11Mbit */
-	*p++ = ratetab[2].plcp;			/* plcp */
-	*p++ = ratetab[2].flags | (1<<6);	/* rflags */
+	*p++ = ratetab[rate].plcp;
+	*p++ = ratetab[rate].flags | (1<<6);
 
 	p += 2;		/* xflags */
-
-	/* BUG: we always use broadcast node! */
-	*p++ = (ctlr->type != Type4965) ? 15 : 31;
-
+	*p++ = nodeid;
 	*p++ = 0;	/* security */
 	*p++ = 0;	/* linkq */
 	p++;		/* reserved */
@@ -1379,11 +1511,7 @@
 	int i;
 
 	ctlr = edev->ctlr;
-	ctlr->channel = 3;
 	for(i = 0; i < edev->nopt; i++){
-		if(strncmp(edev->opt[i], "channel=", 8) == 0)
-			ctlr->channel = atoi(edev->opt[i]+8);
-		else
 		if(strncmp(edev->opt[i], "essid=", 6) == 0){
 			snprint(buf, sizeof(buf), "essid %s", edev->opt[i]+6);
 			if(!waserror()){
@@ -1395,8 +1523,77 @@
 }
 
 static void
+iwlpromiscuous(void *arg, int on)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	qlock(ctlr);
+	ctlr->prom = on;
+	if(ctlr->prom)
+		rxon(edev, nil);
+	else
+		rxon(edev, ctlr->wifi->bss);
+	qunlock(ctlr);
+}
+
+static void
+iwlproc(void *arg)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+	Wifi *wifi;
+	Wnode *bss;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	wifi = ctlr->wifi;
+
+	for(;;){
+		/* hop channels for catching beacons */
+		setled(ctlr, 2, 5, 5);
+		while(wifi->bss == nil){
+			qlock(ctlr);
+			if(wifi->bss != nil){
+				qunlock(ctlr);
+				break;
+			}
+			ctlr->channel = 1 + ctlr->channel % 11;
+			ctlr->aid = 0;
+			rxon(edev, nil);
+			qunlock(ctlr);
+			tsleep(&up->sleep, return0, 0, 1000);
+		}
+
+		/* wait for association */
+		setled(ctlr, 2, 10, 10);
+		while((bss = wifi->bss) != nil){
+			if(bss->aid != 0)
+				break;
+			tsleep(&up->sleep, return0, 0, 1000);
+		}
+
+		if(bss == nil)
+			continue;
+
+		/* wait for disassociation */
+		edev->link = 1;
+		setled(ctlr, 2, 0, 1);
+		while((bss = wifi->bss) != nil){
+			if(bss->aid == 0)
+				break;
+			tsleep(&up->sleep, return0, 0, 1000);
+		}
+		edev->link = 0;
+	}
+}
+
+static void
 iwlattach(Ether *edev)
 {
+	char name[32];
 	FWImage *fw;
 	Ctlr *ctlr;
 	char *err;
@@ -1528,12 +1725,16 @@
 		bootfirmware(ctlr);
 		postboot(ctlr);
 
+		ctlr->bcastnodeid = -1;
+		ctlr->bssnodeid = -1;
+		ctlr->channel = 1;
+		ctlr->aid = 0;
+
 		setoptions(edev);
 
-		rxon(edev, nil);
+		snprint(name, sizeof(name), "#l%diwl", edev->ctlrno);
+		kproc(name, iwlproc, edev);
 
-		edev->prom = 1;
-		edev->link = 1;
 		ctlr->attached = 1;
 	}
 	qunlock(ctlr);
@@ -1783,7 +1984,7 @@
 	edev->attach = iwlattach;
 	edev->ifstat = iwlifstat;
 	edev->ctl = iwlctl;
-	edev->promiscuous = nil;
+	edev->promiscuous = iwlpromiscuous;
 	edev->multicast = nil;
 	edev->mbps = 10;
 
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -121,6 +121,7 @@
 uartaxp.$O:			uartaxp.i
 etherm10g.$O:			etherm10g2k.i etherm10g4k.i
 etheriwl.$O:			wifi.h
+wifi.$O:			wifi.h
 
 init.h:D:		../port/initcode.c init9.c
 	$CC ../port/initcode.c
--- a/sys/src/9/pc/wifi.c
+++ b/sys/src/9/pc/wifi.c
@@ -93,12 +93,12 @@
 }
 
 static void
-wifitx(Wifi *wifi, Block *b)
+wifitx(Wifi *wifi, Wnode *wn, Block *b)
 {
 	Wifipkt *w;
 	uint seq;
 
-	seq = wifi->txseq++;
+	seq = incref(&wifi->txseq);
 	seq <<= 4;
 
 	w = (Wifipkt*)b->rp;
@@ -107,7 +107,7 @@
 	w->seq[0] = seq;
 	w->seq[1] = seq>>8;
 
-	(*wifi->transmit)(wifi, wifi->bss, b);
+	(*wifi->transmit)(wifi, wn, b);
 }
 
 
@@ -118,12 +118,20 @@
 
 	if(memcmp(bssid, wifi->ether->bcast, Eaddrlen) == 0)
 		return nil;
+	if((wn = wifi->bss) != nil){
+		if(memcmp(wn->bssid, bssid, Eaddrlen) == 0){
+			wn->lastseen = MACHP(0)->ticks;
+			return wn;
+		}
+	}
 	for(wn = nn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){
+		if(wn == wifi->bss)
+			continue;
 		if(memcmp(wn->bssid, bssid, Eaddrlen) == 0){
 			wn->lastseen = MACHP(0)->ticks;
 			return wn;
 		}
-		if(wn != wifi->bss && wn->lastseen < nn->lastseen)
+		if(wn->lastseen < nn->lastseen)
 			nn = wn;
 	}
 	if(!new)
@@ -130,6 +138,9 @@
 		return nil;
 	memmove(nn->bssid, bssid, Eaddrlen);
 	nn->lastseen = MACHP(0)->ticks;
+	nn->channel = 0;
+	nn->cap = 0;
+	nn->aid = 0;
 	return nn;
 }
 
@@ -156,7 +167,7 @@
 	*p++ = 0;	/* status */
 	*p++ = 0;
 	b->wp = p;
-	wifitx(wifi, b);
+	wifitx(wifi, bss, b);
 }
 
 static void
@@ -190,7 +201,7 @@
 	*p++ = 0x8b;
 	*p++ = 0x96;
 	b->wp = p;
-	wifitx(wifi, b);
+	wifitx(wifi, bss, b);
 }
 
 static void
@@ -210,6 +221,7 @@
 		wifi->status = Sassoc;
 		break;
 	default:
+		wn->aid = 0;
 		wifi->status = Sunassoc;
 		return;
 	}
@@ -216,7 +228,7 @@
 }
 
 static void
-recvbeacon(Wifi *wifi, Wnode *wn, uchar *d, int len)
+recvbeacon(Wifi *, Wnode *wn, uchar *d, int len)
 {
 	uchar *e, *x;
 	uchar t, m[256/8];
@@ -251,11 +263,6 @@
 			if(len != strlen(wn->ssid) || strncmp(wn->ssid, (char*)d, len) != 0){
 				strncpy(wn->ssid, (char*)d, len);
 				wn->ssid[len] = 0;
-				if(wifi->bss == nil && strcmp(wifi->essid, wn->ssid) == 0){
-					wifi->bss = wn;
-					wifi->status = Sconn;
-					sendauth(wifi, wn);
-				}
 			}
 			break;
 		case 3:	/* DSPARAMS */
@@ -289,6 +296,11 @@
 				continue;
 			b->rp += WIFIHDRSIZE;
 			recvbeacon(wifi, wn, b->rp, BLEN(b));
+			if(wifi->bss == nil && wifi->essid[0] != 0 && strcmp(wifi->essid, wn->ssid) == 0){
+				wifi->bss = wn;
+				wifi->status = Sconn;
+				sendauth(wifi, wn);
+			}
 			continue;
 		}
 		if((wn = nodelookup(wifi, w->a3, 0)) == nil)
@@ -306,7 +318,9 @@
 			sendassoc(wifi, wn);
 			break;
 		case 0xc0:	/* deauth */
+			wn->aid = 0;
 			wifi->status = Sunauth;
+			sendauth(wifi, wn);
 			break;
 		}
 	}
@@ -318,6 +332,7 @@
 {
 	Etherpkt e;
 	Wifipkt *w;
+	Wnode *bss;
 	SNAP *s;
 
 	if(BLEN(b) < ETHERHDRSIZE){
@@ -332,7 +347,8 @@
 	w = (Wifipkt*)b->rp;
 	w->fc[0] = 0x08;	/* data */
 	w->fc[1] = 0x01;	/* STA->AP */
-	memmove(w->a1, wifi->bss ? wifi->bss->bssid : wifi->ether->bcast, Eaddrlen);
+	bss = wifi->bss;
+	memmove(w->a1, bss != nil ? bss->bssid : wifi->ether->bcast, Eaddrlen);
 	memmove(w->a2, e.s, Eaddrlen);
 	memmove(w->a3, e.d, Eaddrlen);
 
@@ -344,7 +360,7 @@
 	s->orgcode[2] = 0;
 	memmove(s->type, e.type, 2);
 
-	wifitx(wifi, b);
+	wifitx(wifi, bss, b);
 }
 
 static void
@@ -364,6 +380,7 @@
 Wifi*
 wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*))
 {
+	char name[32];
 	Wifi *wifi;
 
 	wifi = malloc(sizeof(Wifi));
@@ -372,8 +389,10 @@
 	wifi->transmit = transmit;
 	wifi->status = Snone;
 
-	kproc("wifi", wifiproc, wifi);
-	kproc("wifo", wifoproc, wifi);
+	snprint(name, sizeof(name), "#l%dwifi", ether->ctlrno);
+	kproc(name, wifiproc, wifi);
+	snprint(name, sizeof(name), "#l%dwifo", ether->ctlrno);
+	kproc(name, wifoproc, wifi);
 
 	return wifi;
 }
@@ -392,7 +411,6 @@
 	cb = parsecmd(buf, n);
 	if(cb->f[0] && strcmp(cb->f[0], "essid") == 0){
 		if(cb->f[1] == nil){
-			/* TODO senddeauth(wifi); */
 			wifi->essid[0] = 0;
 			wifi->bss = nil;
 		} else {
@@ -425,7 +443,8 @@
 
 	p = seprint(p, e, "status: %s\n", wifi->status);
 	p = seprint(p, e, "essid: %s\n", wifi->essid);
-	p = seprint(p, e, "bssid: %E\n", wifi->bss ? wifi->bss->bssid : zeros);
+	wn = wifi->bss;
+	p = seprint(p, e, "bssid: %E\n", wn != nil ? wn->bssid : zeros);
 
 	now = MACHP(0)->ticks;
 	for(wn=wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){
--- a/sys/src/9/pc/wifi.h
+++ b/sys/src/9/pc/wifi.h
@@ -34,13 +34,13 @@
 
 	Queue	*iq;
 	char	*status;
+	Ref	txseq;
 	void	(*transmit)(Wifi*, Wnode*, Block*);
 
-	Wnode	node[16];
+	char	essid[32+2];
 	Wnode	*bss;
 
-	uint	txseq;
-	char	essid[32+2];
+	Wnode	node[32];
 };
 
 Wifi *wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*));
--