diff --git a/sys/src/9/pc/etheriwl.c b/sys/src/9/pc/etheriwl.c index 19fd2db70..9fff050bc 100644 --- a/sys/src/9/pc/etheriwl.c +++ b/sys/src/9/pc/etheriwl.c @@ -222,6 +222,28 @@ enum { 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 @@ struct Ctlr { 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 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block) 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,12 +1095,42 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block) 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); } +static void +flushcmd(Ctlr *ctlr) +{ + flushq(ctlr, 4); +} + static void setled(Ctlr *ctlr, int which, int on, int off) { @@ -1099,9 +1157,6 @@ postboot(Ctlr *ctlr) 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 @@ rxon(Ether *edev, Wnode *bss) { 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 @@ rxon(Ether *edev, Wnode *bss) 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 @@ rxon(Ether *edev, Wnode *bss) 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 @@ static struct ratetab { 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 @@ static struct ratetab { { 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 @@ setoptions(Ether *edev) 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()){ @@ -1394,9 +1522,78 @@ setoptions(Ether *edev) } } +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 @@ iwlattach(Ether *edev) 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 @@ again: edev->attach = iwlattach; edev->ifstat = iwlifstat; edev->ctl = iwlctl; - edev->promiscuous = nil; + edev->promiscuous = iwlpromiscuous; edev->multicast = nil; edev->mbps = 10; diff --git a/sys/src/9/pc/mkfile b/sys/src/9/pc/mkfile index 6965ffc7c..5b68b85a2 100644 --- a/sys/src/9/pc/mkfile +++ b/sys/src/9/pc/mkfile @@ -121,6 +121,7 @@ trap.$O: /sys/include/tos.h 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 diff --git a/sys/src/9/pc/wifi.c b/sys/src/9/pc/wifi.c index 3f5cb8153..04410d615 100644 --- a/sys/src/9/pc/wifi.c +++ b/sys/src/9/pc/wifi.c @@ -93,12 +93,12 @@ drop: } 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 @@ wifitx(Wifi *wifi, Block *b) w->seq[0] = seq; w->seq[1] = seq>>8; - (*wifi->transmit)(wifi, wifi->bss, b); + (*wifi->transmit)(wifi, wn, b); } @@ -118,18 +118,29 @@ nodelookup(Wifi *wifi, uchar *bssid, int new) if(memcmp(bssid, wifi->ether->bcast, Eaddrlen) == 0) return nil; - for(wn = nn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){ + if((wn = wifi->bss) != nil){ if(memcmp(wn->bssid, bssid, Eaddrlen) == 0){ wn->lastseen = MACHP(0)->ticks; return wn; } - if(wn != wifi->bss && wn->lastseen < nn->lastseen) + } + 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->lastseen < nn->lastseen) nn = wn; } if(!new) 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 @@ sendauth(Wifi *wifi, Wnode *bss) *p++ = 0; /* status */ *p++ = 0; b->wp = p; - wifitx(wifi, b); + wifitx(wifi, bss, b); } static void @@ -190,7 +201,7 @@ sendassoc(Wifi *wifi, Wnode *bss) *p++ = 0x8b; *p++ = 0x96; b->wp = p; - wifitx(wifi, b); + wifitx(wifi, bss, b); } static void @@ -210,13 +221,14 @@ recvassoc(Wifi *wifi, Wnode *wn, uchar *d, int len) wifi->status = Sassoc; break; default: + wn->aid = 0; wifi->status = Sunassoc; return; } } 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 @@ recvbeacon(Wifi *wifi, Wnode *wn, uchar *d, int len) 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 @@ wifiproc(void *arg) 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 @@ wifiproc(void *arg) sendassoc(wifi, wn); break; case 0xc0: /* deauth */ + wn->aid = 0; wifi->status = Sunauth; + sendauth(wifi, wn); break; } } @@ -318,6 +332,7 @@ wifietheroq(Wifi *wifi, Block *b) { Etherpkt e; Wifipkt *w; + Wnode *bss; SNAP *s; if(BLEN(b) < ETHERHDRSIZE){ @@ -332,7 +347,8 @@ wifietheroq(Wifi *wifi, Block *b) 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 @@ wifietheroq(Wifi *wifi, Block *b) s->orgcode[2] = 0; memmove(s->type, e.type, 2); - wifitx(wifi, b); + wifitx(wifi, bss, b); } static void @@ -364,6 +380,7 @@ wifoproc(void *arg) Wifi* wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*)) { + char name[32]; Wifi *wifi; wifi = malloc(sizeof(Wifi)); @@ -372,8 +389,10 @@ wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*)) 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 @@ wifictl(Wifi *wifi, void *buf, long n) 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 @@ wifistat(Wifi *wifi, void *buf, long n, ulong off) 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++){ diff --git a/sys/src/9/pc/wifi.h b/sys/src/9/pc/wifi.h index 25a290d6c..1b04933b6 100644 --- a/sys/src/9/pc/wifi.h +++ b/sys/src/9/pc/wifi.h @@ -34,13 +34,13 @@ struct Wifi 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*));