summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/src/9/pc/etheriwl.c283
-rw-r--r--sys/src/9/pc/mkfile1
-rw-r--r--sys/src/9/pc/wifi.c55
-rw-r--r--sys/src/9/pc/wifi.h6
4 files changed, 283 insertions, 62 deletions
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,6 +1095,30 @@ 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)
{
@@ -1074,6 +1126,12 @@ cmd(Ctlr *ctlr, uint code, uchar *data, int size)
}
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 @@ 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;
+
+ 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;
- ctlr = wifi->ether->ctlr;
+ 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()){
@@ -1395,8 +1523,77 @@ 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;
+ 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)
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*));