--- /sys/src/9/pc/etheriwl.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/pc/etheriwl.c Tue Apr 22 00:00:00 2014 @@ -0,0 +1,2400 @@ +/* + * Intel WiFi Link driver. + * + * Written without any documentation but Damien Bergaminis + * OpenBSD iwn(4) driver sources. Requires intel firmware + * to be present in /lib/firmware/iwn-* on attach. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "wifi.h" + +enum { + Ntxlog = 8, + Ntx = 1<nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static uint +get16(uchar *p){ + return *((u16int*)p); +} +static uint +get32(uchar *p){ + return *((u32int*)p); +} +static void +put32(uchar *p, uint v){ + *((u32int*)p) = v; +} +static void +put16(uchar *p, uint v){ + *((u16int*)p) = v; +}; + +static char* +niclock(Ctlr *ctlr) +{ + int i; + + csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | MacAccessReq); + for(i=0; i<1000; i++){ + if((csr32r(ctlr, Gpc) & (NicSleep | MacAccessEna)) == MacAccessEna) + return 0; + delay(10); + } + return "niclock: timeout"; +} + +static void +nicunlock(Ctlr *ctlr) +{ + csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) & ~MacAccessReq); +} + +static u32int +prphread(Ctlr *ctlr, uint off) +{ + csr32w(ctlr, PrphRaddr, ((sizeof(u32int)-1)<<24) | off); + coherence(); + return csr32r(ctlr, PrphRdata); +} +static void +prphwrite(Ctlr *ctlr, uint off, u32int data) +{ + csr32w(ctlr, PrphWaddr, ((sizeof(u32int)-1)<<24) | off); + coherence(); + csr32w(ctlr, PrphWdata, data); +} + +static u32int +memread(Ctlr *ctlr, uint off) +{ + csr32w(ctlr, MemRaddr, off); + coherence(); + return csr32r(ctlr, MemRdata); +} +static void +memwrite(Ctlr *ctlr, uint off, u32int data) +{ + csr32w(ctlr, MemWaddr, off); + coherence(); + csr32w(ctlr, MemWdata, data); +} + +static void +setfwinfo(Ctlr *ctlr, uchar *d, int len) +{ + FWInfo *i; + + if(len < 32) + return; + i = &ctlr->fwinfo; + i->minjor = *d++; + i->major = *d++; + d += 2+8; + i->type = *d++; + i->subtype = *d++; + d += 2; + i->logptr = get32(d); d += 4; + i->errptr = get32(d); d += 4; + i->tstamp = get32(d); d += 4; + i->valid = get32(d); +}; + +static void +dumpctlr(Ctlr *ctlr) +{ + u32int dump[13]; + int i; + + print("lastcmd: %ud (0x%ux)\n", ctlr->tx[4].lastcmd, ctlr->tx[4].lastcmd); + if(ctlr->fwinfo.errptr == 0){ + print("no error pointer\n"); + return; + } + for(i=0; ifwinfo.errptr + i*4); + print( "error:\tid %ux, pc %ux,\n" + "\tbranchlink %.8ux %.8ux, interruptlink %.8ux %.8ux,\n" + "\terrordata %.8ux %.8ux, srcline %ud, tsf %ux, time %ux\n", + dump[1], dump[2], + dump[4], dump[3], dump[6], dump[5], + dump[7], dump[8], dump[9], dump[10], dump[11]); +} + +static char* +eepromlock(Ctlr *ctlr) +{ + int i, j; + + for(i=0; i<100; i++){ + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | EepromLocked); + for(j=0; j<100; j++){ + if(csr32r(ctlr, Cfg) & EepromLocked) + return 0; + delay(10); + } + } + return "eepromlock: timeout"; +} +static void +eepromunlock(Ctlr *ctlr) +{ + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) & ~EepromLocked); +} +static char* +eepromread(Ctlr *ctlr, void *data, int count, uint off) +{ + uchar *out = data; + u32int w, s; + int i; + + w = 0; + off += ctlr->eeprom.off; + for(; count > 0; count -= 2, off++){ + csr32w(ctlr, EepromIo, off << 2); + for(i=0; i<10; i++){ + w = csr32r(ctlr, EepromIo); + if(w & 1) + break; + delay(5); + } + if(i == 10) + return "eepromread: timeout"; + if(ctlr->eeprom.otp){ + s = csr32r(ctlr, OtpromGp); + if(s & EccUncorrStts) + return "eepromread: otprom ecc error"; + if(s & EccCorrStts) + csr32w(ctlr, OtpromGp, s); + } + *out++ = w >> 16; + if(count > 1) + *out++ = w >> 24; + } + return 0; +} + +static char* +handover(Ctlr *ctlr) +{ + int i; + + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | NicReady); + for(i=0; i<5; i++){ + if(csr32r(ctlr, Cfg) & NicReady) + return 0; + delay(10); + } + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | Prepare); + for(i=0; i<15000; i++){ + if((csr32r(ctlr, Cfg) & PrepareDone) == 0) + break; + delay(10); + } + if(i >= 15000) + return "handover: timeout"; + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | NicReady); + for(i=0; i<5; i++){ + if(csr32r(ctlr, Cfg) & NicReady) + return 0; + delay(10); + } + return "handover: timeout"; +} + +static char* +clockwait(Ctlr *ctlr) +{ + int i; + + /* Set "initialization complete" bit. */ + csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | InitDone); + for(i=0; i<2500; i++){ + if(csr32r(ctlr, Gpc) & MacClockReady) + return 0; + delay(10); + } + return "clockwait: timeout"; +} + +static char* +poweron(Ctlr *ctlr) +{ + int capoff; + char *err; + + /* Disable L0s exit timer (NMI bug workaround). */ + csr32w(ctlr, Giochicken, csr32r(ctlr, Giochicken) | DisL0Stimer); + + /* Don't wait for ICH L0s (ICH bug workaround). */ + csr32w(ctlr, Giochicken, csr32r(ctlr, Giochicken) | L1AnoL0Srx); + + /* Set FH wait threshold to max (HW bug under stress workaround). */ + csr32w(ctlr, Dbghpetmem, csr32r(ctlr, Dbghpetmem) | 0xffff0000); + + /* Enable HAP INTA to move adapter from L1a to L0s. */ + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | HapwakeL1A); + + capoff = pcicap(ctlr->pdev, PciCapPCIe); + if(capoff != -1){ + /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ + if(pcicfgr16(ctlr->pdev, capoff + 0x10) & 0x2) /* LCSR -> L1 Entry enabled. */ + csr32w(ctlr, Gio, csr32r(ctlr, Gio) | EnaL0S); + else + csr32w(ctlr, Gio, csr32r(ctlr, Gio) & ~EnaL0S); + } + + if(ctlr->type != Type4965 && ctlr->type <= Type1000) + csr32w(ctlr, AnaPll, csr32r(ctlr, AnaPll) | 0x00880300); + + /* Wait for clock stabilization before accessing prph. */ + if((err = clockwait(ctlr)) != nil) + return err; + + if((err = niclock(ctlr)) != nil) + return err; + + /* Enable DMA and BSM (Bootstrap State Machine). */ + if(ctlr->type == Type4965) + prphwrite(ctlr, ApmgClkEna, DmaClkRqt | BsmClkRqt); + else + prphwrite(ctlr, ApmgClkEna, DmaClkRqt); + delay(20); + + /* Disable L1-Active. */ + prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11)); + + nicunlock(ctlr); + + ctlr->power = 1; + + return 0; +} + +static void +poweroff(Ctlr *ctlr) +{ + int i, j; + + csr32w(ctlr, Reset, 1); + + /* Disable interrupts */ + ctlr->ie = 0; + csr32w(ctlr, Imr, 0); + csr32w(ctlr, Isr, ~0); + csr32w(ctlr, FhIsr, ~0); + + /* Stop scheduler */ + if(ctlr->type != Type4965) + prphwrite(ctlr, SchedTxFact5000, 0); + else + prphwrite(ctlr, SchedTxFact4965, 0); + + /* Stop TX ring */ + if(niclock(ctlr) == nil){ + for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--){ + csr32w(ctlr, FhTxConfig + i*32, 0); + for(j = 0; j < 200; j++){ + if(csr32r(ctlr, FhTxStatus) & (0x10000<power = 0; +} + +static char* +rominit(Ctlr *ctlr) +{ + uint prev, last; + uchar buf[2]; + char *err; + int i; + + ctlr->eeprom.otp = 0; + ctlr->eeprom.off = 0; + if(ctlr->type < Type1000 || (csr32r(ctlr, OtpromGp) & DevSelOtp) == 0) + return nil; + + /* Wait for clock stabilization before accessing prph. */ + if((err = clockwait(ctlr)) != nil) + return err; + + if((err = niclock(ctlr)) != nil) + return err; + prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | ResetReq); + delay(5); + prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) & ~ResetReq); + nicunlock(ctlr); + + /* Set auto clock gate disable bit for HW with OTP shadow RAM. */ + if(ctlr->type != Type1000) + csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) | (1<<31)); + + csr32w(ctlr, EepromGp, csr32r(ctlr, EepromGp) & ~0x00000180); + + /* Clear ECC status. */ + csr32w(ctlr, OtpromGp, csr32r(ctlr, OtpromGp) | (EccCorrStts | EccUncorrStts)); + + ctlr->eeprom.otp = 1; + if(ctlr->type != Type1000) + return nil; + + /* Switch to absolute addressing mode. */ + csr32w(ctlr, OtpromGp, csr32r(ctlr, OtpromGp) & ~RelativeAccess); + + /* + * Find the block before last block (contains the EEPROM image) + * for HW without OTP shadow RAM. + */ + prev = last = 0; + for(i=0; i<3; i++){ + if((err = eepromread(ctlr, buf, 2, last)) != nil) + return err; + if(get16(buf) == 0) + break; + prev = last; + last = get16(buf); + } + if(i == 0 || i >= 3) + return "rominit: missing eeprom image"; + + ctlr->eeprom.off = prev+1; + return nil; +} + +static int +iwlinit(Ether *edev) +{ + Ctlr *ctlr; + char *err; + uchar b[4]; + uint u, caloff, regoff; + + ctlr = edev->ctlr; + if((err = handover(ctlr)) != nil) + goto Err; + if((err = poweron(ctlr)) != nil) + goto Err; + if((csr32r(ctlr, EepromGp) & 0x7) == 0){ + err = "bad rom signature"; + goto Err; + } + if((err = eepromlock(ctlr)) != nil) + goto Err; + if((err = rominit(ctlr)) != nil) + goto Err2; + if((err = eepromread(ctlr, edev->ea, sizeof(edev->ea), 0x15)) != nil){ + eepromunlock(ctlr); + goto Err; + } + if((err = eepromread(ctlr, b, 2, 0x048)) != nil){ + Err2: + eepromunlock(ctlr); + goto Err; + } + u = get16(b); + ctlr->rfcfg.type = u & 3; u >>= 2; + ctlr->rfcfg.step = u & 3; u >>= 2; + ctlr->rfcfg.dash = u & 3; u >>= 4; + ctlr->rfcfg.txantmask = u & 15; u >>= 4; + ctlr->rfcfg.rxantmask = u & 15; + if((err = eepromread(ctlr, b, 2, 0x66)) != nil) + goto Err2; + regoff = get16(b); + if((err = eepromread(ctlr, b, 4, regoff+1)) != nil) + goto Err2; + strncpy(ctlr->eeprom.regdom, (char*)b, 4); + ctlr->eeprom.regdom[4] = 0; + if((err = eepromread(ctlr, b, 2, 0x67)) != nil) + goto Err2; + caloff = get16(b); + if((err = eepromread(ctlr, b, 4, caloff)) != nil) + goto Err2; + ctlr->eeprom.version = b[0]; + ctlr->eeprom.type = b[1]; + ctlr->eeprom.volt = get16(b+2); + if(ctlr->type != Type4965 && ctlr->type != Type5150){ + if((err = eepromread(ctlr, b, 4, caloff + 0x128)) != nil) + goto Err2; + ctlr->eeprom.crystal = get32(b); + } + eepromunlock(ctlr); + + switch(ctlr->type){ + case Type4965: + ctlr->rfcfg.txantmask = 3; + ctlr->rfcfg.rxantmask = 7; + break; + case Type5100: + ctlr->rfcfg.txantmask = 2; + ctlr->rfcfg.rxantmask = 3; + break; + case Type6000: + if(ctlr->pdev->did == 0x422c || ctlr->pdev->did == 0x4230){ + ctlr->rfcfg.txantmask = 6; + ctlr->rfcfg.rxantmask = 6; + } + break; + } + poweroff(ctlr); + return 0; +Err: + print("iwlinit: %s\n", err); + poweroff(ctlr); + return -1; +} + +static char* +crackfw(FWImage *i, uchar *data, uint size, int alt) +{ + uchar *p, *e; + FWSect *s; + + memset(i, 0, sizeof(*i)); + if(size < 4){ +Tooshort: + return "firmware image too short"; + } + p = data; + e = p + size; + i->rev = get32(p); p += 4; + if(i->rev == 0){ + uvlong altmask; + + if(size < (4+64+4+4+8)) + goto Tooshort; + if(memcmp(p, "IWL\n", 4) != 0) + return "bad firmware signature"; + p += 4; + strncpy(i->descr, (char*)p, 64); + i->descr[64] = 0; + p += 64; + i->rev = get32(p); p += 4; + i->build = get32(p); p += 4; + altmask = get32(p); p += 4; + altmask |= (uvlong)get32(p) << 32; p += 4; + while(alt > 0 && (altmask & (1ULL< e) + goto Tooshort; + switch(get16(p)){ + case 1: s = &i->main.text; break; + case 2: s = &i->main.data; break; + case 3: s = &i->init.text; break; + case 4: s = &i->init.data; break; + case 5: s = &i->boot.text; break; + default:s = &dummy; + } + p += 2; + if(get16(p) != 0 && get16(p) != alt) + s = &dummy; + p += 2; + s->size = get32(p); p += 4; + s->data = p; + if((p + s->size) > e) + goto Tooshort; + p += (s->size + 3) & ~3; + } + } else { + if(((i->rev>>8) & 0xFF) < 2) + return "need firmware api >= 2"; + if(((i->rev>>8) & 0xFF) >= 3){ + i->build = get32(p); p += 4; + } + if((p + 5*4) > e) + goto Tooshort; + i->main.text.size = get32(p); p += 4; + i->main.data.size = get32(p); p += 4; + i->init.text.size = get32(p); p += 4; + i->init.data.size = get32(p); p += 4; + i->boot.text.size = get32(p); p += 4; + i->main.text.data = p; p += i->main.text.size; + i->main.data.data = p; p += i->main.data.size; + i->init.text.data = p; p += i->init.text.size; + i->init.data.data = p; p += i->init.data.size; + i->boot.text.data = p; p += i->boot.text.size; + if(p > e) + goto Tooshort; + } + return 0; +} + +static FWImage* +readfirmware(char *name) +{ + uchar dirbuf[sizeof(Dir)+100], *data; + char buf[128], *err; + FWImage *fw; + int n, r; + Chan *c; + Dir d; + + if(!iseve()) + error(Eperm); + if(!waserror()){ + snprint(buf, sizeof buf, "/boot/%s", name); + c = namec(buf, Aopen, OREAD, 0); + poperror(); + } else { + snprint(buf, sizeof buf, "/lib/firmware/%s", name); + c = namec(buf, Aopen, OREAD, 0); + } + if(waserror()){ + cclose(c); + nexterror(); + } + n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf); + if(n <= 0) + error("can't stat firmware"); + convM2D(dirbuf, n, &d, nil); + fw = smalloc(sizeof(*fw) + 16 + d.length); + data = (uchar*)(fw+1); + if(waserror()){ + free(fw); + nexterror(); + } + r = 0; + while(r < d.length){ + n = devtab[c->type]->read(c, data+r, d.length-r, (vlong)r); + if(n <= 0) + break; + r += n; + } + if((err = crackfw(fw, data, r, 1)) != nil) + error(err); + poperror(); + poperror(); + cclose(c); + return fw; +} + + +static int +gotirq(void *arg) +{ + Ctlr *ctlr = arg; + return (ctlr->wait.m & ctlr->wait.w) != 0; +} + +static u32int +irqwait(Ctlr *ctlr, u32int mask, int timeout) +{ + u32int r; + + ilock(ctlr); + r = ctlr->wait.m & mask; + if(r == 0){ + ctlr->wait.w = mask; + iunlock(ctlr); + if(!waserror()){ + tsleep(&ctlr->wait, gotirq, ctlr, timeout); + poperror(); + } + ilock(ctlr); + ctlr->wait.w = 0; + r = ctlr->wait.m & mask; + } + ctlr->wait.m &= ~r; + iunlock(ctlr); + return r; +} + +static int +rbplant(Ctlr *ctlr, int i) +{ + Block *b; + + b = iallocb(Rbufsize + 256); + if(b == nil) + return -1; + b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256); + memset(b->rp, 0, Rdscsize); + ctlr->rx.b[i] = b; + ctlr->rx.p[i] = PCIWADDR(b->rp) >> 8; + return 0; +} + +static char* +initring(Ctlr *ctlr) +{ + RXQ *rx; + TXQ *tx; + int i, q; + + rx = &ctlr->rx; + if(rx->b == nil) + rx->b = malloc(sizeof(Block*) * Nrx); + if(rx->p == nil) + rx->p = mallocalign(sizeof(u32int) * Nrx, 256, 0, 0); + if(rx->s == nil) + rx->s = mallocalign(Rstatsize, 16, 0, 0); + if(rx->b == nil || rx->p == nil || rx->s == nil) + return "no memory for rx ring"; + memset(ctlr->rx.s, 0, Rstatsize); + for(i=0; ip[i] = 0; + if(rx->b[i] != nil){ + freeb(rx->b[i]); + rx->b[i] = nil; + } + if(rbplant(ctlr, i) < 0) + return "no memory for rx descriptors"; + } + rx->i = 0; + + if(ctlr->sched.s == nil) + ctlr->sched.s = mallocalign(512 * nelem(ctlr->tx) * 2, 1024, 0, 0); + if(ctlr->sched.s == nil) + return "no memory for sched buffer"; + memset(ctlr->sched.s, 0, 512 * nelem(ctlr->tx)); + + for(q=0; qtx); q++){ + tx = &ctlr->tx[q]; + if(tx->b == nil) + tx->b = malloc(sizeof(Block*) * Ntx); + if(tx->d == nil) + tx->d = mallocalign(Tdscsize * Ntx, 256, 0, 0); + if(tx->c == nil) + tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0); + if(tx->b == nil || tx->d == nil || tx->c == nil) + return "no memory for tx ring"; + memset(tx->d, 0, Tdscsize * Ntx); + memset(tx->c, 0, Tcmdsize * Ntx); + for(i=0; ib[i] != nil){ + freeb(tx->b[i]); + tx->b[i] = nil; + } + } + tx->i = 0; + tx->n = 0; + tx->lastcmd = 0; + } + + if(ctlr->kwpage == nil) + ctlr->kwpage = mallocalign(4096, 4096, 0, 0); + if(ctlr->kwpage == nil) + return "no memory for kwpage"; + memset(ctlr->kwpage, 0, 4096); + + return nil; +} + +static char* +reset(Ctlr *ctlr) +{ + char *err; + int i, q; + + if(ctlr->power) + poweroff(ctlr); + if((err = initring(ctlr)) != nil) + return err; + if((err = poweron(ctlr)) != nil) + return err; + + if((err = niclock(ctlr)) != nil) + return err; + prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain); + nicunlock(ctlr); + + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi); + + if((err = niclock(ctlr)) != nil) + return err; + if(ctlr->type != Type4965) + prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | EarlyPwroffDis); + if(ctlr->type == Type1000){ + /* + * Select first Switching Voltage Regulator (1.32V) to + * solve a stability issue related to noisy DC2DC line + * in the silicon of 1000 Series. + */ + prphwrite(ctlr, ApmgDigitalSvr, + (prphread(ctlr, ApmgDigitalSvr) & ~(0xf<<5)) | (3<<5)); + } + nicunlock(ctlr); + + if((err = niclock(ctlr)) != nil) + return err; + if((ctlr->type == Type6005 || ctlr->type == Type6050) && ctlr->eeprom.version == 6) + csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrvCalV6); + if(ctlr->type == Type6005) + csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrv1X2); + nicunlock(ctlr); + + if((err = niclock(ctlr)) != nil) + return err; + csr32w(ctlr, FhRxConfig, 0); + csr32w(ctlr, FhRxWptr, 0); + csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p) >> 8); + csr32w(ctlr, FhStatusWptr, PCIWADDR(ctlr->rx.s) >> 4); + csr32w(ctlr, FhRxConfig, + FhRxConfigEna | + FhRxConfigIgnRxfEmpty | + FhRxConfigIrqDstHost | + FhRxConfigSingleFrame | + (Nrxlog << FhRxConfigNrbdShift)); + csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7); + nicunlock(ctlr); + + if((err = niclock(ctlr)) != nil) + return err; + if(ctlr->type != Type4965) + prphwrite(ctlr, SchedTxFact5000, 0); + else + prphwrite(ctlr, SchedTxFact4965, 0); + csr32w(ctlr, FhKwAddr, PCIWADDR(ctlr->kwpage) >> 4); + for(q = (ctlr->type != Type4965) ? 19 : 15; q >= 0; q--) + csr32w(ctlr, FhCbbcQueue + q*4, PCIWADDR(ctlr->tx[q].d) >> 8); + nicunlock(ctlr); + + for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--) + csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna); + + csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill); + csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked); + + ctlr->broken = 0; + ctlr->wait.m = 0; + ctlr->wait.w = 0; + + ctlr->ie = Idefmask; + csr32w(ctlr, Imr, ctlr->ie); + csr32w(ctlr, Isr, ~0); + + if(ctlr->type >= Type6000) + csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff); + + return nil; +} + +static char* +postboot(Ctlr *ctlr) +{ + uint ctxoff, ctxlen, dramaddr; + char *err; + int i, q; + + if((err = niclock(ctlr)) != nil) + return err; + + if(ctlr->type != Type4965){ + dramaddr = SchedDramAddr5000; + ctxoff = SchedCtxOff5000; + ctxlen = SchedCtxLen5000; + } else { + dramaddr = SchedDramAddr4965; + ctxoff = SchedCtxOff4965; + ctxlen = SchedCtxLen4965; + } + + ctlr->sched.base = prphread(ctlr, SchedSramAddr); + for(i=0; i < ctxlen; i += 4) + memwrite(ctlr, ctlr->sched.base + ctxoff + i, 0); + + prphwrite(ctlr, dramaddr, PCIWADDR(ctlr->sched.s)>>10); + + csr32w(ctlr, FhTxChicken, csr32r(ctlr, FhTxChicken) | 2); + + if(ctlr->type != Type4965){ + /* Enable chain mode for all queues, except command queue 4. */ + prphwrite(ctlr, SchedQChainSel5000, 0xfffef); + prphwrite(ctlr, SchedAggrSel5000, 0); + + for(q=0; q<20; q++){ + prphwrite(ctlr, SchedQueueRdptr5000 + q*4, 0); + csr32w(ctlr, HbusTargWptr, q << 8); + + memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 0); + /* Set scheduler window size and frame limit. */ + memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16 | 64); + } + /* Enable interrupts for all our 20 queues. */ + prphwrite(ctlr, SchedIntrMask5000, 0xfffff); + + /* Identify TX FIFO rings (0-7). */ + prphwrite(ctlr, SchedTxFact5000, 0xff); + } else { + /* Disable chain mode for all our 16 queues. */ + prphwrite(ctlr, SchedQChainSel4965, 0); + + for(q=0; q<16; q++) { + prphwrite(ctlr, SchedQueueRdptr4965 + q*4, 0); + csr32w(ctlr, HbusTargWptr, q << 8); + + /* Set scheduler window size. */ + memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 64); + /* Set scheduler window size and frame limit. */ + memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16); + } + /* Enable interrupts for all our 16 queues. */ + prphwrite(ctlr, SchedIntrMask4965, 0xffff); + + /* Identify TX FIFO rings (0-7). */ + prphwrite(ctlr, SchedTxFact4965, 0xff); + } + + /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ + for(q=0; q<7; q++){ + if(ctlr->type != Type4965){ + static uchar qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 }; + prphwrite(ctlr, SchedQueueStatus5000 + q*4, 0x00ff0018 | qid2fifo[q]); + } else { + static uchar qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 }; + prphwrite(ctlr, SchedQueueStatus4965 + q*4, 0x0007fc01 | qid2fifo[q]<<1); + } + } + nicunlock(ctlr); + + if(ctlr->type != Type4965){ + uchar c[Tcmdsize]; + + /* disable wimax coexistance */ + memset(c, 0, sizeof(c)); + if((err = cmd(ctlr, 90, c, 4+4*16)) != nil) + return err; + + if(ctlr->type != Type5150){ + /* calibrate crystal */ + memset(c, 0, sizeof(c)); + c[0] = 15; /* code */ + c[1] = 0; /* group */ + c[2] = 1; /* ngroup */ + c[3] = 1; /* isvalid */ + c[4] = ctlr->eeprom.crystal; + c[5] = ctlr->eeprom.crystal>>16; + if((err = cmd(ctlr, 176, c, 8)) != nil) + return err; + } + + if(ctlr->calib.done == 0){ + /* query calibration (init firmware) */ + memset(c, 0, sizeof(c)); + put32(c + 0*(5*4) + 0, 0xffffffff); + put32(c + 0*(5*4) + 4, 0xffffffff); + put32(c + 0*(5*4) + 8, 0xffffffff); + put32(c + 2*(5*4) + 0, 0xffffffff); + if((err = cmd(ctlr, 101, c, (((2*(5*4))+4)*2)+4)) != nil) + return err; + + /* wait to collect calibration records */ + if(irqwait(ctlr, Ierr, 2000)) + return "calibration failed"; + + if(ctlr->calib.done == 0){ + print("iwl: no calibration results\n"); + ctlr->calib.done = 1; + } + } else { + static uchar cmds[] = {8, 9, 11, 17, 16}; + + /* send calibration records (runtime firmware) */ + for(q=0; qtype != Type5150) + continue; + if(i == 17 && (ctlr->type >= Type6000 || ctlr->type == Type5150)) + continue; + if((b = ctlr->calib.cmd[i]) == nil) + continue; + b->ref++; /* dont free on command completion */ + if((err = qcmd(ctlr, 4, 176, nil, 0, b)) != nil){ + freeb(b); + return err; + } + if((err = flushq(ctlr, 4)) != nil) + return err; + } + + if(ctlr->type == Type6005){ + /* temperature sensor offset */ + memset(c, 0, sizeof(c)); + c[0] = 18; + c[1] = 0; + c[2] = 1; + c[3] = 1; + put16(c + 4, 2700); + if((err = cmd(ctlr, 176, c, 4+2+2)) != nil) + return err; + } + + if(ctlr->type == Type6005 || ctlr->type == Type6050){ + /* runtime DC calibration */ + memset(c, 0, sizeof(c)); + put32(c + 0*(5*4) + 0, 0xffffffff); + put32(c + 0*(5*4) + 4, 1<<1); + if((err = cmd(ctlr, 101, c, (((2*(5*4))+4)*2)+4)) != nil) + return err; + } + + /* set tx antenna config */ + put32(c, ctlr->rfcfg.txantmask & 7); + if((err = cmd(ctlr, 152, c, 4)) != nil) + return err; + } + } + + return nil; +} + +static char* +loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size) +{ + uchar *dma; + char *err; + + dma = mallocalign(size, 16, 0, 0); + if(dma == nil) + return "no memory for dma"; + memmove(dma, data, size); + coherence(); + if((err = niclock(ctlr)) != 0){ + free(dma); + return err; + } + csr32w(ctlr, FhTxConfig + 9*32, 0); + csr32w(ctlr, FhSramAddr + 9*4, dst); + csr32w(ctlr, FhTfbdCtrl0 + 9*8, PCIWADDR(dma)); + csr32w(ctlr, FhTfbdCtrl1 + 9*8, size); + csr32w(ctlr, FhTxBufStatus + 9*32, + (1<fw; + + if(fw->boot.text.size == 0){ + if(ctlr->calib.done == 0){ + if((err = loadfirmware1(ctlr, 0x00000000, fw->init.text.data, fw->init.text.size)) != nil) + return err; + if((err = loadfirmware1(ctlr, 0x00800000, fw->init.data.data, fw->init.data.size)) != nil) + return err; + csr32w(ctlr, Reset, 0); + if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive) + return "init firmware boot failed"; + if((err = postboot(ctlr)) != nil) + return err; + if((err = reset(ctlr)) != nil) + return err; + } + if((err = loadfirmware1(ctlr, 0x00000000, fw->main.text.data, fw->main.text.size)) != nil) + return err; + if((err = loadfirmware1(ctlr, 0x00800000, fw->main.data.data, fw->main.data.size)) != nil) + return err; + csr32w(ctlr, Reset, 0); + if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive) + return "main firmware boot failed"; + return postboot(ctlr); + } + + size = ROUND(fw->init.data.size, 16) + ROUND(fw->init.text.size, 16); + dma = mallocalign(size, 16, 0, 0); + if(dma == nil) + return "no memory for dma"; + + if((err = niclock(ctlr)) != nil){ + free(dma); + return err; + } + + p = dma; + memmove(p, fw->init.data.data, fw->init.data.size); + coherence(); + prphwrite(ctlr, BsmDramDataAddr, PCIWADDR(p) >> 4); + prphwrite(ctlr, BsmDramDataSize, fw->init.data.size); + p += ROUND(fw->init.data.size, 16); + memmove(p, fw->init.text.data, fw->init.text.size); + coherence(); + prphwrite(ctlr, BsmDramTextAddr, PCIWADDR(p) >> 4); + prphwrite(ctlr, BsmDramTextSize, fw->init.text.size); + + nicunlock(ctlr); + if((err = niclock(ctlr)) != nil){ + free(dma); + return err; + } + + p = fw->boot.text.data; + n = fw->boot.text.size/4; + for(i=0; imain.data.size, 16) + ROUND(fw->main.text.size, 16); + dma = mallocalign(size, 16, 0, 0); + if(dma == nil) + return "no memory for dma"; + if((err = niclock(ctlr)) != nil){ + free(dma); + return err; + } + p = dma; + memmove(p, fw->main.data.data, fw->main.data.size); + coherence(); + prphwrite(ctlr, BsmDramDataAddr, PCIWADDR(p) >> 4); + prphwrite(ctlr, BsmDramDataSize, fw->main.data.size); + p += ROUND(fw->main.data.size, 16); + memmove(p, fw->main.text.data, fw->main.text.size); + coherence(); + prphwrite(ctlr, BsmDramTextAddr, PCIWADDR(p) >> 4); + prphwrite(ctlr, BsmDramTextSize, fw->main.text.size | (1<<31)); + nicunlock(ctlr); + + if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){ + free(dma); + return "main firmware boot failed"; + } + free(dma); + return postboot(ctlr); +} + +static int +txqready(void *arg) +{ + TXQ *q = arg; + return q->n < Ntx; +} + +static char* +qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block) +{ + uchar *d, *c; + TXQ *q; + + assert(qid < nelem(ctlr->tx)); + assert(size <= Tcmdsize-4); + + ilock(ctlr); + q = &ctlr->tx[qid]; + while(q->n >= Ntx && !ctlr->broken){ + iunlock(ctlr); + qlock(q); + if(!waserror()){ + tsleep(q, txqready, q, 10); + poperror(); + } + qunlock(q); + ilock(ctlr); + } + if(ctlr->broken){ + iunlock(ctlr); + return "qcmd: broken"; + } + q->n++; + + q->lastcmd = code; + q->b[q->i] = block; + c = q->c + q->i * Tcmdsize; + d = q->d + q->i * Tdscsize; + + /* build command */ + c[0] = code; + c[1] = 0; /* flags */ + c[2] = q->i; + c[3] = qid; + + if(size > 0) + memmove(c+4, data, size); + + size += 4; + + /* build descriptor */ + *d++ = 0; + *d++ = 0; + *d++ = 0; + *d++ = 1 + (block != nil); /* nsegs */ + put32(d, PCIWADDR(c)); d += 4; + put16(d, size << 4); d += 2; + if(block != nil){ + size = BLEN(block); + put32(d, PCIWADDR(block->rp)); d += 4; + put16(d, size << 4); + } + + coherence(); + + q->i = (q->i+1) % Ntx; + csr32w(ctlr, HbusTargWptr, (qid<<8) | q->i); + + iunlock(ctlr); + + return nil; +} + +static int +txqempty(void *arg) +{ + TXQ *q = arg; + return q->n == 0; +} + +static char* +flushq(Ctlr *ctlr, uint qid) +{ + TXQ *q; + int i; + + q = &ctlr->tx[qid]; + qlock(q); + for(i = 0; i < 200 && !ctlr->broken; i++){ + if(txqempty(q)){ + qunlock(q); + return nil; + } + if(!waserror()){ + tsleep(q, txqempty, q, 10); + poperror(); + } + } + qunlock(q); + if(ctlr->broken) + return "flushq: broken"; + return "flushq: timeout"; +} + +static char* +cmd(Ctlr *ctlr, uint code, uchar *data, int size) +{ + char *err; + + if(0) print("cmd %ud\n", code); + if((err = qcmd(ctlr, 4, code, data, size, nil)) != nil) + return err; + return flushq(ctlr, 4); +} + +static void +setled(Ctlr *ctlr, int which, int on, int off) +{ + uchar c[8]; + + csr32w(ctlr, Led, csr32r(ctlr, Led) & ~LedBsmCtrl); + + memset(c, 0, sizeof(c)); + put32(c, 10000); + c[4] = which; + c[5] = on; + c[6] = off; + cmd(ctlr, 72, c, sizeof(c)); +} + +static void +addnode(Ctlr *ctlr, uchar id, uchar *addr) +{ + uchar c[Tcmdsize], *p; + + memset(p = c, 0, sizeof(c)); + *p++ = 0; /* control (1 = update) */ + p += 3; /* reserved */ + memmove(p, addr, 6); + p += 6; + p += 2; /* reserved */ + *p++ = id; /* node id */ + p++; /* flags */ + p += 2; /* reserved */ + p += 2; /* kflags */ + p++; /* tcs2 */ + p++; /* reserved */ + p += 5*2; /* ttak */ + p++; /* kid */ + p++; /* reserved */ + p += 16; /* key */ + if(ctlr->type != Type4965){ + p += 8; /* tcs */ + p += 8; /* rxmic */ + p += 8; /* txmic */ + } + p += 4; /* htflags */ + p += 4; /* mask */ + p += 2; /* disable tid */ + p += 2; /* reserved */ + p++; /* add ba tid */ + p++; /* del ba tid */ + p += 2; /* add ba ssn */ + p += 4; /* reserved */ + cmd(ctlr, 24, c, p - c); +} + +void +rxon(Ether *edev, Wnode *bss) +{ + uchar c[Tcmdsize], *p; + int filter, flags; + Ctlr *ctlr; + char *err; + + ctlr = edev->ctlr; + filter = FilterNoDecrypt | FilterMulticast | FilterBeacon; + if(ctlr->prom){ + filter |= FilterPromisc; + if(bss != nil) + ctlr->channel = bss->channel; + bss = nil; + } + if(bss != nil){ + ctlr->channel = bss->channel; + memmove(ctlr->bssid, bss->bssid, Eaddrlen); + ctlr->aid = bss->aid; + if(ctlr->aid != 0){ + filter |= FilterBSS; + filter &= ~FilterBeacon; + ctlr->bssnodeid = -1; + } else + ctlr->bcastnodeid = -1; + } else { + memmove(ctlr->bssid, edev->bcast, Eaddrlen); + ctlr->aid = 0; + ctlr->bcastnodeid = -1; + ctlr->bssnodeid = -1; + } + flags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto; + + if(ctlr->aid != 0) + setled(ctlr, 2, 0, 1); /* on when associated */ + else if(memcmp(ctlr->bssid, edev->bcast, Eaddrlen) != 0) + setled(ctlr, 2, 10, 10); /* slow blink when connecting */ + else + setled(ctlr, 2, 5, 5); /* fast blink when scanning */ + + if(ctlr->wifi->debug) + print("#l%d: rxon: bssid %E, aid %x, channel %d, filter %x, flags %x\n", + edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, filter, flags); + + memset(p = c, 0, sizeof(c)); + memmove(p, edev->ea, 6); p += 8; /* myaddr */ + memmove(p, ctlr->bssid, 6); p += 8; /* bssid */ + memmove(p, edev->ea, 6); p += 8; /* wlap */ + *p++ = 3; /* mode (STA) */ + *p++ = 0; /* air (?) */ + /* rxchain */ + put16(p, ((ctlr->rfcfg.rxantmask & 7)<<1) | (2<<10) | (2<<12)); + p += 2; + *p++ = 0xff; /* ofdm mask (not yet negotiated) */ + *p++ = 0x0f; /* cck mask (not yet negotiated) */ + put16(p, ctlr->aid & 0x3fff); + p += 2; /* aid */ + put32(p, flags); + p += 4; + put32(p, filter); + p += 4; + *p++ = ctlr->channel; + p++; /* reserved */ + *p++ = 0xff; /* ht single mask */ + *p++ = 0xff; /* ht dual mask */ + if(ctlr->type != Type4965){ + *p++ = 0xff; /* ht triple mask */ + p++; /* reserved */ + put16(p, 0); p += 2; /* acquisition */ + p += 2; /* reserved */ + } + if((err = cmd(ctlr, 16, c, p - c)) != nil){ + print("rxon: %s\n", err); + return; + } + + 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 { + uchar rate; + uchar plcp; + uchar flags; +} ratetab[] = { + { 2, 10, RFlagCCK }, + { 4, 20, RFlagCCK }, + { 11, 55, RFlagCCK }, + { 22, 110, RFlagCCK }, + { 12, 0xd, 0 }, + { 18, 0xf, 0 }, + { 24, 0x5, 0 }, + { 36, 0x7, 0 }, + { 48, 0x9, 0 }, + { 72, 0xb, 0 }, + { 96, 0x1, 0 }, + { 108, 0x3, 0 }, + { 120, 0x3, 0 } +}; + +static uchar iwlrates[] = { + 0x80 | 2, + 0x80 | 4, + 0x80 | 11, + 0x80 | 22, + 0x80 | 12, + 0x80 | 18, + 0x80 | 24, + 0x80 | 36, + 0x80 | 48, + 0x80 | 72, + 0x80 | 96, + 0x80 | 108, + 0x80 | 120, + + 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 *wn, Block *b) +{ + int flags, nodeid, rate, ant; + uchar c[Tcmdsize], *p; + Ether *edev; + Ctlr *ctlr; + Wifipkt *w; + char *err; + + edev = wifi->ether; + ctlr = edev->ctlr; + + qlock(ctlr); + if(ctlr->attached == 0 || ctlr->broken){ + qunlock(ctlr); + freeb(b); + return; + } + + if((wn->channel != ctlr->channel) + || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0))) + rxon(edev, wn); + + if(b == nil){ + /* association note has no data to transmit */ + qunlock(ctlr); + return; + } + + flags = 0; + nodeid = ctlr->bcastnodeid; + p = wn->minrate; + w = (Wifipkt*)b->rp; + 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; + p = wn->maxrate; + } + + if(flags & (TFlagNeedRTS|TFlagNeedCTS)){ + if(ctlr->type != Type4965){ + flags &= ~(TFlagNeedRTS|TFlagNeedCTS); + flags |= TFlagNeedProtection; + } else + flags |= TFlagFullTxOp; + } + } + qunlock(ctlr); + + rate = 0; + if(p >= iwlrates && p < &iwlrates[nelem(ratetab)]) + rate = p - iwlrates; + + /* select first available antenna */ + ant = ctlr->rfcfg.txantmask & 7; + ant |= (ant == 0); + ant = ((ant - 1) & ant) ^ ant; + + memset(p = c, 0, sizeof(c)); + put16(p, BLEN(b)); + p += 2; + p += 2; /* lnext */ + put32(p, flags); + p += 4; + put32(p, 0); + p += 4; /* scratch */ + + *p++ = ratetab[rate].plcp; + *p++ = ratetab[rate].flags | (ant<<6); + + p += 2; /* xflags */ + *p++ = nodeid; + *p++ = 0; /* security */ + *p++ = 0; /* linkq */ + p++; /* reserved */ + p += 16; /* key */ + p += 2; /* fnext */ + p += 2; /* reserved */ + put32(p, ~0); /* lifetime */ + p += 4; + + /* BUG: scratch ptr? not clear what this is for */ + put32(p, PCIWADDR(ctlr->kwpage)); + p += 5; + + *p++ = 60; /* rts ntries */ + *p++ = 15; /* data ntries */ + *p++ = 0; /* tid */ + put16(p, 0); /* timeout */ + p += 2; + p += 2; /* txop */ + if((err = qcmd(ctlr, 0, 28, c, p - c, b)) != nil){ + print("transmit: %s\n", err); + freeb(b); + } +} + +static long +iwlctl(Ether *edev, void *buf, long n) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->wifi) + return wifictl(ctlr->wifi, buf, n); + return 0; +} + +static long +iwlifstat(Ether *edev, void *buf, long n, ulong off) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->wifi) + return wifistat(ctlr->wifi, buf, n, off); + return 0; +} + +static void +setoptions(Ether *edev) +{ + char buf[64], *p; + Ctlr *ctlr; + int i; + + ctlr = edev->ctlr; + for(i = 0; i < edev->nopt; i++){ + snprint(buf, sizeof(buf), "%s", edev->opt[i]); + p = strchr(buf, '='); + if(p != nil) + *p = 0; + if(strcmp(buf, "debug") == 0 + || strcmp(buf, "essid") == 0 + || strcmp(buf, "bssid") == 0){ + if(p != nil) + *p = ' '; + if(!waserror()){ + wifictl(ctlr->wifi, buf, strlen(buf)); + poperror(); + } + } + } +} + +static void +iwlpromiscuous(void *arg, int on) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + ctlr = edev->ctlr; + qlock(ctlr); + ctlr->prom = on; + rxon(edev, ctlr->wifi->bss); + qunlock(ctlr); +} + +static void +iwlrecover(void *arg) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + ctlr = edev->ctlr; + while(waserror()) + ; + for(;;){ + tsleep(&up->sleep, return0, 0, 4000); + + qlock(ctlr); + for(;;){ + if(ctlr->broken == 0) + break; + + if(ctlr->power) + poweroff(ctlr); + + if((csr32r(ctlr, Gpc) & RfKill) == 0) + break; + + if(reset(ctlr) != nil) + break; + if(boot(ctlr) != nil) + break; + + ctlr->bcastnodeid = -1; + ctlr->bssnodeid = -1; + ctlr->aid = 0; + rxon(edev, ctlr->wifi->bss); + break; + } + qunlock(ctlr); + } +} + +static void +iwlattach(Ether *edev) +{ + FWImage *fw; + Ctlr *ctlr; + char *err; + + ctlr = edev->ctlr; + qlock(ctlr); + if(waserror()){ + print("#l%d: %s\n", edev->ctlrno, up->errstr); + if(ctlr->power) + poweroff(ctlr); + qunlock(ctlr); + nexterror(); + } + if(ctlr->attached == 0){ + if((csr32r(ctlr, Gpc) & RfKill) == 0) + error("wifi disabled by switch"); + + if(ctlr->wifi == nil){ + ctlr->wifi = wifiattach(edev, transmit); + ctlr->wifi->rates = iwlrates; + } + + if(ctlr->fw == nil){ + fw = readfirmware(fwname[ctlr->type]); + print("#l%d: firmware: %s, rev %ux, build %ud, size %ux+%ux+%ux+%ux+%ux\n", + edev->ctlrno, + fwname[ctlr->type], + fw->rev, fw->build, + fw->main.text.size, fw->main.data.size, + fw->init.text.size, fw->init.data.size, + fw->boot.text.size); + ctlr->fw = fw; + } + + if((err = reset(ctlr)) != nil) + error(err); + if((err = boot(ctlr)) != nil) + error(err); + + ctlr->bcastnodeid = -1; + ctlr->bssnodeid = -1; + ctlr->channel = 1; + ctlr->aid = 0; + + setoptions(edev); + + ctlr->attached = 1; + + kproc("iwlrecover", iwlrecover, edev); + } + qunlock(ctlr); + poperror(); +} + +static void +receive(Ctlr *ctlr) +{ + Block *b, *bb; + uchar *d, *dd, *cc; + RXQ *rx; + TXQ *tx; + uint hw; + + rx = &ctlr->rx; + if(ctlr->broken || rx->s == nil || rx->b == nil) + return; + for(hw = get16(rx->s) % Nrx; rx->i != hw; rx->i = (rx->i + 1) % Nrx){ + uchar type, flags, idx, qid; + u32int len; + + b = rx->b[rx->i]; + if(b == nil) + continue; + + d = b->rp; + len = get32(d); d += 4; + type = *d++; + flags = *d++; + USED(flags); + idx = *d++; + qid = *d++; + + if((qid & 0x80) == 0 && qid < nelem(ctlr->tx)){ + tx = &ctlr->tx[qid]; + if(tx->n > 0){ + bb = tx->b[idx]; + if(bb != nil){ + tx->b[idx] = nil; + freeb(bb); + } + /* paranoia: clear tx descriptors */ + dd = tx->d + idx*Tdscsize; + cc = tx->c + idx*Tcmdsize; + memset(dd, 0, Tdscsize); + memset(cc, 0, Tcmdsize); + tx->n--; + + wakeup(tx); + } + } + + len &= 0x3fff; + if(len < 4 || type == 0) + continue; + + len -= 4; + switch(type){ + case 1: /* microcontroller ready */ + setfwinfo(ctlr, d, len); + break; + case 24: /* add node done */ + break; + case 28: /* tx done */ + break; + case 102: /* calibration result (Type5000 only) */ + if(len < 4) + break; + idx = d[0]; + if(idx >= nelem(ctlr->calib.cmd)) + break; + if(rbplant(ctlr, rx->i) < 0) + break; + if(ctlr->calib.cmd[idx] != nil) + freeb(ctlr->calib.cmd[idx]); + b->rp = d; + b->wp = d + len; + ctlr->calib.cmd[idx] = b; + continue; + case 103: /* calibration done (Type5000 only) */ + ctlr->calib.done = 1; + break; + case 130: /* start scan */ + break; + case 132: /* stop scan */ + break; + case 156: /* rx statistics */ + break; + case 157: /* beacon statistics */ + break; + case 161: /* state changed */ + break; + case 162: /* beacon missed */ + break; + case 192: /* rx phy */ + break; + case 195: /* rx done */ + if(d + 2 > b->lim) + break; + d += d[1]; + d += 56; + case 193: /* mpdu rx done */ + if(d + 4 > b->lim) + break; + len = get16(d); d += 4; + if(d + len + 4 > b->lim) + break; + if((get32(d + len) & 3) != 3) + break; + if(ctlr->wifi == nil) + break; + if(rbplant(ctlr, rx->i) < 0) + break; + b->rp = d; + b->wp = d + len; + wifiiq(ctlr->wifi, b); + continue; + case 197: /* rx compressed ba */ + break; + } + /* paranoia: clear the descriptor */ + memset(b->rp, 0, Rdscsize); + } + csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7); +} + +static void +iwlinterrupt(Ureg*, void *arg) +{ + u32int isr, fhisr; + Ether *edev; + Ctlr *ctlr; + + edev = arg; + ctlr = edev->ctlr; + ilock(ctlr); + csr32w(ctlr, Imr, 0); + isr = csr32r(ctlr, Isr); + fhisr = csr32r(ctlr, FhIsr); + if(isr == 0xffffffff || (isr & 0xfffffff0) == 0xa5a5a5a0){ + iunlock(ctlr); + return; + } + if(isr == 0 && fhisr == 0) + goto done; + csr32w(ctlr, Isr, isr); + csr32w(ctlr, FhIsr, fhisr); + if((isr & (Iswrx | Ifhrx | Irxperiodic)) || (fhisr & Ifhrx)) + receive(ctlr); + if(isr & Ierr){ + ctlr->broken = 1; + print("#l%d: fatal firmware error\n", edev->ctlrno); + dumpctlr(ctlr); + } + ctlr->wait.m |= isr; + if(ctlr->wait.m & ctlr->wait.w) + wakeup(&ctlr->wait); +done: + csr32w(ctlr, Imr, ctlr->ie); + iunlock(ctlr); +} + +static void +iwlshutdown(Ether *edev) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->power) + poweroff(ctlr); + ctlr->broken = 0; +} + +static Ctlr *iwlhead, *iwltail; + +static void +iwlpci(void) +{ + Pcidev *pdev; + + pdev = nil; + while(pdev = pcimatch(pdev, 0, 0)) { + Ctlr *ctlr; + void *mem; + + if(pdev->ccrb != 2 || pdev->ccru != 0x80) + continue; + if(pdev->vid != 0x8086) + continue; + + switch(pdev->did){ + default: + continue; + case 0x0084: /* WiFi Link 1000 */ + case 0x4229: /* WiFi Link 4965 */ + case 0x4230: /* WiFi Link 4965 */ + case 0x4232: /* Wifi Link 5100 */ + case 0x4236: /* WiFi Link 5300 AGN */ + case 0x4237: /* Wifi Link 5100 AGN */ + case 0x0085: /* Centrino Advanced-N 6205 */ + case 0x422b: /* Centrino Ultimate-N 6300 */ + case 0x08ae: /* Centrino Wireless-N 100 */ + break; + } + + /* Clear device-specific "PCI retry timeout" register (41h). */ + if(pcicfgr8(pdev, 0x41) != 0) + pcicfgw8(pdev, 0x41, 0); + + /* Clear interrupt disable bit. Hardware bug workaround. */ + if(pdev->pcr & 0x400){ + pdev->pcr &= ~0x400; + pcicfgw16(pdev, PciPCR, pdev->pcr); + } + + pcisetbme(pdev); + pcisetpms(pdev, 0); + + ctlr = malloc(sizeof(Ctlr)); + if(ctlr == nil) { + print("iwl: unable to alloc Ctlr\n"); + continue; + } + ctlr->port = pdev->mem[0].bar & ~0x0F; + mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size); + if(mem == nil) { + print("iwl: can't map %8.8luX\n", pdev->mem[0].bar); + free(ctlr); + continue; + } + ctlr->nic = mem; + ctlr->pdev = pdev; + ctlr->type = (csr32r(ctlr, Rev) >> 4) & 0xF; + + if(fwname[ctlr->type] == nil){ + print("iwl: unsupported controller type %d\n", ctlr->type); + vunmap(mem, pdev->mem[0].size); + free(ctlr); + continue; + } + + if(iwlhead != nil) + iwltail->link = ctlr; + else + iwlhead = ctlr; + iwltail = ctlr; + } +} + +static int +iwlpnp(Ether* edev) +{ + Ctlr *ctlr; + + if(iwlhead == nil) + iwlpci(); +again: + for(ctlr = iwlhead; ctlr != nil; ctlr = ctlr->link){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pdev->intl; + edev->tbdf = ctlr->pdev->tbdf; + edev->arg = edev; + edev->interrupt = iwlinterrupt; + edev->attach = iwlattach; + edev->ifstat = iwlifstat; + edev->ctl = iwlctl; + edev->shutdown = iwlshutdown; + edev->promiscuous = iwlpromiscuous; + edev->multicast = nil; + edev->mbps = 54; + + if(iwlinit(edev) < 0){ + edev->ctlr = nil; + goto again; + } + + return 0; +} + +void +etheriwllink(void) +{ + addethercard("iwl", iwlpnp); +} --- /sys/src/9/pc/etherrt2860.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/pc/etherrt2860.c Tue Apr 22 00:00:00 2014 @@ -0,0 +1,3567 @@ +/* + * Ralink RT2860 driver + * + * Written without any documentation but Damien Bergaminis + * OpenBSD ral(4) driver sources. Requires ralink firmware + * to be present in /lib/firmware/ral-rt2860 on attach. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "wifi.h" + +/* for consistency */ +typedef signed char s8int; + +enum { + /* PCI registers */ + PciCfg = 0x0000, + PciCfgUsb = (1 << 17), + PciCfgPci = (1 << 16), + PciEectrl = 0x0004, + EectrlC = (1 << 0), + EectrlS = (1 << 1), + EectrlD = (1 << 2), + EectrlShiftD = 2, + EectrlQ = (1 << 3), + EectrlShiftQ = 3, + PciMcuctrl = 0x0008, + PciSysctrl = 0x000c, + PcieJtag = 0x0010, + + Rt3090AuxCtrl = 0x010c, + + Rt3070Opt14 = 0x0114, +}; + +enum { + /* SCH/DMA registers */ + IntStatus = 0x0200, + /* flags for registers IntStatus/IntMask */ + TxCoherent = (1 << 17), + RxCoherent = (1 << 16), + MacInt4 = (1 << 15), + MacInt3 = (1 << 14), + MacInt2 = (1 << 13), + MacInt1 = (1 << 12), + MacInt0 = (1 << 11), + TxRxCoherent = (1 << 10), + McuCmdInt = (1 << 9), + TxDoneInt5 = (1 << 8), + TxDoneInt4 = (1 << 7), + TxDoneInt3 = (1 << 6), + TxDoneInt2 = (1 << 5), + TxDoneInt1 = (1 << 4), + TxDoneInt0 = (1 << 3), + RxDoneInt = (1 << 2), + TxDlyInt = (1 << 1), + RxDlyInt = (1 << 0), + IntMask = 0x0204, + WpdmaGloCfg = 0x0208, + HdrSegLenShift = 8, + BigEndian = (1 << 7), + TxWbDdone = (1 << 6), + WpdmaBtSizeShift = 4, + WpdmaBtSize16 = 0, + WpdmaBtSize32 = 1, + WpdmaBtSize64 = 2, + WpdmaBtSize128 = 3, + RxDmaBusy = (1 << 3), + RxDmaEn = (1 << 2), + TxDmaBusy = (1 << 1), + TxDmaEn = (1 << 0), + WpdmaRstIdx = 0x020c, + DelayIntCfg = 0x0210, + TxdlyIntEn = (1 << 31), + TxmaxPintShift = 24, + TxmaxPtimeShift = 16, + RxdlyIntEn = (1 << 15), + RxmaxPintShift = 8, + RxmaxPtimeShift = 0, + WmmAifsnCfg = 0x0214, + WmmCwminCfg = 0x0218, + WmmCwmaxCfg = 0x021c, + WmmTxop0Cfg = 0x0220, + WmmTxop1Cfg = 0x0224, + GpioCtrl = 0x0228, + GpioDShift = 8, + GpioOShift = 0, + McuCmdReg = 0x022c, +#define TxBasePtr(qid) (0x0230 + (qid) * 16) +#define TxMaxCnt(qid) (0x0234 + (qid) * 16) +#define TxCtxIdx(qid) (0x0238 + (qid) * 16) +#define TxDtxIdx(qid) (0x023c + (qid) * 16) + RxBasePtr = 0x0290, + RxMaxCnt = 0x0294, + RxCalcIdx = 0x0298, + FsDrxIdx = 0x029c, + UsbDmaCfg = 0x02a0 /* RT2870 only */, + UsbTxBusy = (1 << 31), + UsbRxBusy = (1 << 30), + UsbEpoutVldShift = 24, + UsbTxEn = (1 << 23), + UsbRxEn = (1 << 22), + UsbRxAggEn = (1 << 21), + UsbTxopHalt = (1 << 20), + UsbTxClear = (1 << 19), + UsbPhyWdEn = (1 << 16), + UsbPhyManRst = (1 << 15), +#define UsbRxAggLmt(x) ((x) << 8) /* in unit of 1KB */ +#define UsbRxAggTo(x) ((x) & 0xff) /* in unit of 33ns */ + UsCycCnt = 0x02a4, + TestEn = (1 << 24), + TestSelShift = 16, + BtModeEn = (1 << 8), + UsCycCntShift = 0, +}; + +enum { + /* PBF registers */ + SysCtrl = 0x0400, + HstPmSel = (1 << 16), + CapMode = (1 << 14), + PmeOen = (1 << 13), + Clkselect = (1 << 12), + PbfClkEn = (1 << 11), + MacClkEn = (1 << 10), + DmaClkEn = (1 << 9), + McuReady = (1 << 7), + AsyReset = (1 << 4), + PbfReset = (1 << 3), + MacReset = (1 << 2), + DmaReset = (1 << 1), + McuReset = (1 << 0), + HostCmd = 0x0404, + McuCmdSleep = 0x30, + McuCmdWakeup = 0x31, + McuCmdLeds = 0x50, + LedRadio = (1 << 13), + LedLink2ghz = (1 << 14), + LedLink5ghz = (1 << 15), + McuCmdLedRssi = 0x51, + McuCmdLed1 = 0x52, + McuCmdLed2 = 0x53, + McuCmdLed3 = 0x54, + McuCmdRfreset = 0x72, + McuCmdAntsel = 0x73, + McuCmdBbp = 0x80, + McuCmdPslevel = 0x83, + PbfCfg = 0x0408, + Tx1qNumShift = 21, + Tx2qNumShift = 16, + Null0Mode = (1 << 15), + Null1Mode = (1 << 14), + RxDropMode = (1 << 13), + Tx0qManual = (1 << 12), + Tx1qManual = (1 << 11), + Tx2qManual = (1 << 10), + Rx0qManual = (1 << 9), + HccaEn = (1 << 8), + Tx0qEn = (1 << 4), + Tx1qEn = (1 << 3), + Tx2qEn = (1 << 2), + Rx0qEn = (1 << 1), + MaxPcnt = 0x040c, + BufCtrl = 0x0410, +#define WriteTxq(qid) (1 << (11 - (qid))) + Null0Kick = (1 << 7), + Null1Kick = (1 << 6), + BufReset = (1 << 5), +#define ReadTxq(qid) = (1 << (3 - (qid)) + ReadRx0q = (1 << 0), + McuIntSta = 0x0414, + /* flags for registers McuIntSta/McuIntEna */ + McuMacInt8 = (1 << 24), + McuMacInt7 = (1 << 23), + McuMacInt6 = (1 << 22), + McuMacInt4 = (1 << 20), + McuMacInt3 = (1 << 19), + McuMacInt2 = (1 << 18), + McuMacInt1 = (1 << 17), + McuMacInt0 = (1 << 16), + Dtx0Int = (1 << 11), + Dtx1Int = (1 << 10), + Dtx2Int = (1 << 9), + Drx0Int = (1 << 8), + HcmdInt = (1 << 7), + N0txInt = (1 << 6), + N1txInt = (1 << 5), + BcntxInt = (1 << 4), + Mtx0Int = (1 << 3), + Mtx1Int = (1 << 2), + Mtx2Int = (1 << 1), + Mrx0Int = (1 << 0), + McuIntEna = 0x0418, +#define TxqIo(qid) (0x041c + (qid) * 4) + Rx0qIo = 0x0424, + BcnOffset0 = 0x042c, + BcnOffset1 = 0x0430, + TxrxqSta = 0x0434, + TxrxqPcnt = 0x0438, + Rx0qPcntMask = 0xff000000, + Tx2qPcntMask = 0x00ff0000, + Tx1qPcntMask = 0x0000ff00, + Tx0qPcntMask = 0x000000ff, + PbfDbg = 0x043c, + CapCtrl = 0x0440, + CapAdcFeq = (1 << 31), + CapStart = (1 << 30), + ManTrig = (1 << 29), + TrigOffsetShift = 16, + StartAddrShift = 0, +}; + +enum { + /* RT3070 registers */ + Rt3070RfCsrCfg = 0x0500, + Rt3070RfKick = (1 << 17), + Rt3070RfWrite = (1 << 16), + Rt3070EfuseCtrl = 0x0580, + Rt3070SelEfuse = (1 << 31), + Rt3070EfsromKick = (1 << 30), + Rt3070EfsromAinMask = 0x03ff0000, + Rt3070EfsromAinShift = 16, + Rt3070EfsromModeMask = 0x000000c0, + Rt3070EfuseAoutMask = 0x0000003f, + Rt3070EfuseData0 = 0x0590, + Rt3070EfuseData1 = 0x0594, + Rt3070EfuseData2 = 0x0598, + Rt3070EfuseData3 = 0x059c, + Rt3090OscCtrl = 0x05a4, + Rt3070LdoCfg0 = 0x05d4, + Rt3070GpioSwitch = 0x05dc, +}; + +enum { + /* MAC registers */ + AsicVerId = 0x1000, + MacSysCtrl = 0x1004, + RxTsEn = (1 << 7), + WlanHaltEn = (1 << 6), + PbfLoopEn = (1 << 5), + ContTxTest = (1 << 4), + MacRxEn = (1 << 3), + MacTxEn = (1 << 2), + BbpHrst = (1 << 1), + MacSrst = (1 << 0), + MacAddrDw0 = 0x1008, + MacAddrDw1 = 0x100c, + MacBssidDw0 = 0x1010, + MacBssidDw1 = 0x1014, + MultiBcnNumShift = 18, + MultiBssidModeShift = 16, + MaxLenCfg = 0x1018, + MinMpduLenShift = 16, + MaxPsduLenShift = 12, + MaxPsduLen8k = 0, + MaxPsduLen16k = 1, + MaxPsduLen32k = 2, + MaxPsduLen64k = 3, + MaxMpduLenShift = 0, + BbpCsrCfg = 0x101c, + BbpRwParallel = (1 << 19), + BbpParDur1125 = (1 << 18), + BbpCsrKick = (1 << 17), + BbpCsrRead = (1 << 16), + BbpAddrShift = 8, + BbpDataShift = 0, + RfCsrCfg0 = 0x1020, + RfRegCtrl = (1 << 31), + RfLeSel1 = (1 << 30), + RfLeStby = (1 << 29), + RfRegWidthShift = 24, + RfReg0Shift = 0, + RfCsrCfg1 = 0x1024, + RfDur5 = (1 << 24), + RfReg1Shift = 0, + RfCsrCfg2 = 0x1028, + LedCfg = 0x102c, + LedPol = (1 << 30), + YLedModeShift = 28, + GLedModeShift = 26, + RLedModeShift = 24, + LedModeOff = 0, + LedModeBlinkTx = 1, + LedModeSlowBlink = 2, + LedModeOn = 3, + SlowBlkTimeShift = 16, + LedOffTimeShift = 8, + LedOnTimeShift = 0, +}; + +enum { + /* undocumented registers */ + Debug = 0x10f4, +}; + +enum { + /* MAC Timing control registers */ + XifsTimeCfg = 0x1100, + BbRxendEn = (1 << 29), + EifsTimeShift = 20, + OfdmXifsTimeShift = 16, + OfdmSifsTimeShift = 8, + CckSifsTimeShift = 0, + BkoffSlotCfg = 0x1104, + CcDelayTimeShift = 8, + SlotTime = 0, + NavTimeCfg = 0x1108, + NavUpd = (1 << 31), + NavUpdValShift = 16, + NavClrEn = (1 << 15), + NavTimerShift = 0, + ChTimeCfg = 0x110c, + EifsAsChBusy = (1 << 4), + NavAsChBusy = (1 << 3), + RxAsChBusy = (1 << 2), + TxAsChBusy = (1 << 1), + ChStaTimerEn = (1 << 0), + PbfLifeTimer = 0x1110, + BcnTimeCfg = 0x1114, + TsfInsCompShift = 24, + BcnTxEn = (1 << 20), + TbttTimerEn = (1 << 19), + TsfSyncModeShift = 17, + TsfSyncModeDis = 0, + TsfSyncModeSta = 1, + TsfSyncModeIbss = 2, + TsfSyncModeHostap = 3, + TsfTimerEn = (1 << 16), + BcnIntvalShift = 0, + TbttSyncCfg = 0x1118, + BcnCwminShift = 20, + BcnAifsnShift = 16, + BcnExpWinShift = 8, + TbttAdjustShift = 0, + TsfTimerDw0 = 0x111c, + TsfTimerDw1 = 0x1120, + TbttTimer = 0x1124, + IntTimerCfg = 0x1128, + GpTimerShift = 16, + PreTbttTimerShift = 0, + IntTimerEn = 0x112c, + GpTimerEn = (1 << 1), + PreTbttIntEn = (1 << 0), + ChIdleTime = 0x1130, +}; + +enum { + /* MAC Power Save configuration registers */ + MacStatusReg = 0x1200, + RxStatusBusy = (1 << 1), + TxStatusBusy = (1 << 0), + PwrPinCfg = 0x1204, + IoAddaPd = (1 << 3), + IoPllPd = (1 << 2), + IoRaPe = (1 << 1), + IoRfPe = (1 << 0), + AutoWakeupCfg = 0x1208, + AutoWakeupEn = (1 << 15), + SleepTbttNumShift = 8, + WakeupLeadTimeShift = 0, +}; + +enum { + /* MAC TX configuration registers */ +#define EdcaAcCfg(aci) (0x1300 + (aci) * 4) + EdcaTidAcMap = 0x1310, +#define TxPwrCfg(ridx) (0x1314 + (ridx) * 4) + TxPinCfg = 0x1328, + Rt3593LnaPeG2Pol = (1 << 31), + Rt3593LnaPeA2Pol = (1 << 30), + Rt3593LnaPeG2En = (1 << 29), + Rt3593LnaPeA2En = (1 << 28), + Rt3593LnaPe2En = (Rt3593LnaPeA2En | Rt3593LnaPeG2En), + Rt3593PaPeG2Pol = (1 << 27), + Rt3593PaPeA2Pol = (1 << 26), + Rt3593PaPeG2En = (1 << 25), + Rt3593PaPeA2En = (1 << 24), + TrswPol = (1 << 19), + TrswEn = (1 << 18), + RftrPol = (1 << 17), + RftrEn = (1 << 16), + LnaPeG1Pol = (1 << 15), + LnaPeA1Pol = (1 << 14), + LnaPeG0Pol = (1 << 13), + LnaPeA0Pol = (1 << 12), + LnaPeG1En = (1 << 11), + LnaPeA1En = (1 << 10), + LnaPe1En = (LnaPeA1En | LnaPeG1En), + LnaPeG0En = (1 << 9), + LnaPeA0En = (1 << 8), + LnaPe0En = (LnaPeA0En | LnaPeG0En), + PaPeG1Pol = (1 << 7), + PaPeA1Pol = (1 << 6), + PaPeG0Pol = (1 << 5), + PaPeA0Pol = (1 << 4), + PaPeG1En = (1 << 3), + PaPeA1En = (1 << 2), + PaPeG0En = (1 << 1), + PaPeA0En = (1 << 0), + TxBandCfg = 0x132c, + Tx5gBandSelN = (1 << 2), + Tx5gBandSelP = (1 << 1), + TxBandSel = (1 << 0), + TxSwCfg0 = 0x1330, + DlyRftrEnShift = 24, + DlyTrswEnShift = 16, + DlyPapeEnShift = 8, + DlyTxpeEnShift = 0, + TxSwCfg1 = 0x1334, + DlyRftrDisShift = 16, + DlyTrswDisShift = 8, + DlyPapeDisShift = 0, + TxSwCfg2 = 0x1338, + DlyLnaEnShift = 24, + DlyLnaDisShift = 16, + DlyDacEnShift = 8, + DlyDacDisShift = 0, + TxopThresCfg = 0x133c, + TxopRemThresShift = 24, + CfEndThresShift = 16, + RdgInThres = 8, + RdgOutThres = 0, + TxopCtrlCfg = 0x1340, + ExtCwMinShift = 16, + ExtCcaDlyShift = 8, + ExtCcaEn = (1 << 7), + LsigTxopEn = (1 << 6), + TxopTrunEnMimops = (1 << 4), + TxopTrunEnTxop = (1 << 3), + TxopTrunEnRate = (1 << 2), + TxopTrunEnAc = (1 << 1), + TxopTrunEnTimeout = (1 << 0), + TxRtsCfg = 0x1344, + RtsFbkEn = (1 << 24), + RtsThresShift = 8, + RtsRtyLimitShift = 0, + TxTimeoutCfg = 0x1348, + TxopTimeoutShift = 16, + RxAckTimeoutShift = 8, + MpduLifeTimeShift = 4, + TxRtyCfg = 0x134c, + TxAutofbEn = (1 << 30), + AggRtyModeTimer = (1 << 29), + NagRtyModeTimer = (1 << 28), + LongRtyThresShift = 16, + LongRtyLimitShift = 8, + ShortRtyLimitShift = 0, + TxLinkCfg = 0x1350, + RemoteMfsShift = 24, + RemoteMfbShift = 16, + TxCfackEn = (1 << 12), + TxRdgEn = (1 << 11), + TxMrqEn = (1 << 10), + RemoteUmfsEn = (1 << 9), + TxMfbEn = (1 << 8), + RemoteMfbLtShift = 0, + HtFbkCfg0 = 0x1354, + HtFbkCfg1 = 0x1358, + LgFbkCfg0 = 0x135c, + LgFbkCfg1 = 0x1360, + CckProtCfg = 0x1364, + /* possible flags for registers *ProtCfg */ + RtsthEn = (1 << 26), + TxopAllowGf40 = (1 << 25), + TxopAllowGf20 = (1 << 24), + TxopAllowMm40 = (1 << 23), + TxopAllowMm20 = (1 << 22), + TxopAllowOfdm = (1 << 21), + TxopAllowCck = (1 << 20), + TxopAllowAll = (0x3f << 20), + ProtNavShort = (1 << 18), + ProtNavLong = (2 << 18), + ProtCtrlRtsCts = (1 << 16), + ProtCtrlCts = (2 << 16), + OfdmProtCfg = 0x1368, + Mm20ProtCfg = 0x136c, + Mm40ProtCfg = 0x1370, + Gf20ProtCfg = 0x1374, + Gf40ProtCfg = 0x1378, + ExpCtsTime = 0x137c, + /* possible flags for registers EXP_{CTS,ACK}_TIME */ + ExpOfdmTimeShift = 16, + ExpCckTimeShift = 0, + ExpAckTime = 0x1380, +}; + +enum { + /* MAC RX configuration registers */ + RxFiltrCfg = 0x1400, + DropCtrlRsv = (1 << 16), + DropBar = (1 << 15), + DropBa = (1 << 14), + DropPspoll = (1 << 13), + DropRts = (1 << 12), + DropCts = (1 << 11), + DropAck = (1 << 10), + DropCfend = (1 << 9), + DropCfack = (1 << 8), + DropDupl = (1 << 7), + DropBc = (1 << 6), + DropMc = (1 << 5), + DropVerErr = (1 << 4), + DropNotMybss = (1 << 3), + DropUcNome = (1 << 2), + DropPhyErr = (1 << 1), + DropCrcErr = (1 << 0), + AutoRspCfg = 0x1404, + CtrlPwrBit = (1 << 7), + BacAckPolicy = (1 << 6), + CckShortEn = (1 << 4), + Cts40mRefEn = (1 << 3), + Cts40mModeEn = (1 << 2), + BacAckpolicyEn = (1 << 1), + AutoRspEn = (1 << 0), + LegacyBasicRate = 0x1408, + HtBasicRate = 0x140c, + HtCtrlCfg = 0x1410, + SifsCostCfg = 0x1414, + OfdmSifsCostShift = 8, + CckSifsCostShift = 0, + RxParserCfg = 0x1418, +}; + +enum { + /* MAC Security configuration registers */ + TxSecCnt0 = 0x1500, + RxSecCnt0 = 0x1504, + CcmpFcMute = 0x1508, +}; + +enum { + /* MAC HCCA/PSMP configuration registers */ + TxopHldrAddr0 = 0x1600, + TxopHldrAddr1 = 0x1604, + TxopHldrEt = 0x1608, + TxopEtm1En = (1 << 25), + TxopEtm0En = (1 << 24), + TxopEtmThresShift = 16, + TxopEtoEn = (1 << 8), + TxopEtoThresShift = 1, + PerRxRstEn = (1 << 0), + QosCfpollRaDw0 = 0x160c, + QosCfpollA1Dw1 = 0x1610, + QosCfpollQc = 0x1614, +}; + +enum { + /* MAC Statistics Counters */ + RxStaCnt0 = 0x1700, + RxStaCnt1 = 0x1704, + RxStaCnt2 = 0x1708, + TxStaCnt0 = 0x170c, + TxStaCnt1 = 0x1710, + TxStaCnt2 = 0x1714, + TxStatFifo = 0x1718, + TxqMcsShift = 16, + TxqWcidShift = 8, + TxqAckreq = (1 << 7), + TxqAgg = (1 << 6), + TxqOk = (1 << 5), + TxqPidShift = 1, + TxqVld = (1 << 0), +}; + +/* RX WCID search table */ +#define WcidEntry(wcid) (0x1800 + (wcid) * 8) + +enum { + FwBase = 0x2000, + Rt2870FwBase = 0x3000, +}; + +/* Pair-wise key table */ +#define Pkey(wcid) (0x4000 + (wcid) * 32) + +/* IV/EIV table */ +#define Iveiv(wcid) (0x6000 + (wcid) * 8) + +/* WCID attribute table */ +#define WcidAttr(wcid) (0x6800 + (wcid) * 4) + +/* possible flags for register WCID_ATTR */ +enum { + ModeNosec = 0, + ModeWep40 = 1, + ModeWep104 = 2, + ModeTkip = 3, + ModeAesCcmp = 4, + ModeCkip40 = 5, + ModeCkip104 = 6, + ModeCkip128 = 7, + RxPkeyEn = (1 << 0), +}; + +/* Shared Key Table */ +#define Skey(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32) + +/* Shared Key Mode */ +enum { + SkeyMode07 = 0x7000, + SkeyMode815 = 0x7004, + SkeyMode1623 = 0x7008, + SkeyMode2431 = 0x700c, +}; + +enum { + /* Shared Memory between MCU and host */ + H2mMailbox = 0x7010, + H2mBusy = (1 << 24), + TokenNoIntr = 0xff, + H2mMailboxCid = 0x7014, + H2mMailboxStatus = 0x701c, + H2mBbpagent = 0x7028, +#define BcnBase(vap) (0x7800 + (vap) * 512) +}; + +/* + * RT2860 TX descriptor + * -------------------- + * u32int sdp0 Segment Data Pointer 0 + * u16int sdl1 Segment Data Length 1 + * u16int sdl0 Segment Data Length 0 + * u32int sdp1 Segment Data Pointer 1 + * u8int reserved[3] + * u8int flags + */ + +enum { + /* sdl1 flags */ + TxBurst = (1 << 15), + TxLs1 = (1 << 14) /* SDP1 is the last segment */, + /* sdl0 flags */ + TxDdone = (1 << 15), + TxLs0 = (1 << 14) /* SDP0 is the last segment */, + /* flags */ + TxQselShift = 1, + TxQselMgmt = (0 << 1), + TxQselHcca = (1 << 1), + TxQselEdca = (2 << 1), + TxWiv = (1 << 0), +}; + +/* + * TX Wireless Information + * ----------------------- + * u8int flags + * u8int txop + * u16int phy + * u8int xflags + * u8int wcid Wireless Client ID + * u16int len + * u32int iv + * u32int eiv + */ + +enum { + /* flags */ + TxMpduDsityShift = 5, + TxAmpdu = (1 << 4), + TxTs = (1 << 3), + TxCfack = (1 << 2), + TxMmps = (1 << 1), + TxFrag = (1 << 0), + /* txop */ + TxTxopHt = 0, + TxTxopPifs = 1, + TxTxopSifs = 2, + TxTxopBackoff = 3, + /* phy */ + PhyMode = 0xc000, + PhyCck = (0 << 14), + PhyOfdm = (1 << 14), + PhyHt = (2 << 14), + PhyHtGf = (3 << 14), + PhySgi = (1 << 8), + PhyBw40 = (1 << 7), + PhyMcs = 0x7f, + PhyShpre = (1 << 3), + /* xflags */ + TxBawinsizeShift = 2, + TxNseq = (1 << 1), + TxAck = (1 << 0), + /* len */ + TxPidShift = 12, +}; + +/* + * RT2860 RX descriptor + * -------------------- + * u32int sdp0 + * u16int sdl1 unused + * u16int sdl0 + * u32int sdp1 unused + * u32int flags + */ + +enum { + /* sdl flags */ + RxDdone = (1 << 15), + RxLs0 = (1 << 14), + /* flags */ + RxDec = (1 << 16), + RxAmpdu = (1 << 15), + RxL2pad = (1 << 14), + RxRssi = (1 << 13), + RxHtc = (1 << 12), + RxAmsdu = (1 << 11), + RxMicerr = (1 << 10), + RxIcverr = (1 << 9), + RxCrcerr = (1 << 8), + RxMybss = (1 << 7), + RxBc = (1 << 6), + RxMc = (1 << 5), + RxUc2me = (1 << 4), + RxFrag = (1 << 3), + RxNull = (1 << 2), + RxData = (1 << 1), + RxBa = (1 << 0), +}; + +/* + * RX Wireless Information + * ----------------------- + * u8int wcid + * u8int keyidx + * u16int len + * u16int seq + * u16int phy + * u8int rssi[3] + * u8int reserved1 + * u8int snr[2] + * u16int reserved2 + */ + +enum { + /* keyidx flags */ + RxUdfShift = 5, + RxBssIdxShift = 2, + /* len flags */ + RxTidShift = 12, +}; + +enum { + WIFIHDRSIZE = 2+2+3*6+2, + Rdscsize = 16, + Tdscsize = 16, + Rbufsize = 4096, + Tbufsize = 4096, + Rxwisize = 16, + Txwisize = 16, + /* first DMA segment contains TXWI + 802.11 header + 32-bit padding */ + TxwiDmaSz = Txwisize + WIFIHDRSIZE + 2 +}; + +/* RF registers */ +enum { + Rf1 = 0, + Rf2 = 2, + Rf3 = 1, + Rf4 = 3, +}; + +enum { + Rf2820 = 1 /* 2T3R */, + Rf2850 = 2 /* dual-band 2T3R */, + Rf2720 = 3 /* 1T2R */, + Rf2750 = 4 /* dual-band 1T2R */, + Rf3020 = 5 /* 1T1R */, + Rf2020 = 6 /* b/g */, + Rf3021 = 7 /* 1T2R */, + Rf3022 = 8 /* 2T2R */, + Rf3052 = 9 /* dual-band 2T2R */, + Rf3320 = 11 /* 1T1R */, + Rf3053 = 13 /* dual-band 3T3R */, +}; + +enum { + Rt3070RfBlock = (1 << 0), + Rt3070Rx0Pd = (1 << 2), + Rt3070Tx0Pd = (1 << 3), + Rt3070Rx1Pd = (1 << 4), + Rt3070Tx1Pd = (1 << 5), + Rt3070Rx2Pd = (1 << 6), + Rt3070Tx2Pd = (1 << 7), + Rt3070Tune = (1 << 0), + Rt3070TxLo2 = (1 << 3), + Rt3070TxLo1 = (1 << 3), + Rt3070RxLo1 = (1 << 3), + Rt3070RxLo2 = (1 << 3), + Rt3070RxCtb = (1 << 7), + Rt3070BbLoopback = (1 << 0), + + Rt3593Vco = (1 << 0), + Rt3593Rescal = (1 << 7), + Rt3593Vcocal = (1 << 7), + Rt3593VcoIc = (1 << 6), + Rt3593LdoPllVcMask = 0x0e, + Rt3593LdoRfVcMask = 0xe0, + Rt3593CpIcMask = 0xe0, + Rt3593CpIcShift = 5, + Rt3593RxCtb = (1 << 5) +}; + +static const char* rfnames[] = { + [Rf2820] "RT2820", + [Rf2850] "RT2850", + [Rf2720] "RT2720", + [Rf2750] "RT2750", + [Rf3020] "RT3020", + [Rf2020] "RT2020", + [Rf3021] "RT3021", + [Rf3022] "RT3022", + [Rf3052] "RT3052", + [Rf3320] "RT3320", + [Rf3053] "RT3053", +}; + +enum { + /* USB commands, RT2870 only */ + Rt2870Reset = 1, + Rt2870Write2 = 2, + Rt2870WriteRegion1 = 6, + Rt2870ReadRegion1 = 7, + Rt2870EepromRead = 9, +}; + +enum { + EepromDelay = 1 /* minimum hold time (microsecond) */, + + EepromVersion = 0x01, + EepromMac01 = 0x02, + EepromMac23 = 0x03, + EepromMac45 = 0x04, + EepromPciePslevel = 0x11, + EepromRev = 0x12, + EepromAntenna = 0x1a, + EepromConfig = 0x1b, + EepromCountry = 0x1c, + EepromFreqLeds = 0x1d, + EepromLed1 = 0x1e, + EepromLed2 = 0x1f, + EepromLed3 = 0x20, + EepromLna = 0x22, + EepromRssi12ghz = 0x23, + EepromRssi22ghz = 0x24, + EepromRssi15ghz = 0x25, + EepromRssi25ghz = 0x26, + EepromDeltapwr = 0x28, + EepromPwr2ghzBase1 = 0x29, + EepromPwr2ghzBase2 = 0x30, + EepromTssi12ghz = 0x37, + EepromTssi22ghz = 0x38, + EepromTssi32ghz = 0x39, + EepromTssi42ghz = 0x3a, + EepromTssi52ghz = 0x3b, + EepromPwr5ghzBase1 = 0x3c, + EepromPwr5ghzBase2 = 0x53, + EepromTssi15ghz = 0x6a, + EepromTssi25ghz = 0x6b, + EepromTssi35ghz = 0x6c, + EepromTssi45ghz = 0x6d, + EepromTssi55ghz = 0x6e, + EepromRpwr = 0x6f, + EepromBbpBase = 0x78, + Rt3071EepromRfBase = 0x82, +}; + +enum { + RidxCck1 = 0, + RidxCck11 = 3, + RidxOfdm6 = 4, + RidxMax = 11, +}; + +/* ring and pool count */ +enum { + Nrx = 128, + Ntx = 64, + Ntxpool = Ntx * 2 +}; + +typedef struct FWImage FWImage; +typedef struct TXQ TXQ; +typedef struct RXQ RXQ; +typedef struct Pool Pool; + +typedef struct Ctlr Ctlr; + +struct FWImage { + uint size; + uchar *data; +}; + +struct TXQ +{ + uint n; /* next */ + uint i; /* current */ + Block **b; + u32int *d; /* descriptors */ + + Rendez; + QLock; +}; + +struct RXQ +{ + uint i; + Block **b; + u32int *p; +}; + +struct Pool +{ + uint i; /* current */ + uchar *p; /* txwi */ +}; + +struct Ctlr { + Lock; + QLock; + + Ctlr *link; + Pcidev *pdev; + Wifi *wifi; + + u16int mac_ver; + u16int mac_rev; + u8int rf_rev; + u8int freq; + u8int ntxchains; + u8int nrxchains; + u8int pslevel; + s8int txpow1[54]; + s8int txpow2[54]; + s8int rssi_2ghz[3]; + s8int rssi_5ghz[3]; + u8int lna[4]; + u8int rf24_20mhz; + u8int rf24_40mhz; + u8int patch_dac; + u8int rfswitch; + u8int ext_2ghz_lna; + u8int ext_5ghz_lna; + u8int calib_2ghz; + u8int calib_5ghz; + u8int txmixgain_2ghz; + u8int txmixgain_5ghz; + u8int tssi_2ghz[9]; + u8int tssi_5ghz[9]; + u8int step_2ghz; + u8int step_5ghz; + uint mgtqid; + + struct { + u8int reg; + u8int val; + } bbp[8], rf[10]; + u8int leds; + u16int led[3]; + u32int txpow20mhz[5]; + u32int txpow40mhz_2ghz[5]; + u32int txpow40mhz_5ghz[5]; + + int flags; + + int port; + int power; + int active; + int broken; + int attached; + + u32int *nic; + + /* assigned node ids in hardware node table or -1 if unassigned */ + int bcastnodeid; + int bssnodeid; + u8int wcid; + /* current receiver settings */ + uchar bssid[Eaddrlen]; + int channel; + int prom; + int aid; + + RXQ rx; + TXQ tx[6]; + Pool pool; + + FWImage *fw; +}; + +/* controller flags */ +enum { + AdvancedPs = 1 << 0, + ConnPciE = 1 << 1, +}; + +static const struct rt2860_rate { + u8int rate; + u8int mcs; + /*enum ieee80211_phytype phy;*/ + u8int ctl_ridx; + u16int sp_ack_dur; + u16int lp_ack_dur; +} rt2860_rates[] = { + { 2, 0,/* IEEE80211_T_DS,*/ 0, 314, 314 }, + { 4, 1,/* IEEE80211_T_DS,*/ 1, 258, 162 }, + { 11, 2,/* IEEE80211_T_DS,*/ 2, 223, 127 }, + { 22, 3,/* IEEE80211_T_DS,*/ 3, 213, 117 }, + { 12, 0,/* IEEE80211_T_OFDM,*/ 4, 60, 60 }, + { 18, 1,/* IEEE80211_T_OFDM,*/ 4, 52, 52 }, + { 24, 2,/* IEEE80211_T_OFDM,*/ 6, 48, 48 }, + { 36, 3,/* IEEE80211_T_OFDM,*/ 6, 44, 44 }, + { 48, 4,/* IEEE80211_T_OFDM,*/ 8, 44, 44 }, + { 72, 5,/* IEEE80211_T_OFDM,*/ 8, 40, 40 }, + { 96, 6,/* IEEE80211_T_OFDM,*/ 8, 40, 40 }, + { 108, 7,/* IEEE80211_T_OFDM,*/ 8, 40, 40 } +}; + +/* + * Default values for MAC registers; values taken from the reference driver. + */ +static const struct { + u32int reg; + u32int val; +} rt2860_def_mac[] = { + { BcnOffset0, 0xf8f0e8e0 }, + { LegacyBasicRate, 0x0000013f }, + { HtBasicRate, 0x00008003 }, + { MacSysCtrl, 0x00000000 }, + { BkoffSlotCfg, 0x00000209 }, + { TxSwCfg0, 0x00000000 }, + { TxSwCfg1, 0x00080606 }, + { TxLinkCfg, 0x00001020 }, + { TxTimeoutCfg, 0x000a2090 }, + { LedCfg, 0x7f031e46 }, + { WmmAifsnCfg, 0x00002273 }, + { WmmCwminCfg, 0x00002344 }, + { WmmCwmaxCfg, 0x000034aa }, + { MaxPcnt, 0x1f3fbf9f }, + { TxRtyCfg, 0x47d01f0f }, + { AutoRspCfg, 0x00000013 }, + { CckProtCfg, 0x05740003 }, + { OfdmProtCfg, 0x05740003 }, + { Gf20ProtCfg, 0x01744004 }, + { Gf40ProtCfg, 0x03f44084 }, + { Mm20ProtCfg, 0x01744004 }, + { Mm40ProtCfg, 0x03f54084 }, + { TxopCtrlCfg, 0x0000583f }, + { TxopHldrEt, 0x00000002 }, + { TxRtsCfg, 0x00092b20 }, + { ExpAckTime, 0x002400ca }, + { XifsTimeCfg, 0x33a41010 }, + { PwrPinCfg, 0x00000003 }, +}; + +/* + * Default values for BBP registers; values taken from the reference driver. + */ +static const struct { + u8int reg; + u8int val; +} rt2860_def_bbp[] = { + { 65, 0x2c }, + { 66, 0x38 }, + { 69, 0x12 }, + { 70, 0x0a }, + { 73, 0x10 }, + { 81, 0x37 }, + { 82, 0x62 }, + { 83, 0x6a }, + { 84, 0x99 }, + { 86, 0x00 }, + { 91, 0x04 }, + { 92, 0x00 }, + { 103, 0x00 }, + { 105, 0x05 }, + { 106, 0x35 }, +}; + +/* + * Default settings for RF registers; values derived from the reference driver. + */ +static const struct rfprog { + u8int chan; + u32int r1, r2, r3, r4; +} rt2860_rf2850[] = { + { 1, 0x100bb3, 0x1301e1, 0x05a014, 0x001402 }, + { 2, 0x100bb3, 0x1301e1, 0x05a014, 0x001407 }, + { 3, 0x100bb3, 0x1301e2, 0x05a014, 0x001402 }, + { 4, 0x100bb3, 0x1301e2, 0x05a014, 0x001407 }, + { 5, 0x100bb3, 0x1301e3, 0x05a014, 0x001402 }, + { 6, 0x100bb3, 0x1301e3, 0x05a014, 0x001407 }, + { 7, 0x100bb3, 0x1301e4, 0x05a014, 0x001402 }, + { 8, 0x100bb3, 0x1301e4, 0x05a014, 0x001407 }, + { 9, 0x100bb3, 0x1301e5, 0x05a014, 0x001402 }, + { 10, 0x100bb3, 0x1301e5, 0x05a014, 0x001407 }, + { 11, 0x100bb3, 0x1301e6, 0x05a014, 0x001402 }, + { 12, 0x100bb3, 0x1301e6, 0x05a014, 0x001407 }, + { 13, 0x100bb3, 0x1301e7, 0x05a014, 0x001402 }, + { 14, 0x100bb3, 0x1301e8, 0x05a014, 0x001404 }, + { 36, 0x100bb3, 0x130266, 0x056014, 0x001408 }, + { 38, 0x100bb3, 0x130267, 0x056014, 0x001404 }, + { 40, 0x100bb2, 0x1301a0, 0x056014, 0x001400 }, + { 44, 0x100bb2, 0x1301a0, 0x056014, 0x001408 }, + { 46, 0x100bb2, 0x1301a1, 0x056014, 0x001402 }, + { 48, 0x100bb2, 0x1301a1, 0x056014, 0x001406 }, + { 52, 0x100bb2, 0x1301a2, 0x056014, 0x001404 }, + { 54, 0x100bb2, 0x1301a2, 0x056014, 0x001408 }, + { 56, 0x100bb2, 0x1301a3, 0x056014, 0x001402 }, + { 60, 0x100bb2, 0x1301a4, 0x056014, 0x001400 }, + { 62, 0x100bb2, 0x1301a4, 0x056014, 0x001404 }, + { 64, 0x100bb2, 0x1301a4, 0x056014, 0x001408 }, + { 100, 0x100bb2, 0x1301ac, 0x05e014, 0x001400 }, + { 102, 0x100bb2, 0x1701ac, 0x15e014, 0x001404 }, + { 104, 0x100bb2, 0x1701ac, 0x15e014, 0x001408 }, + { 108, 0x100bb3, 0x17028c, 0x15e014, 0x001404 }, + { 110, 0x100bb3, 0x13028d, 0x05e014, 0x001400 }, + { 112, 0x100bb3, 0x13028d, 0x05e014, 0x001406 }, + { 116, 0x100bb3, 0x13028e, 0x05e014, 0x001408 }, + { 118, 0x100bb3, 0x13028f, 0x05e014, 0x001404 }, + { 120, 0x100bb1, 0x1300e0, 0x05e014, 0x001400 }, + { 124, 0x100bb1, 0x1300e0, 0x05e014, 0x001404 }, + { 126, 0x100bb1, 0x1300e0, 0x05e014, 0x001406 }, + { 128, 0x100bb1, 0x1300e0, 0x05e014, 0x001408 }, + { 132, 0x100bb1, 0x1300e1, 0x05e014, 0x001402 }, + { 134, 0x100bb1, 0x1300e1, 0x05e014, 0x001404 }, + { 136, 0x100bb1, 0x1300e1, 0x05e014, 0x001406 }, + { 140, 0x100bb1, 0x1300e2, 0x05e014, 0x001400 }, + { 149, 0x100bb1, 0x1300e2, 0x05e014, 0x001409 }, + { 151, 0x100bb1, 0x1300e3, 0x05e014, 0x001401 }, + { 153, 0x100bb1, 0x1300e3, 0x05e014, 0x001403 }, + { 157, 0x100bb1, 0x1300e3, 0x05e014, 0x001407 }, + { 159, 0x100bb1, 0x1300e3, 0x05e014, 0x001409 }, + { 161, 0x100bb1, 0x1300e4, 0x05e014, 0x001401 }, + { 165, 0x100bb1, 0x1300e4, 0x05e014, 0x001405 }, + { 167, 0x100bb1, 0x1300f4, 0x05e014, 0x001407 }, + { 169, 0x100bb1, 0x1300f4, 0x05e014, 0x001409 }, + { 171, 0x100bb1, 0x1300f5, 0x05e014, 0x001401 }, + { 173, 0x100bb1, 0x1300f5, 0x05e014, 0x001403 }, +}; + +struct { + u8int n; + u8int r; + u8int k; +} rt3090_freqs[] = { + { 0xf1, 2, 2 }, + { 0xf1, 2, 7 }, + { 0xf2, 2, 2 }, + { 0xf2, 2, 7 }, + { 0xf3, 2, 2 }, + { 0xf3, 2, 7 }, + { 0xf4, 2, 2 }, + { 0xf4, 2, 7 }, + { 0xf5, 2, 2 }, + { 0xf5, 2, 7 }, + { 0xf6, 2, 2 }, + { 0xf6, 2, 7 }, + { 0xf7, 2, 2 }, + { 0xf8, 2, 4 }, + { 0x56, 0, 4 }, + { 0x56, 0, 6 }, + { 0x56, 0, 8 }, + { 0x57, 0, 0 }, + { 0x57, 0, 2 }, + { 0x57, 0, 4 }, + { 0x57, 0, 8 }, + { 0x57, 0, 10 }, + { 0x58, 0, 0 }, + { 0x58, 0, 4 }, + { 0x58, 0, 6 }, + { 0x58, 0, 8 }, + { 0x5b, 0, 8 }, + { 0x5b, 0, 10 }, + { 0x5c, 0, 0 }, + { 0x5c, 0, 4 }, + { 0x5c, 0, 6 }, + { 0x5c, 0, 8 }, + { 0x5d, 0, 0 }, + { 0x5d, 0, 2 }, + { 0x5d, 0, 4 }, + { 0x5d, 0, 8 }, + { 0x5d, 0, 10 }, + { 0x5e, 0, 0 }, + { 0x5e, 0, 4 }, + { 0x5e, 0, 6 }, + { 0x5e, 0, 8 }, + { 0x5f, 0, 0 }, + { 0x5f, 0, 9 }, + { 0x5f, 0, 11 }, + { 0x60, 0, 1 }, + { 0x60, 0, 5 }, + { 0x60, 0, 7 }, + { 0x60, 0, 9 }, + { 0x61, 0, 1 }, + { 0x61, 0, 3 }, + { 0x61, 0, 5 }, + { 0x61, 0, 7 }, + { 0x61, 0, 9 } +}; + +static const struct { + u8int reg; + u8int val; +} rt3090_def_rf[] = { + { 4, 0x40 }, + { 5, 0x03 }, + { 6, 0x02 }, + { 7, 0x70 }, + { 9, 0x0f }, + { 10, 0x41 }, + { 11, 0x21 }, + { 12, 0x7b }, + { 14, 0x90 }, + { 15, 0x58 }, + { 16, 0xb3 }, + { 17, 0x92 }, + { 18, 0x2c }, + { 19, 0x02 }, + { 20, 0xba }, + { 21, 0xdb }, + { 24, 0x16 }, + { 25, 0x01 }, + { 29, 0x1f } +}; + +/* vendors */ +enum { + Ralink = 0x1814, + Awt = 0x1a3b, +}; +/* products */ +enum { + RalinkRT2890 = 0x0681, + RalinkRT2790 = 0x0781, + RalinkRT3090 = 0x3090, + AwtRT2890 = 0x1059, +}; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static int rbplant(Ctlr*, int); +static void setchan(Ctlr*, uint); +static void rt3090setchan(Ctlr*, uint); +static void selchangroup(Ctlr*, int); +static void setleds(Ctlr*, u16int); + +static uint +get16(uchar *p){ + return *((u16int*)p); +} +static uint +get32(uchar *p){ + return *((u32int*)p); +} +static void +put32(uchar *p, uint v){ + *((u32int*)p) = v; +} +static void +put16(uchar *p, uint v){ + *((u16int*)p) = v; +}; +static void +memwrite(Ctlr *ctlr, u32int off, uchar *data, uint size){ + memmove((uchar*)ctlr->nic + off, data, size); +} +static void +setregion(Ctlr *ctlr, u32int off, uint val, uint size){ + memset((uchar*)ctlr->nic + off, val, size); +} + +static long +rt2860ctl(Ether *edev, void *buf, long n) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->wifi) + return wifictl(ctlr->wifi, buf, n); + return 0; +} + +static long +rt2860ifstat(Ether *edev, void *buf, long n, ulong off) +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + if(ctlr->wifi) + return wifistat(ctlr->wifi, buf, n, off); + return 0; +} + +static void +setoptions(Ether *edev) +{ + Ctlr *ctlr; + char buf[64]; + int i; + + ctlr = edev->ctlr; + for(i = 0; i < edev->nopt; i++){ + if(strncmp(edev->opt[i], "essid=", 6) == 0){ + snprint(buf, sizeof(buf), "essid %s", edev->opt[i]+6); + if(!waserror()){ + wifictl(ctlr->wifi, buf, strlen(buf)); + poperror(); + } + } + } +} + +static void +rxon(Ether *edev, Wnode *bss) +{ + u32int tmp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + + if(bss != nil){ + ctlr->channel = bss->channel; + memmove(ctlr->bssid, bss->bssid, Eaddrlen); + ctlr->aid = bss->aid; + if(ctlr->aid != 0){ + if(ctlr->wifi->debug) + print("new assoc!"); + ctlr->bssnodeid = -1; + }else + ctlr->bcastnodeid = -1; + }else{ + memmove(ctlr->bssid, edev->bcast, Eaddrlen); + ctlr->aid = 0; + ctlr->bcastnodeid = -1; + ctlr->bssnodeid = -1; + } + if(ctlr->aid != 0) + setleds(ctlr, LedRadio | LedLink2ghz); + else + setleds(ctlr, LedRadio); + + if(ctlr->wifi->debug) + print("#l%d: rxon: bssid %E, aid %x, channel %d wcid %d\n", + edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, ctlr->wcid); + + /* Set channel */ + if(ctlr->mac_ver >= 0x3071) + rt3090setchan(ctlr, ctlr->channel); + else + setchan(ctlr, ctlr->channel); + selchangroup(ctlr, 0); + microdelay(1000); + + /* enable mrr(?) */ +#define CCK(mcs) (mcs) +#define OFDM(mcs) (1 << 3 | (mcs)) + csr32w(ctlr, LgFbkCfg0, + OFDM(6) << 28 | /* 54->48 */ + OFDM(5) << 24 | /* 48->36 */ + OFDM(4) << 20 | /* 36->24 */ + OFDM(3) << 16 | /* 24->18 */ + OFDM(2) << 12 | /* 18->12 */ + OFDM(1) << 8 | /* 12-> 9 */ + OFDM(0) << 4 | /* 9-> 6 */ + OFDM(0)); /* 6-> 6 */ + + csr32w(ctlr, LgFbkCfg1, + CCK(2) << 12 | /* 11->5.5 */ + CCK(1) << 8 | /* 5.5-> 2 */ + CCK(0) << 4 | /* 2-> 1 */ + CCK(0)); /* 1-> 1 */ +#undef OFDM +#undef CCK + /* update slot */ + tmp = csr32r(ctlr, BkoffSlotCfg); + tmp &= ~0xff; + tmp |= /* (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : */ 20; + csr32w(ctlr, BkoffSlotCfg, tmp); + + /* set TX preamble */ + tmp = csr32r(ctlr, AutoRspCfg); + tmp &= ~CckShortEn; +/* if(sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= CckShortEn; */ + csr32w(ctlr, AutoRspCfg, tmp); + + /* set basic rates */ + csr32w(ctlr, LegacyBasicRate, 0x003); /* 11B */ + + /* Set BSSID */ + csr32w(ctlr, MacBssidDw0, + ctlr->bssid[0] | ctlr->bssid[1] << 8 | ctlr->bssid[2] << 16 | ctlr->bssid[3] << 24); + csr32w(ctlr, MacBssidDw1, + ctlr->bssid[4] | ctlr->bssid[5] << 8); + + if(ctlr->bcastnodeid == -1){ + ctlr->bcastnodeid = 0xff; + memwrite(ctlr, WcidEntry(ctlr->bcastnodeid), edev->bcast, Eaddrlen); + } + if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){ + ctlr->bssnodeid = 0; + memwrite(ctlr, WcidEntry(ctlr->bssnodeid), ctlr->bssid, Eaddrlen); + } +} + +static void +rt2860promiscuous(void *arg, int on) +{ + Ether *edev; + Ctlr *ctlr; + + edev = arg; + ctlr = edev->ctlr; + if(ctlr->attached == 0) + return; + qlock(ctlr); + ctlr->prom = on; + rxon(edev, ctlr->wifi->bss); + qunlock(ctlr); +} + +static FWImage* +readfirmware(void){ + static char name[] = "ral-rt2860"; + uchar dirbuf[sizeof(Dir)+100], *data; + char buf[128]; + FWImage *fw; + int n, r; + Chan *c; + Dir d; + + if(!iseve()) + error(Eperm); + if(!waserror()){ + snprint(buf, sizeof buf, "/boot/%s", name); + c = namec(buf, Aopen, OREAD, 0); + poperror(); + }else{ + snprint(buf, sizeof buf, "/lib/firmware/%s", name); + c = namec(buf, Aopen, OREAD, 0); + } + if(waserror()){ + cclose(c); + nexterror(); + } + n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf); + if(n <= 0) + error("can't stat firmware"); + convM2D(dirbuf, n, &d, nil); + fw = malloc(sizeof(*fw)); + fw->size = d.length; + data = fw->data = smalloc(d.length); + if(waserror()){ + free(fw); + nexterror(); + } + r = 0; + while(r < d.length){ + n = devtab[c->type]->read(c, data+r, d.length-r, (vlong)r); + if(n <= 0) + break; + r += n; + } + poperror(); + poperror(); + cclose(c); + return fw; +} + +static char* +boot(Ctlr *ctlr) +{ + int ntries; + + /* set "host program ram write selection" bit */ + csr32w(ctlr, SysCtrl, HstPmSel); + /* write microcode image */ + memwrite(ctlr, FwBase, ctlr->fw->data, ctlr->fw->size); + /* kick microcontroller unit */ + csr32w(ctlr, SysCtrl, 0); + coherence(); + csr32w(ctlr, SysCtrl, McuReset); + + csr32w(ctlr, H2mBbpagent, 0); + csr32w(ctlr, H2mMailbox, 0); + + /* wait until microcontroller is ready */ + coherence(); + for(ntries = 0; ntries < 1000; ntries++){ + if(csr32r(ctlr, SysCtrl) & McuReady) + break; + microdelay(1000); + } + if(ntries == 1000) + return "timeout waiting for MCU to initialize"; + return 0; +} + +/* + * Send a command to the 8051 microcontroller unit. + */ +static int +mcucmd(Ctlr *ctlr, u8int cmd, u16int arg, int wait) +{ + int slot, ntries; + u32int tmp; + u8int cid; + + SET(slot); + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, H2mMailbox) & H2mBusy)) + break; + microdelay(2); + } + if(ntries == 100) + return -1; + + cid = wait ? cmd : TokenNoIntr; + csr32w(ctlr, H2mMailbox, H2mBusy | cid << 16 | arg); + coherence(); + csr32w(ctlr, HostCmd, cmd); + + if(!wait) + return 0; + /* wait for the command to complete */ + for(ntries = 0; ntries < 200; ntries++){ + tmp = csr32r(ctlr, H2mMailboxCid); + /* find the command slot */ + for(slot = 0; slot < 4; slot++, tmp >>= 8) + if((tmp & 0xff) == cid) + break; + if(slot < 4) + break; + microdelay(100); + } + if(ntries == 200){ + /* clear command and status */ + csr32w(ctlr, H2mMailboxStatus, 0xffffffff); + csr32w(ctlr, H2mMailboxCid, 0xffffffff); + return -1; + } + /* get command status (1 means success) */ + tmp = csr32r(ctlr, H2mMailboxStatus); + tmp = (tmp >> (slot * 8)) & 0xff; + /* clear command and status */ + csr32w(ctlr, H2mMailboxStatus, 0xffffffff); + csr32w(ctlr, H2mMailboxCid, 0xffffffff); + return (tmp == 1) ? 0 : -1; +} + + +/* + * Reading and writing from/to the BBP is different from RT2560 and RT2661. + * We access the BBP through the 8051 microcontroller unit which means that + * the microcode must be loaded first. + */ +static void +bbpwrite(Ctlr *ctlr, u8int reg, u8int val) +{ + int ntries; + + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, H2mBbpagent) & BbpCsrKick)) + break; + microdelay(1); + } + if(ntries == 100){ + print("could not write to BBP through MCU\n"); + return; + } + + csr32w(ctlr, H2mBbpagent, BbpRwParallel | + BbpCsrKick | reg << 8 | val); + coherence(); + + mcucmd(ctlr, McuCmdBbp, 0, 0); + microdelay(1000); +} + +static u8int +bbpread(Ctlr *ctlr, u8int reg) +{ + u32int val; + int ntries; + + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, H2mBbpagent) & BbpCsrKick)) + break; + microdelay(1); + } + if(ntries == 100){ + print("could not read from BBP through MCU"); + return 0; + } + + csr32w(ctlr, H2mBbpagent, BbpRwParallel | + BbpCsrKick | BbpCsrRead | reg << 8); + coherence(); + + mcucmd(ctlr, McuCmdBbp, 0, 0); + microdelay(1000); + + for(ntries = 0; ntries < 100; ntries++){ + val = csr32r(ctlr, H2mBbpagent); + if(!(val & BbpCsrKick)) + return val & 0xff; + microdelay(1); + } + print("could not read from BBP through MCU\n"); + + return 0; +} + +static char* +bbpinit(Ctlr *ctlr) +{ + int i, ntries; + char *err; + + /* wait for BBP to wake up */ + for(ntries = 0; ntries < 20; ntries++){ + u8int bbp0 = bbpread(ctlr, 0); + if(bbp0 != 0 && bbp0 != 0xff) + break; + } + if(ntries == 20){ + err = "timeout waiting for BBP to wake up"; + return err; + } + + /* initialize BBP registers to default values */ + for(i = 0; i < nelem(rt2860_def_bbp); i++){ + bbpwrite(ctlr, rt2860_def_bbp[i].reg, + rt2860_def_bbp[i].val); + } + + /* fix BBP84 for RT2860E */ + if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev != 0x0101) + bbpwrite(ctlr, 84, 0x19); + + if(ctlr->mac_ver >= 0x3071){ + bbpwrite(ctlr, 79, 0x13); + bbpwrite(ctlr, 80, 0x05); + bbpwrite(ctlr, 81, 0x33); + }else if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev == 0x0100){ + bbpwrite(ctlr, 69, 0x16); + bbpwrite(ctlr, 73, 0x12); + } + + return nil; + +} + +static void +setleds(Ctlr *ctlr, u16int which) +{ + mcucmd(ctlr, McuCmdLeds, + which | (ctlr->leds & 0x7f), 0); +} + +static char* +txrxon(Ctlr *ctlr) +{ + u32int tmp; + int ntries; + char *err; + + SET(tmp); + /* enable Tx/Rx DMA engine */ + csr32w(ctlr, MacSysCtrl, MacTxEn); + coherence(); + for(ntries = 0; ntries < 200; ntries++){ + tmp = csr32r(ctlr, WpdmaGloCfg); + if((tmp & (TxDmaBusy | RxDmaBusy)) == 0) + break; + microdelay(1000); + } + if(ntries == 200){ + err = "timeout waiting for DMA engine"; + return err; + } + + microdelay(50); + + tmp |= RxDmaEn | TxDmaEn | + WpdmaBtSize64 << WpdmaBtSizeShift; + csr32w(ctlr, WpdmaGloCfg, tmp); + + /* set Rx filter */ + tmp = DropCrcErr | DropPhyErr; + if(!ctlr->prom){ + tmp |= DropUcNome | DropDupl | + DropCts | DropBa | DropAck | + DropVerErr | DropCtrlRsv | + DropCfack | DropCfend; + tmp |= DropRts | DropPspoll; + } + csr32w(ctlr, RxFiltrCfg, tmp); + + csr32w(ctlr, MacSysCtrl, MacRxEn | MacTxEn); + + return 0; +} + +/* + * Write to one of the 4 programmable 24-bit RF registers. + */ +static void +rfwrite(Ctlr *ctlr, u8int reg, u32int val) +{ + u32int tmp; + int ntries; + + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, RfCsrCfg0) & RfRegCtrl)) + break; + microdelay(1); + } + if(ntries == 100){ + print("could not write to RF\n"); + return; + } + + /* RF registers are 24-bit on the RT2860 */ + tmp = RfRegCtrl | 24 << RfRegWidthShift | + (val & 0x3fffff) << 2 | (reg & 3); + csr32w(ctlr, RfCsrCfg0, tmp); +} + +u8int +rt3090rfread(Ctlr *ctlr, u8int reg) +{ + u32int tmp; + int ntries; + + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, Rt3070RfCsrCfg) & Rt3070RfKick)) + break; + microdelay(1); + } + if(ntries == 100){ + print("could not read RF register\n"); + return 0xff; + } + tmp = Rt3070RfKick | reg << 8; + csr32w(ctlr, Rt3070RfCsrCfg, tmp); + + for(ntries = 0; ntries < 100; ntries++){ + tmp = csr32r(ctlr, Rt3070RfCsrCfg); + if(!(tmp & Rt3070RfKick)) + break; + microdelay(1); + } + if(ntries == 100){ + print("could not read RF register\n"); + return 0xff; + } + return tmp & 0xff; +} + +static void +rt3090rfwrite(Ctlr *ctlr, u8int reg, u8int val) +{ + u32int tmp; + int ntries; + + for(ntries = 0; ntries < 10; ntries++){ + if(!(csr32r(ctlr, Rt3070RfCsrCfg) & Rt3070RfKick)) + break; + microdelay(10); + } + if(ntries == 10){ + print("could not write to RF\n"); + return; + } + + tmp = Rt3070RfWrite | Rt3070RfKick | reg << 8 | val; + csr32w(ctlr, Rt3070RfCsrCfg, tmp); +} + +static void +selchangroup(Ctlr *ctlr, int group) +{ + u32int tmp; + u8int agc; + + bbpwrite(ctlr, 62, 0x37 - ctlr->lna[group]); + bbpwrite(ctlr, 63, 0x37 - ctlr->lna[group]); + bbpwrite(ctlr, 64, 0x37 - ctlr->lna[group]); + bbpwrite(ctlr, 86, 0x00); + + if(group == 0){ + if(ctlr->ext_2ghz_lna){ + bbpwrite(ctlr, 82, 0x62); + bbpwrite(ctlr, 75, 0x46); + }else{ + bbpwrite(ctlr, 82, 0x84); + bbpwrite(ctlr, 75, 0x50); + } + }else{ + if(ctlr->ext_5ghz_lna){ + bbpwrite(ctlr, 82, 0xf2); + bbpwrite(ctlr, 75, 0x46); + }else{ + bbpwrite(ctlr, 82, 0xf2); + bbpwrite(ctlr, 75, 0x50); + } + } + + tmp = csr32r(ctlr, TxBandCfg); + tmp &= ~(Tx5gBandSelN | Tx5gBandSelP); + tmp |= (group == 0) ? Tx5gBandSelN : Tx5gBandSelP; + csr32w(ctlr, TxBandCfg, tmp); + + /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ + tmp = RftrEn | TrswEn | LnaPe0En; + if(ctlr->nrxchains > 1) + tmp |= LnaPe1En; + if(ctlr->mac_ver == 0x3593 && ctlr->nrxchains > 2) + tmp |= Rt3593LnaPe2En; + if(group == 0){ /* 2GHz */ + tmp |= PaPeG0En; + if(ctlr->ntxchains > 1) + tmp |= PaPeG1En; + if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains > 2) + tmp |= Rt3593PaPeG2En; + }else{ /* 5GHz */ + tmp |= PaPeA0En; + if(ctlr->ntxchains > 1) + tmp |= PaPeA1En; + if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains > 2) + tmp |= Rt3593PaPeA2En; + } + csr32w(ctlr, TxPinCfg, tmp); + + if(ctlr->mac_ver == 0x3593){ + tmp = csr32r(ctlr, GpioCtrl); + if(ctlr->flags & ConnPciE){ + tmp &= ~0x01010000; + if(group == 0) + tmp |= 0x00010000; + }else{ + tmp &= ~0x00008080; + if(group == 0) + tmp |= 0x00000080; + } + tmp = (tmp & ~0x00001000) | 0x00000010; + csr32w(ctlr, GpioCtrl, tmp); + } + + /* set initial AGC value */ + if(group == 0){ /* 2GHz band */ + if(ctlr->mac_ver >= 0x3071) + agc = 0x1c + ctlr->lna[0] * 2; + else + agc = 0x2e + ctlr->lna[0]; + }else{ /* 5GHz band */ + agc = 0x32 + (ctlr->lna[group] * 5) / 3; + } + bbpwrite(ctlr, 66, agc); + + microdelay(1000); + +} + +static void +setchan(Ctlr *ctlr, uint chan) +{ + const struct rfprog *rfprog = rt2860_rf2850; + u32int r2, r3, r4; + s8int txpow1, txpow2; + uint i; + + /* find the settings for this channel (we know it exists) */ + for(i = 0; rfprog[i].chan != chan; i++); + + r2 = rfprog[i].r2; + if(ctlr->ntxchains == 1) + r2 |= 1 << 12; /* 1T: disable Tx chain 2 */ + if(ctlr->nrxchains == 1) + r2 |= 1 << 15 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ + else if(ctlr->nrxchains == 2) + r2 |= 1 << 4; /* 2R: disable Rx chain 3 */ + + /* use Tx power values from EEPROM */ + txpow1 = ctlr->txpow1[i]; + txpow2 = ctlr->txpow2[i]; + if(chan > 14){ + if(txpow1 >= 0) + txpow1 = txpow1 << 1 | 1; + else + txpow1 = (7 + txpow1) << 1; + if(txpow2 >= 0) + txpow2 = txpow2 << 1 | 1; + else + txpow2 = (7 + txpow2) << 1; + } + r3 = rfprog[i].r3 | txpow1 << 7; + r4 = rfprog[i].r4 | ctlr->freq << 13 | txpow2 << 4; + + rfwrite(ctlr, Rf1, rfprog[i].r1); + rfwrite(ctlr, Rf2, r2); + rfwrite(ctlr, Rf3, r3); + rfwrite(ctlr, Rf4, r4); + + microdelay(200); + + rfwrite(ctlr, Rf1, rfprog[i].r1); + rfwrite(ctlr, Rf2, r2); + rfwrite(ctlr, Rf3, r3 | 1); + rfwrite(ctlr, Rf4, r4); + + microdelay(200); + + rfwrite(ctlr, Rf1, rfprog[i].r1); + rfwrite(ctlr, Rf2, r2); + rfwrite(ctlr, Rf3, r3); + rfwrite(ctlr, Rf4, r4); +} + +static void +rt3090setchan(Ctlr *ctlr, uint chan) +{ + s8int txpow1, txpow2; + u8int rf; + int i; + + assert(chan >= 1 && chan <= 14); /* RT3090 is 2GHz only */ + + /* find the settings for this channel (we know it exists) */ + for(i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = ctlr->txpow1[i]; + txpow2 = ctlr->txpow2[i]; + + rt3090rfwrite(ctlr, 2, rt3090_freqs[i].n); + rf = rt3090rfread(ctlr, 3); + rf = (rf & ~0x0f) | rt3090_freqs[i].k; + rt3090rfwrite(ctlr, 3, rf); + rf = rt3090rfread(ctlr, 6); + rf = (rf & ~0x03) | rt3090_freqs[i].r; + rt3090rfwrite(ctlr, 6, rf); + + /* set Tx0 power */ + rf = rt3090rfread(ctlr, 12); + rf = (rf & ~0x1f) | txpow1; + rt3090rfwrite(ctlr, 12, rf); + + /* set Tx1 power */ + rf = rt3090rfread(ctlr, 13); + rf = (rf & ~0x1f) | txpow2; + rt3090rfwrite(ctlr, 13, rf); + + rf = rt3090rfread(ctlr, 1); + rf &= ~0xfc; + if(ctlr->ntxchains == 1) + rf |= Rt3070Tx1Pd | Rt3070Tx2Pd; + else if(ctlr->ntxchains == 2) + rf |= Rt3070Tx2Pd; + if(ctlr->nrxchains == 1) + rf |= Rt3070Rx1Pd | Rt3070Rx2Pd; + else if(ctlr->nrxchains == 2) + rf |= Rt3070Rx2Pd; + rt3090rfwrite(ctlr, 1, rf); + + /* set RF offset */ + rf = rt3090rfread(ctlr, 23); + rf = (rf & ~0x7f) | ctlr->freq; + rt3090rfwrite(ctlr, 23, rf); + + /* program RF filter */ + rf = rt3090rfread(ctlr, 24); /* Tx */ + rf = (rf & ~0x3f) | ctlr->rf24_20mhz; + rt3090rfwrite(ctlr, 24, rf); + rf = rt3090rfread(ctlr, 31); /* Rx */ + rf = (rf & ~0x3f) | ctlr->rf24_20mhz; + rt3090rfwrite(ctlr, 31, rf); + + /* enable RF tuning */ + rf = rt3090rfread(ctlr, 7); + rt3090rfwrite(ctlr, 7, rf | Rt3070Tune); +} + +static int +rt3090filtercalib(Ctlr *ctlr, u8int init, u8int target, u8int *val) +{ + u8int rf22, rf24; + u8int bbp55_pb, bbp55_sb, delta; + int ntries; + + /* program filter */ + rf24 = rt3090rfread(ctlr, 24); + rf24 = (rf24 & 0xc0) | init; /* initial filter value */ + rt3090rfwrite(ctlr, 24, rf24); + + /* enable baseband loopback mode */ + rf22 = rt3090rfread(ctlr, 22); + rt3090rfwrite(ctlr, 22, rf22 | Rt3070BbLoopback); + + /* set power and frequency of passband test tone */ + bbpwrite(ctlr, 24, 0x00); + for(ntries = 0, bbp55_pb = 0; ntries < 100; ntries++){ + /* transmit test tone */ + bbpwrite(ctlr, 25, 0x90); + microdelay(1000); + /* read received power */ + bbp55_pb = bbpread(ctlr, 55); + if(bbp55_pb != 0) + break; + } + if(ntries == 100) + return -1; + + /* set power and frequency of stopband test tone */ + bbpwrite(ctlr, 24, 0x06); + for(ntries = 0; ntries < 100; ntries++){ + /* transmit test tone */ + bbpwrite(ctlr, 25, 0x90); + microdelay(1000); + /* read received power */ + bbp55_sb = bbpread(ctlr, 55); + + delta = bbp55_pb - bbp55_sb; + if(delta > target) + break; + + /* reprogram filter */ + rf24++; + rt3090rfwrite(ctlr, 24, rf24); + } + if(ntries < 100){ + if(rf24 != init) + rf24--; /* backtrack */ + *val = rf24; + rt3090rfwrite(ctlr, 24, rf24); + } + + /* restore initial state */ + bbpwrite(ctlr, 24, 0x00); + + /* disable baseband loopback mode */ + rf22 = rt3090rfread(ctlr, 22); + rt3090rfwrite(ctlr, 22, rf22 & ~Rt3070BbLoopback); + + return 0; +} + +static void +rt3090setrxantenna(Ctlr *ctlr, int aux) +{ + u32int tmp; + + if(aux){ + tmp = csr32r(ctlr, PciEectrl); + csr32w(ctlr, PciEectrl, tmp & ~EectrlC); + tmp = csr32r(ctlr, GpioCtrl); + csr32w(ctlr, GpioCtrl, (tmp & ~0x0808) | 0x08); + }else{ + tmp = csr32r(ctlr, PciEectrl); + csr32w(ctlr, PciEectrl, tmp | EectrlC); + tmp = csr32r(ctlr, GpioCtrl); + csr32w(ctlr, GpioCtrl, tmp & ~0x0808); + } +} + +static int +rt3090rfinit(Ctlr *ctlr) +{ + u32int tmp; + u8int rf, bbp; + int i; + + rf = rt3090rfread(ctlr, 30); + /* toggle RF R30 bit 7 */ + rt3090rfwrite(ctlr, 30, rf | 0x80); + microdelay(1000); + rt3090rfwrite(ctlr, 30, rf & ~0x80); + + tmp = csr32r(ctlr, Rt3070LdoCfg0); + tmp &= ~0x1f000000; + if(ctlr->patch_dac && ctlr->mac_rev < 0x0211) + tmp |= 0x0d000000; /* 1.35V */ + else + tmp |= 0x01000000; /* 1.2V */ + csr32w(ctlr, Rt3070LdoCfg0, tmp); + + /* patch LNA_PE_G1 */ + tmp = csr32r(ctlr, Rt3070GpioSwitch); + csr32w(ctlr, Rt3070GpioSwitch, tmp & ~0x20); + + /* initialize RF registers to default value */ + for(i = 0; i < nelem(rt3090_def_rf); i++){ + rt3090rfwrite(ctlr, rt3090_def_rf[i].reg, + rt3090_def_rf[i].val); + } + + /* select 20MHz bandwidth */ + rt3090rfwrite(ctlr, 31, 0x14); + + rf = rt3090rfread(ctlr, 6); + rt3090rfwrite(ctlr, 6, rf | 0x40); + + if(ctlr->mac_ver != 0x3593){ + /* calibrate filter for 20MHz bandwidth */ + ctlr->rf24_20mhz = 0x1f; /* default value */ + rt3090filtercalib(ctlr, 0x07, 0x16, &ctlr->rf24_20mhz); + + /* select 40MHz bandwidth */ + bbp = bbpread(ctlr, 4); + bbpwrite(ctlr, 4, (bbp & ~0x08) | 0x10); + rf = rt3090rfread(ctlr, 31); + rt3090rfwrite(ctlr, 31, rf | 0x20); + + /* calibrate filter for 40MHz bandwidth */ + ctlr->rf24_40mhz = 0x2f; /* default value */ + rt3090filtercalib(ctlr, 0x27, 0x19, &ctlr->rf24_40mhz); + + /* go back to 20MHz bandwidth */ + bbp = bbpread(ctlr, 4); + bbpwrite(ctlr, 4, bbp & ~0x18); + } + if(ctlr->mac_rev < 0x0211) + rt3090rfwrite(ctlr, 27, 0x03); + + tmp = csr32r(ctlr, Rt3070Opt14); + csr32w(ctlr, Rt3070Opt14, tmp | 1); + + if(ctlr->rf_rev == Rf3020) + rt3090setrxantenna(ctlr, 0); + + bbp = bbpread(ctlr, 138); + if(ctlr->mac_ver == 0x3593){ + if(ctlr->ntxchains == 1) + bbp |= 0x60; /* turn off DAC1 and DAC2 */ + else if(ctlr->ntxchains == 2) + bbp |= 0x40; /* turn off DAC2 */ + if(ctlr->nrxchains == 1) + bbp &= ~0x06; /* turn off ADC1 and ADC2 */ + else if(ctlr->nrxchains == 2) + bbp &= ~0x04; /* turn off ADC2 */ + }else{ + if(ctlr->ntxchains == 1) + bbp |= 0x20; /* turn off DAC1 */ + if(ctlr->nrxchains == 1) + bbp &= ~0x02; /* turn off ADC1 */ + } + bbpwrite(ctlr, 138, bbp); + + rf = rt3090rfread(ctlr, 1); + rf &= ~(Rt3070Rx0Pd | Rt3070Tx0Pd); + rf |= Rt3070RfBlock | Rt3070Rx1Pd | Rt3070Tx1Pd; + rt3090rfwrite(ctlr, 1, rf); + + rf = rt3090rfread(ctlr, 15); + rt3090rfwrite(ctlr, 15, rf & ~Rt3070TxLo2); + + rf = rt3090rfread(ctlr, 17); + rf &= ~Rt3070TxLo1; + if(ctlr->mac_rev >= 0x0211 && !ctlr->ext_2ghz_lna) + rf |= 0x20; /* fix for long range Rx issue */ + if(ctlr->txmixgain_2ghz >= 2) + rf = (rf & ~0x7) | ctlr->txmixgain_2ghz; + rt3090rfwrite(ctlr, 17, rf); + + rf = rt3090rfread(ctlr, 20); + rt3090rfwrite(ctlr, 20, rf & ~Rt3070RxLo1); + + rf = rt3090rfread(ctlr, 21); + rt3090rfwrite(ctlr, 21, rf & ~Rt3070RxLo2); + + return 0; +} + +static void +rt3090rfwakeup(Ctlr *ctlr) +{ + u32int tmp; + u8int rf; + + if(ctlr->mac_ver == 0x3593){ + /* enable VCO */ + rf = rt3090rfread(ctlr, 1); + rt3090rfwrite(ctlr, 1, rf | Rt3593Vco); + + /* initiate VCO calibration */ + rf = rt3090rfread(ctlr, 3); + rt3090rfwrite(ctlr, 3, rf | Rt3593Vcocal); + + /* enable VCO bias current control */ + rf = rt3090rfread(ctlr, 6); + rt3090rfwrite(ctlr, 6, rf | Rt3593VcoIc); + + /* initiate res calibration */ + rf = rt3090rfread(ctlr, 2); + rt3090rfwrite(ctlr, 2, rf | Rt3593Rescal); + + /* set reference current control to 0.33 mA */ + rf = rt3090rfread(ctlr, 22); + rf &= ~Rt3593CpIcMask; + rf |= 1 << Rt3593CpIcShift; + rt3090rfwrite(ctlr, 22, rf); + + /* enable RX CTB */ + rf = rt3090rfread(ctlr, 46); + rt3090rfwrite(ctlr, 46, rf | Rt3593RxCtb); + + rf = rt3090rfread(ctlr, 20); + rf &= ~(Rt3593LdoRfVcMask | Rt3593LdoPllVcMask); + rt3090rfwrite(ctlr, 20, rf); + }else{ + /* enable RF block */ + rf = rt3090rfread(ctlr, 1); + rt3090rfwrite(ctlr, 1, rf | Rt3070RfBlock); + + /* enable VCO bias current control */ + rf = rt3090rfread(ctlr, 7); + rt3090rfwrite(ctlr, 7, rf | 0x30); + + rf = rt3090rfread(ctlr, 9); + rt3090rfwrite(ctlr, 9, rf | 0x0e); + + /* enable RX CTB */ + rf = rt3090rfread(ctlr, 21); + rt3090rfwrite(ctlr, 21, rf | Rt3070RxCtb); + + /* fix Tx to Rx IQ glitch by raising RF voltage */ + rf = rt3090rfread(ctlr, 27); + rf &= ~0x77; + if(ctlr->mac_rev < 0x0211) + rf |= 0x03; + rt3090rfwrite(ctlr, 27, rf); + } + if(ctlr->patch_dac && ctlr->mac_rev < 0x0211){ + tmp = csr32r(ctlr, Rt3070LdoCfg0); + tmp = (tmp & ~0x1f000000) | 0x0d000000; + csr32w(ctlr, Rt3070LdoCfg0, tmp); + } +} + +static void +rt3090rfsetup(Ctlr *ctlr) +{ + u8int bbp; + int i; + + if(ctlr->mac_rev >= 0x0211){ + /* enable DC filter */ + bbpwrite(ctlr, 103, 0xc0); + + /* improve power consumption */ + bbp = bbpread(ctlr, 31); + bbpwrite(ctlr, 31, bbp & ~0x03); + } + + csr32w(ctlr, TxSwCfg1, 0); + if(ctlr->mac_rev < 0x0211){ + csr32w(ctlr, TxSwCfg2, + ctlr->patch_dac ? 0x2c : 0x0f); + }else + csr32w(ctlr, TxSwCfg2, 0); + + /* initialize RF registers from ROM */ + for(i = 0; i < 10; i++){ + if(ctlr->rf[i].reg == 0 || ctlr->rf[i].reg == 0xff) + continue; + rt3090rfwrite(ctlr, ctlr->rf[i].reg, ctlr->rf[i].val); + } +} + +static void +updateprot(Ctlr *ctlr) +{ + u32int tmp; + + tmp = RtsthEn | ProtNavShort | TxopAllowAll; + /* setup protection frame rate (MCS code) */ + tmp |= /*(ic->ic_curmode == IEEE80211_MODE_11A) ? + rt2860_rates[RT2860_RIDX_OFDM6].mcs :*/ + rt2860_rates[RidxCck11].mcs; + + /* CCK frames don't require protection */ + csr32w(ctlr, CckProtCfg, tmp); +/* XXX + if(ic->ic_flags & IEEE80211_F_USEPROT){ + if(ic->ic_protmode == IEEE80211_PROT_RTSCTS) + tmp |= ProtCtrlRtsCts; + else if(ic->ic_protmode == IEEE80211_PROT_CTSONLY) + tmp |= ProtCtrlCts; + } + csr32w(ctlr, OfdmProtCfg, tmp); */ +} + +static char* +rt2860start(Ether *edev) +{ + u32int tmp; + u8int bbp1, bbp3; + int i, qid, ridx, ntries; + char *err; + Ctlr *ctlr; + + ctlr = edev->ctlr; + csr32w(ctlr, PwrPinCfg, IoRaPe); + + /* disable DMA */ + tmp = csr32r(ctlr, WpdmaGloCfg); + tmp &= 0xff0; + csr32w(ctlr, WpdmaGloCfg, tmp); + + /* PBF hardware reset */ + csr32w(ctlr, SysCtrl, 0xe1f); + coherence(); + csr32w(ctlr, SysCtrl, 0xe00); + + if((err = boot(ctlr)) != nil){ + /*XXX: rt2860stop(ifp, 1);*/ + return err; + } + /* set MAC address */ + csr32w(ctlr, MacAddrDw0, + edev->ea[0] | edev->ea[1] << 8 | edev->ea[2] << 16 | edev->ea[3] << 24); + csr32w(ctlr, MacAddrDw1, + edev->ea[4] | edev->ea[5] << 8 | 0xff << 16); + + /* init Tx power for all Tx rates (from EEPROM) */ + for(ridx = 0; ridx < 5; ridx++){ + if(ctlr->txpow20mhz[ridx] == 0xffffffff) + continue; + csr32w(ctlr, TxPwrCfg(ridx), ctlr->txpow20mhz[ridx]); + } + + for(ntries = 0; ntries < 100; ntries++){ + tmp = csr32r(ctlr, WpdmaGloCfg); + if((tmp & (TxDmaBusy | RxDmaBusy)) == 0) + break; + microdelay(1000); + } + if(ntries == 100){ + err = "timeout waiting for DMA engine"; + /*rt2860_stop(ifp, 1);*/ + return err; + } + tmp &= 0xff0; + csr32w(ctlr, WpdmaGloCfg, tmp); + + /* reset Rx ring and all 6 Tx rings */ + csr32w(ctlr, WpdmaRstIdx, 0x1003f); + + /* PBF hardware reset */ + csr32w(ctlr, SysCtrl, 0xe1f); + coherence(); + csr32w(ctlr, SysCtrl, 0xe00); + + csr32w(ctlr, PwrPinCfg, IoRaPe | IoRfPe); + + csr32w(ctlr, MacSysCtrl, BbpHrst | MacSrst); + coherence(); + csr32w(ctlr, MacSysCtrl, 0); + + for(i = 0; i < nelem(rt2860_def_mac); i++) + csr32w(ctlr, rt2860_def_mac[i].reg, rt2860_def_mac[i].val); + if(ctlr->mac_ver >= 0x3071){ + /* set delay of PA_PE assertion to 1us (unit of 0.25us) */ + csr32w(ctlr, TxSwCfg0, + 4 << DlyPapeEnShift); + } + + if(!(csr32r(ctlr, PciCfg) & PciCfgPci)){ + ctlr->flags |= ConnPciE; + /* PCIe has different clock cycle count than PCI */ + tmp = csr32r(ctlr, UsCycCnt); + tmp = (tmp & ~0xff) | 0x7d; + csr32w(ctlr, UsCycCnt, tmp); + } + + /* wait while MAC is busy */ + for(ntries = 0; ntries < 100; ntries++){ + if(!(csr32r(ctlr, MacStatusReg) & + (RxStatusBusy | TxStatusBusy))) + break; + microdelay(1000); + } + if(ntries == 100){ + err = "timeout waiting for MAC"; + /*rt2860_stop(ifp, 1);*/ + return err; + } + + /* clear Host to MCU mailbox */ + csr32w(ctlr, H2mBbpagent, 0); + csr32w(ctlr, H2mMailbox, 0); + + mcucmd(ctlr, McuCmdRfreset, 0, 0); + microdelay(1000); + + if((err = bbpinit(ctlr)) != nil){ + /*rt2860_stop(ifp, 1);*/ + return err; + } + /* clear RX WCID search table */ + setregion(ctlr, WcidEntry(0), 0, 512); + /* clear pairwise key table */ + setregion(ctlr, Pkey(0), 0, 2048); + /* clear IV/EIV table */ + setregion(ctlr, Iveiv(0), 0, 512); + /* clear WCID attribute table */ + setregion(ctlr, WcidAttr(0), 0, 256); + /* clear shared key table */ + setregion(ctlr, Skey(0, 0), 0, 8 * 32); + /* clear shared key mode */ + setregion(ctlr, SkeyMode07, 0, 4); + + /* init Tx rings (4 EDCAs + HCCA + Mgt) */ + for(qid = 0; qid < 6; qid++){ + csr32w(ctlr, TxBasePtr(qid), PCIWADDR(ctlr->tx[qid].d)); + csr32w(ctlr, TxMaxCnt(qid), Ntx); + csr32w(ctlr, TxCtxIdx(qid), 0); + } + + /* init Rx ring */ + csr32w(ctlr, RxBasePtr, PCIWADDR(ctlr->rx.p)); + csr32w(ctlr, RxMaxCnt, Nrx); + csr32w(ctlr, RxCalcIdx, Nrx - 1); + + /* setup maximum buffer sizes */ + csr32w(ctlr, MaxLenCfg, 1 << 12 | + (Rbufsize - Rxwisize - 2)); + + for(ntries = 0; ntries < 100; ntries++){ + tmp = csr32r(ctlr, WpdmaGloCfg); + if((tmp & (TxDmaBusy | RxDmaBusy)) == 0) + break; + microdelay(1000); + } + if(ntries == 100){ + err = "timeout waiting for DMA engine"; + /*rt2860_stop(ifp, 1);*/ + return err; + } + tmp &= 0xff0; + csr32w(ctlr, WpdmaGloCfg, tmp); + + /* disable interrupts mitigation */ + csr32w(ctlr, DelayIntCfg, 0); + + /* write vendor-specific BBP values (from EEPROM) */ + for(i = 0; i < 8; i++){ + if(ctlr->bbp[i].reg == 0 || ctlr->bbp[i].reg == 0xff) + continue; + bbpwrite(ctlr, ctlr->bbp[i].reg, ctlr->bbp[i].val); + } + + /* select Main antenna for 1T1R devices */ + if(ctlr->rf_rev == Rf2020 || + ctlr->rf_rev == Rf3020 || + ctlr->rf_rev == Rf3320) + rt3090setrxantenna(ctlr, 0); + + /* send LEDs operating mode to microcontroller */ + mcucmd(ctlr, McuCmdLed1, ctlr->led[0], 0); + mcucmd(ctlr, McuCmdLed2, ctlr->led[1], 0); + mcucmd(ctlr, McuCmdLed3, ctlr->led[2], 0); + + if(ctlr->mac_ver >= 0x3071) + rt3090rfinit(ctlr); + + mcucmd(ctlr, McuCmdSleep, 0x02ff, 1); + mcucmd(ctlr, McuCmdWakeup, 0, 1); + + if(ctlr->mac_ver >= 0x3071) + rt3090rfwakeup(ctlr); + + /* disable non-existing Rx chains */ + bbp3 = bbpread(ctlr, 3); + bbp3 &= ~(1 << 3 | 1 << 4); + if(ctlr->nrxchains == 2) + bbp3 |= 1 << 3; + else if(ctlr->nrxchains == 3) + bbp3 |= 1 << 4; + bbpwrite(ctlr, 3, bbp3); + + /* disable non-existing Tx chains */ + bbp1 = bbpread(ctlr, 1); + if(ctlr->ntxchains == 1) + bbp1 = (bbp1 & ~(1 << 3 | 1 << 4)); + else if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains == 2) + bbp1 = (bbp1 & ~(1 << 4)) | 1 << 3; + else if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains == 3) + bbp1 = (bbp1 & ~(1 << 3)) | 1 << 4; + bbpwrite(ctlr, 1, bbp1); + + if(ctlr->mac_ver >= 0x3071) + rt3090rfsetup(ctlr); + + /* select default channel */ + if(ctlr->mac_ver >= 0x3071) + rt3090setchan(ctlr, 3); + else + setchan(ctlr, 3); + + /* reset RF from MCU */ + mcucmd(ctlr, McuCmdRfreset, 0, 0); + + /* set RTS threshold */ + tmp = csr32r(ctlr, TxRtsCfg); + tmp &= ~0xffff00; + tmp |= 1 /* ic->ic_rtsthreshold */ << 8; + csr32w(ctlr, TxRtsCfg, tmp); + + /* setup initial protection mode */ + updateprot(ctlr); + + /* turn radio LED on */ + setleds(ctlr, LedRadio); + + /* enable Tx/Rx DMA engine */ + if((err = txrxon(ctlr)) != 0){ + /*rt2860_stop(ifp, 1);*/ + return err; + } + + /* clear pending interrupts */ + csr32w(ctlr, IntStatus, 0xffffffff); + /* enable interrupts */ + csr32w(ctlr, IntMask, 0x3fffc); + + if(ctlr->flags & AdvancedPs) + mcucmd(ctlr, McuCmdPslevel, ctlr->pslevel, 0); + return nil; +} + +/* + * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. + * Used to adjust per-rate Tx power registers. + */ +static u32int +b4inc(u32int b32, s8int delta) +{ + s8int i, b4; + + for(i = 0; i < 8; i++){ + b4 = b32 & 0xf; + b4 += delta; + if(b4 < 0) + b4 = 0; + else if(b4 > 0xf) + b4 = 0xf; + b32 = b32 >> 4 | b4 << 28; + } + return b32; +} + +static void +transmit(Wifi *wifi, Wnode *wn, Block *b) +{ + Ether *edev; + Ctlr *ctlr; + Wifipkt *w; + u8int mcs, qid; + int ridx, /*ctl_ridx,*/ hdrlen; + uchar *p; + int nodeid; + Block *outb; + TXQ *tx; + Pool *pool; + + edev = wifi->ether; + ctlr = edev->ctlr; + + qlock(ctlr); + if(ctlr->attached == 0 || ctlr->broken){ + qunlock(ctlr); + freeb(b); + return; + } + if((wn->channel != ctlr->channel) + || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0))) + rxon(edev, wn); + + if(b == nil){ + /* association note has no data to transmit */ + qunlock(ctlr); + return; + } + + pool = &ctlr->pool; + qid = 0; /* for now */ + ridx = 0; + tx = &ctlr->tx[qid]; + + nodeid = ctlr->bcastnodeid; + w = (Wifipkt*)b->rp; + hdrlen = wifihdrlen(w); + + p = pool->p + pool->i * TxwiDmaSz; + if((w->a1[0] & 1) == 0){ + *(p+4) = TxAck; /* xflags */ + + if(BLEN(b) > 512-4) + *(p+1) = TxTxopBackoff; /* txop */ + + if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){ + nodeid = ctlr->bssnodeid; + ridx = 2; /* BUG: hardcode 11Mbit */ + } + } + + /*ctl_ridx = rt2860_rates[ridx].ctl_ridx;*/ + mcs = rt2860_rates[ridx].mcs; + + /* setup TX Wireless Information */ + *p = 0; /* flags */ + *(p+2) = PhyCck | mcs; /* phy */ + /* let HW generate seq numbers */ + *(p+4) |= TxNseq; /* xflags */ + put16(p + 6, BLEN(b) | (((mcs+1) & 0xf) << TxPidShift) ); /* length */ + +/* put16((uchar*)&w->dur[0], rt2860_rates[ctl_ridx].lp_ack_dur); */ + + *(p+5) = nodeid; /* wcid */ + + /* copy packet header */ + memmove(p + Txwisize, b->rp, hdrlen); + + /* setup tx descriptor */ + /* first segment is TXWI + 802.11 header */ + p = (uchar*)tx->d + Tdscsize * tx->i; + put32(p, PCIWADDR(pool->p + pool->i * TxwiDmaSz)); /* sdp0 */ + put16(p + 6, Txwisize + hdrlen); /* sdl0 */ + *(p + 15) = TxQselEdca; /* flags */ + + /* allocate output buffer */ + b->rp += hdrlen; + tx->b[tx->i] = outb = iallocb(BLEN(b) + 256); + if(outb == nil){ + print("outb = nil\n"); + return; + } + outb->rp = (uchar*)ROUND((uintptr)outb->base, 256); + memset(outb->rp, 0, BLEN(b)); + memmove(outb->rp, b->rp, BLEN(b)); + outb->wp = outb->rp + BLEN(b); + freeb(b); + + /* setup payload segments */ + put32(p + 8, PCIWADDR(outb->rp)); /* sdp1 */ + put16(p + 4, BLEN(outb) | TxLs1); /* sdl1 */ + + p = pool->p + pool->i * TxwiDmaSz; + w = (Wifipkt*)(p + Txwisize); + if(ctlr->wifi->debug){ + print("transmit: %E->%E,%E nodeid=%x txq[%d]=%d size=%ld\n", w->a2, w->a1, w->a3, nodeid, qid, ctlr->tx[qid].i, BLEN(outb)); + } + + tx->i = (tx->i + 1) % Ntx; + pool->i = (pool->i + 1) % Ntxpool; + + coherence(); + + /* kick Tx */ + csr32w(ctlr, TxCtxIdx(qid), ctlr->tx[qid].i); + + qunlock(ctlr); + return; +} + +static void +rt2860attach(Ether *edev) +{ + FWImage *fw; + Ctlr *ctlr; + char *err; + + ctlr = edev->ctlr; + qlock(ctlr); + if(waserror()){ + print("#l%d: %s\n", edev->ctlrno, up->errstr); + /*if(ctlr->power) + poweroff(ctlr);*/ + qunlock(ctlr); + nexterror(); + } + if(ctlr->attached == 0){ + if(ctlr->wifi == nil) + ctlr->wifi = wifiattach(edev, transmit); + + if(ctlr->fw == nil){ + fw = readfirmware(); + ctlr->fw = fw; + } + if((err = rt2860start(edev)) != nil){ + error(err); + } + + ctlr->bcastnodeid = -1; + ctlr->bssnodeid = -1; + ctlr->channel = 1; + ctlr->aid = 0; + + setoptions(edev); + + ctlr->attached = 1; + } + qunlock(ctlr); + poperror(); +} + +static void +receive(Ctlr *ctlr) +{ + u32int hw; + RXQ *rx; + Block *b; + uchar *d; + + rx = &ctlr->rx; + if(rx->b == nil){ + print("rx->b == nil!"); + return; + } + hw = csr32r(ctlr, FsDrxIdx) & 0xfff; + while(rx->i != hw){ + u16int sdl0, len; + u32int flags; + uchar *p; + Wifipkt *w; + int hdrlen; + + p = (uchar*)rx->p + Rdscsize * rx->i; + sdl0 = get16(p + 4 /* sdp0 */ + 2 /* sdl1 */); + if(!(sdl0 & RxDdone)){ + print("rxd ddone bit not set\n"); + break; /* should not happen */ + } + flags = get32(p + 12); + if(flags & (RxCrcerr | RxIcverr)){ + /* print("crc | icv err\n"); */ + goto skip; + } + + b = rx->b[rx->i]; + if(b == nil){ + print("no buf\n"); + goto skip; + } + d = b->rp; + if(ctlr->wifi == nil) + goto skip; + if(rbplant(ctlr, rx->i) < 0){ + print("can't plant"); + goto skip; + } + ctlr->wcid = *b->rp; + len = get16(b->rp + 2 /* wcid, keyidx */) & 0xfff; + b->rp = d + Rxwisize; + b->wp = b->rp + len; + w = (Wifipkt*)b->rp; + hdrlen = wifihdrlen(w); + /* HW may insert 2 padding bytes after 802.11 header */ + if(flags & RxL2pad){ + memmove(b->rp + 2, b->rp, hdrlen); + b->rp += 2; + } + w = (Wifipkt*)b->rp; + if(ctlr->wifi->debug) + print("receive: %E->%E,%E wcid 0x%x \n", w->a2, w->a1, w->a3, ctlr->wcid); + wifiiq(ctlr->wifi, b); +skip: + put16(p + 4 /* sdp0 */ + 2 /* sdl1 */, sdl0 & ~RxDdone); + rx->i = (rx->i + 1) % Nrx; + } + coherence(); + /* tell HW what we have processed */ + csr32w(ctlr, RxCalcIdx, (rx->i - 1) % Nrx); +} + +static void +stats(Ctlr *ctlr) +{ + u32int stat; + u8int wcid; + + while((stat = csr32r(ctlr, TxStatFifo)) & TxqVld){ + wcid = (stat >> TxqWcidShift) & 0xff; + /* if no ACK was requested, no feedback is available */ + if(!(stat & TxqAckreq) || wcid == 0xff){ + continue; + } + } +} + +static void +rt2860tx(Ctlr *ctlr, u8int q) +{ + u32int hw; + TXQ *tx; + + stats(ctlr); + tx = &ctlr->tx[q]; + hw = csr32r(ctlr, TxDtxIdx(q)); + while(tx->n != hw){ + uchar *p = (uchar*)tx->d + Rdscsize * tx->n; + u16int sdl0; + + if(tx->b[tx->n]){ + freeb(tx->b[tx->n]); + tx->b[tx->n] = nil; + } + sdl0 = get16(p + 4 /* sdp0 */ + 2 /* sdl1 */); + if(!(sdl0 & TxDdone)){ + print("txd ddone bit not set\n"); + break; /* should not happen */ + } + memset((uchar*)ctlr->pool.p + TxwiDmaSz * tx->n, 0, TxwiDmaSz); + memset((uchar*)tx->d + Tdscsize * tx->n, 0, Tdscsize); + // put16(p + 4 /* sdp0 */ + 2 /* sdl1 */, sdl0 & ~TxDdone); + tx->n = (tx->n + 1) % Ntx; + } + coherence(); +} + +static void +rt2860interrupt(Ureg*, void *arg) +{ + u32int r; + Ether *edev; + Ctlr *ctlr; + int debug; + + edev = arg; + ctlr = edev->ctlr; + ilock(ctlr); + + debug = ctlr->wifi->debug; + + r = csr32r(ctlr, IntStatus); + if(r == 0xffffffff){ + iunlock(ctlr); + return; + } + if(r == 0){ + iunlock(ctlr); + return; + } + + /* acknowledge interrupts */ + csr32w(ctlr, IntStatus, r); + + if(r & TxRxCoherent){ + u32int tmp; + /* DMA finds data coherent event when checking the DDONE bit */ + if(debug) + print("txrx coherent intr\n"); + + /* restart DMA engine */ + tmp = csr32r(ctlr, WpdmaGloCfg); + tmp &= ~(TxWbDdone | RxDmaEn | TxDmaEn); + csr32w(ctlr, WpdmaGloCfg, tmp); + + txrxon(ctlr); + } + if(r & MacInt2) + stats(ctlr); + + if(r & TxDoneInt5) + rt2860tx(ctlr, 5); + + if(r & RxDoneInt) + receive(ctlr); + + if(r & TxDoneInt4) + rt2860tx(ctlr, 4); + + if(r & TxDoneInt3) + rt2860tx(ctlr, 3); + + if(r & TxDoneInt2) + rt2860tx(ctlr, 2); + + if(r & TxDoneInt1) + rt2860tx(ctlr, 1); + + if(r & TxDoneInt0) + rt2860tx(ctlr, 0); + + iunlock(ctlr); +} + +static void +eepromctl(Ctlr *ctlr, u32int val) +{ + csr32w(ctlr, PciEectrl, val); + coherence(); + microdelay(EepromDelay); +} + +/* + * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46, + * 93C66 or 93C86). + */ +static u16int +eeread2(Ctlr *ctlr, u16int addr) +{ + u32int tmp; + u16int val; + int n; + + /* clock C once before the first command */ + eepromctl(ctlr, 0); + + eepromctl(ctlr, EectrlS); + eepromctl(ctlr, EectrlS | EectrlC); + eepromctl(ctlr, EectrlS); + + /* write start bit (1) */ + eepromctl(ctlr, EectrlS | EectrlD); + eepromctl(ctlr, EectrlS | EectrlD | EectrlC); + + /* write READ opcode (10) */ + eepromctl(ctlr, EectrlS | EectrlD); + eepromctl(ctlr, EectrlS | EectrlD | EectrlC); + eepromctl(ctlr, EectrlS); + eepromctl(ctlr, EectrlS | EectrlC); + + /* write address (A5-A0 or A7-A0) */ + n = ((csr32r(ctlr, PciEectrl) & 0x30) == 0) ? 5 : 7; + for(; n >= 0; n--){ + eepromctl(ctlr, EectrlS | + (((addr >> n) & 1) << EectrlShiftD)); + eepromctl(ctlr, EectrlS | + (((addr >> n) & 1) << EectrlShiftD) | EectrlC); + } + + eepromctl(ctlr, EectrlS); + + /* read data Q15-Q0 */ + val = 0; + for(n = 15; n >= 0; n--){ + eepromctl(ctlr, EectrlS | EectrlC); + tmp = csr32r(ctlr, PciEectrl); + val |= ((tmp & EectrlQ) >> EectrlShiftQ) << n; + eepromctl(ctlr, EectrlS); + } + + eepromctl(ctlr, 0); + + /* clear Chip Select and clock C */ + eepromctl(ctlr, EectrlS); + eepromctl(ctlr, 0); + eepromctl(ctlr, EectrlC); + + return val; +} + +/* Read 16-bit from eFUSE ROM (>=RT3071 only.) */ +static u16int +efuseread2(Ctlr *ctlr, u16int addr) +{ + u32int tmp; + u16int reg; + int ntries; + + addr *= 2; + /*- + * Read one 16-byte block into registers EFUSE_DATA[0-3]: + * DATA0: F E D C + * DATA1: B A 9 8 + * DATA2: 7 6 5 4 + * DATA3: 3 2 1 0 + */ + tmp = csr32r(ctlr, Rt3070EfuseCtrl); + tmp &= ~(Rt3070EfsromModeMask | Rt3070EfsromAinMask); + tmp |= (addr & ~0xf) << Rt3070EfsromAinShift | Rt3070EfsromKick; + csr32w(ctlr, Rt3070EfuseCtrl, tmp); + for(ntries = 0; ntries < 500; ntries++){ + tmp = csr32r(ctlr, Rt3070EfuseCtrl); + if(!(tmp & Rt3070EfsromKick)) + break; + microdelay(2); + } + if(ntries == 500) + return 0xffff; + + if((tmp & Rt3070EfuseAoutMask) == Rt3070EfuseAoutMask) + return 0xffff; /* address not found */ + + /* determine to which 32-bit register our 16-bit word belongs */ + reg = Rt3070EfuseData3 - (addr & 0xc); + tmp = csr32r(ctlr, reg); + + return (addr & 2) ? tmp >> 16 : tmp & 0xffff; +} + +static char* +eepromread(Ether *edev) +{ + s8int delta_2ghz, delta_5ghz; + u32int tmp; + u16int val; + int ridx, ant, i; + u16int (*rom_read)(Ctlr*, u16int); + Ctlr *ctlr; + + enum { DefLna = 10 }; + + ctlr = edev->ctlr; + /* check whether the ROM is eFUSE ROM or EEPROM */ + rom_read = eeread2; + if(ctlr->mac_ver >= 0x3071){ + tmp = csr32r(ctlr, Rt3070EfuseCtrl); + if(tmp & Rt3070SelEfuse) + rom_read = efuseread2; + } + + /* read MAC address */ + val = rom_read(ctlr, EepromMac01); + edev->ea[0] = val & 0xff; + edev->ea[1] = val >> 8; + val = rom_read(ctlr, EepromMac23); + edev->ea[2] = val & 0xff; + edev->ea[3] = val >> 8; + val = rom_read(ctlr, EepromMac45); + edev->ea[4] = val & 0xff; + edev->ea[5] = val >> 8; + + /* read vendor BBP settings */ + for(i = 0; i < 8; i++){ + val = rom_read(ctlr, EepromBbpBase + i); + ctlr->bbp[i].val = val & 0xff; + ctlr->bbp[i].reg = val >> 8; + } + + if(ctlr->mac_ver >= 0x3071){ + /* read vendor RF settings */ + for(i = 0; i < 10; i++){ + val = rom_read(ctlr, Rt3071EepromRfBase + i); + ctlr->rf[i].val = val & 0xff; + ctlr->rf[i].reg = val >> 8; + } + } + + /* read RF frequency offset from EEPROM */ + val = rom_read(ctlr, EepromFreqLeds); + ctlr->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; + if((val >> 8) != 0xff){ + /* read LEDs operating mode */ + ctlr->leds = val >> 8; + ctlr->led[0] = rom_read(ctlr, EepromLed1); + ctlr->led[1] = rom_read(ctlr, EepromLed2); + ctlr->led[2] = rom_read(ctlr, EepromLed3); + }else{ + /* broken EEPROM, use default settings */ + ctlr->leds = 0x01; + ctlr->led[0] = 0x5555; + ctlr->led[1] = 0x2221; + ctlr->led[2] = 0xa9f8; + } + /* read RF information */ + val = rom_read(ctlr, EepromAntenna); + if(val == 0xffff){ + if(ctlr->mac_ver == 0x3593){ + /* default to RF3053 3T3R */ + ctlr->rf_rev = Rf3053; + ctlr->ntxchains = 3; + ctlr->nrxchains = 3; + }else if(ctlr->mac_ver >= 0x3071){ + /* default to RF3020 1T1R */ + ctlr->rf_rev = Rf3020; + ctlr->ntxchains = 1; + ctlr->nrxchains = 1; + }else{ + /* default to RF2820 1T2R */ + ctlr->rf_rev = Rf2820; + ctlr->ntxchains = 1; + ctlr->nrxchains = 2; + } + }else{ + ctlr->rf_rev = (val >> 8) & 0xf; + ctlr->ntxchains = (val >> 4) & 0xf; + ctlr->nrxchains = val & 0xf; + } + + /* check if RF supports automatic Tx access gain control */ + val = rom_read(ctlr, EepromConfig); + /* check if driver should patch the DAC issue */ + if((val >> 8) != 0xff) + ctlr->patch_dac = (val >> 15) & 1; + if((val & 0xff) != 0xff){ + ctlr->ext_5ghz_lna = (val >> 3) & 1; + ctlr->ext_2ghz_lna = (val >> 2) & 1; + /* check if RF supports automatic Tx access gain control */ + ctlr->calib_2ghz = ctlr->calib_5ghz = 0; /* XXX (val >> 1) & 1 */; + /* check if we have a hardware radio switch */ + ctlr->rfswitch = val & 1; + } + if(ctlr->flags & AdvancedPs){ + /* read PCIe power save level */ + val = rom_read(ctlr, EepromPciePslevel); + if((val & 0xff) != 0xff){ + ctlr->pslevel = val & 0x3; + val = rom_read(ctlr, EepromRev); + if((val & 0xff80) != 0x9280) + ctlr->pslevel = MIN(ctlr->pslevel, 1); + } + } + + /* read power settings for 2GHz channels */ + for(i = 0; i < 14; i += 2){ + val = rom_read(ctlr, + EepromPwr2ghzBase1 + i / 2); + ctlr->txpow1[i + 0] = (s8int)(val & 0xff); + ctlr->txpow1[i + 1] = (s8int)(val >> 8); + + val = rom_read(ctlr, + EepromPwr2ghzBase2 + i / 2); + ctlr->txpow2[i + 0] = (s8int)(val & 0xff); + ctlr->txpow2[i + 1] = (s8int)(val >> 8); + } + /* fix broken Tx power entries */ + + for(i = 0; i < 14; i++){ + if(ctlr->txpow1[i] < 0 || ctlr->txpow1[i] > 31) + ctlr->txpow1[i] = 5; + if(ctlr->txpow2[i] < 0 || ctlr->txpow2[i] > 31) + ctlr->txpow2[i] = 5; + } + /* read power settings for 5GHz channels */ + for(i = 0; i < 40; i += 2){ + val = rom_read(ctlr, + EepromPwr5ghzBase1 + i / 2); + ctlr->txpow1[i + 14] = (s8int)(val & 0xff); + ctlr->txpow1[i + 15] = (s8int)(val >> 8); + + val = rom_read(ctlr, + EepromPwr5ghzBase2 + i / 2); + ctlr->txpow2[i + 14] = (s8int)(val & 0xff); + ctlr->txpow2[i + 15] = (s8int)(val >> 8); + } + + /* fix broken Tx power entries */ + for(i = 0; i < 40; i++){ + if(ctlr->txpow1[14 + i] < -7 || ctlr->txpow1[14 + i] > 15) + ctlr->txpow1[14 + i] = 5; + if(ctlr->txpow2[14 + i] < -7 || ctlr->txpow2[14 + i] > 15) + ctlr->txpow2[14 + i] = 5; + } + + /* read Tx power compensation for each Tx rate */ + val = rom_read(ctlr, EepromDeltapwr); + delta_2ghz = delta_5ghz = 0; + if((val & 0xff) != 0xff && (val & 0x80)){ + delta_2ghz = val & 0xf; + if(!(val & 0x40)) /* negative number */ + delta_2ghz = -delta_2ghz; + } + val >>= 8; + if((val & 0xff) != 0xff && (val & 0x80)){ + delta_5ghz = val & 0xf; + if(!(val & 0x40)) /* negative number */ + delta_5ghz = -delta_5ghz; + } + + for(ridx = 0; ridx < 5; ridx++){ + u32int reg; + + val = rom_read(ctlr, EepromRpwr + ridx * 2); + reg = val; + val = rom_read(ctlr, EepromRpwr + ridx * 2 + 1); + reg |= (u32int)val << 16; + + ctlr->txpow20mhz[ridx] = reg; + ctlr->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); + ctlr->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); + } + + /* read factory-calibrated samples for temperature compensation */ + val = rom_read(ctlr, EepromTssi12ghz); + ctlr->tssi_2ghz[0] = val & 0xff; /* [-4] */ + ctlr->tssi_2ghz[1] = val >> 8; /* [-3] */ + val = rom_read(ctlr, EepromTssi22ghz); + ctlr->tssi_2ghz[2] = val & 0xff; /* [-2] */ + ctlr->tssi_2ghz[3] = val >> 8; /* [-1] */ + val = rom_read(ctlr, EepromTssi32ghz); + ctlr->tssi_2ghz[4] = val & 0xff; /* [+0] */ + ctlr->tssi_2ghz[5] = val >> 8; /* [+1] */ + val = rom_read(ctlr, EepromTssi42ghz); + ctlr->tssi_2ghz[6] = val & 0xff; /* [+2] */ + ctlr->tssi_2ghz[7] = val >> 8; /* [+3] */ + val = rom_read(ctlr, EepromTssi52ghz); + ctlr->tssi_2ghz[8] = val & 0xff; /* [+4] */ + ctlr->step_2ghz = val >> 8; + /* check that ref value is correct, otherwise disable calibration */ + if(ctlr->tssi_2ghz[4] == 0xff) + ctlr->calib_2ghz = 0; + + val = rom_read(ctlr, EepromTssi15ghz); + ctlr->tssi_5ghz[0] = val & 0xff; /* [-4] */ + ctlr->tssi_5ghz[1] = val >> 8; /* [-3] */ + val = rom_read(ctlr, EepromTssi25ghz); + ctlr->tssi_5ghz[2] = val & 0xff; /* [-2] */ + ctlr->tssi_5ghz[3] = val >> 8; /* [-1] */ + val = rom_read(ctlr, EepromTssi35ghz); + ctlr->tssi_5ghz[4] = val & 0xff; /* [+0] */ + ctlr->tssi_5ghz[5] = val >> 8; /* [+1] */ + val = rom_read(ctlr, EepromTssi45ghz); + ctlr->tssi_5ghz[6] = val & 0xff; /* [+2] */ + ctlr->tssi_5ghz[7] = val >> 8; /* [+3] */ + val = rom_read(ctlr, EepromTssi55ghz); + ctlr->tssi_5ghz[8] = val & 0xff; /* [+4] */ + ctlr->step_5ghz = val >> 8; + /* check that ref value is correct, otherwise disable calibration */ + if(ctlr->tssi_5ghz[4] == 0xff) + ctlr->calib_5ghz = 0; + + /* read RSSI offsets and LNA gains from EEPROM */ + val = rom_read(ctlr, EepromRssi12ghz); + ctlr->rssi_2ghz[0] = val & 0xff; /* Ant A */ + ctlr->rssi_2ghz[1] = val >> 8; /* Ant B */ + val = rom_read(ctlr, EepromRssi22ghz); + if(ctlr->mac_ver >= 0x3071){ + /* + * On RT3090 chips (limited to 2 Rx chains), this ROM + * field contains the Tx mixer gain for the 2GHz band. + */ + if((val & 0xff) != 0xff) + ctlr->txmixgain_2ghz = val & 0x7; + }else + ctlr->rssi_2ghz[2] = val & 0xff; /* Ant C */ + ctlr->lna[2] = val >> 8; /* channel group 2 */ + + val = rom_read(ctlr, EepromRssi15ghz); + ctlr->rssi_5ghz[0] = val & 0xff; /* Ant A */ + ctlr->rssi_5ghz[1] = val >> 8; /* Ant B */ + val = rom_read(ctlr, EepromRssi25ghz); + ctlr->rssi_5ghz[2] = val & 0xff; /* Ant C */ + ctlr->lna[3] = val >> 8; /* channel group 3 */ + + val = rom_read(ctlr, EepromLna); + if(ctlr->mac_ver >= 0x3071) + ctlr->lna[0] = DefLna; + else /* channel group 0 */ + ctlr->lna[0] = val & 0xff; + ctlr->lna[1] = val >> 8; /* channel group 1 */ + + /* fix broken 5GHz LNA entries */ + if(ctlr->lna[2] == 0 || ctlr->lna[2] == 0xff){ + ctlr->lna[2] = ctlr->lna[1]; + } + if(ctlr->lna[3] == 0 || ctlr->lna[3] == 0xff){ + ctlr->lna[3] = ctlr->lna[1]; + } + + /* fix broken RSSI offset entries */ + for(ant = 0; ant < 3; ant++){ + if(ctlr->rssi_2ghz[ant] < -10 || ctlr->rssi_2ghz[ant] > 10){ + ctlr->rssi_2ghz[ant] = 0; + } + if(ctlr->rssi_5ghz[ant] < -10 || ctlr->rssi_5ghz[ant] > 10){ + ctlr->rssi_5ghz[ant] = 0; + } + } + + return 0; +} + +static const char * +getrfname(u8int rev) +{ + if((rev == 0) || (rev >= nelem(rfnames))) + return "unknown"; + if(rfnames[rev][0] == '\0') + return "unknown"; + return rfnames[rev]; +} + +static int +rbplant(Ctlr *ctlr, int i) +{ + Block *b; + uchar *p; + + b = iallocb(Rbufsize + 256); + if(b == nil) + return -1; + b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256); + memset(b->rp, 0, Rbufsize); + ctlr->rx.b[i] = b; + p = (uchar*)&ctlr->rx.p[i * 4]; /* sdp0 */ + memset(p, 0, Rdscsize); + put32(p, PCIWADDR(b->rp)); + p = (uchar*)&ctlr->rx.p[i * 4 + 1]; /* sdl0 */ + p += 2; /* sdl1 */ + put16(p, Rbufsize);; + + return 0; +} + +static char* +allocrx(Ctlr *ctlr, RXQ *rx) +{ + int i; + + if(rx->b == nil) + rx->b = malloc(sizeof(Block*) * Nrx); + if(rx->p == nil) /* Rx descriptors */ + rx->p = mallocalign(Nrx * Rdscsize, 16, 0, 0); + if(rx->b == nil || rx->p == nil) + return "no memory for rx ring"; + memset(rx->p, 0, Nrx * Rdscsize); + for(i=0; ib[i] != nil){ + freeb(rx->b[i]); + rx->b[i] = nil; + } + if(rbplant(ctlr, i) < 0) + return "no memory for rx descriptors"; + } + rx->i = 0; + return nil; +} + +static void +freerx(Ctlr *, RXQ *rx) +{ + int i; + + for(i = 0; i < Nrx; i++){ + if(rx->b[i] != nil){ + freeb(rx->b[i]); + rx->b[i] = nil; + } + } + free(rx->b); + free(rx->p); + rx->p = nil; + rx->b = nil; + rx->i = 0; +} + +static char* +alloctx(Ctlr *, TXQ *tx) +{ + if(tx->b == nil) + tx->b = malloc(sizeof(Block*) * Ntx); + if(tx->d == nil) /* Tx descriptors */ + tx->d = mallocalign(Ntx * Tdscsize, 16, 0, 0); + if(tx->b == nil || tx->d == nil) + return "no memory for tx ring"; + memset(tx->d, 0, Ntx * Tdscsize); + memset(tx->b, 0, Ntx * sizeof(Block*)); + tx->i = 0; + return nil; +} + +static void +freetx(Ctlr *, TXQ *tx) +{ + free(tx->b); + free(tx->d); + tx->d = nil; + tx->i = 0; +} + +static char* +alloctxpool(Ctlr *ctlr) +{ + Pool *pool; + + pool = &ctlr->pool; + if(pool->p == nil) + pool->p = mallocalign(Ntxpool * TxwiDmaSz, 4096, 0, 0); + if(pool->p == nil) + return "no memory for pool"; + memset(pool->p, 0, Ntxpool * TxwiDmaSz); + pool->i = 0; + return 0; +} + +static char* +initring(Ctlr *ctlr) +{ + int qid; + char *err; + + /* + * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings. + */ + for(qid = 0; qid < 6; qid++){ + if((err = alloctx(ctlr, &ctlr->tx[qid])) != nil) + goto fail1; + } + if((err = allocrx(ctlr, &ctlr->rx)) != nil) + goto fail1; + + if((err = alloctxpool(ctlr)) != nil) + goto fail2; + /* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */ + + ctlr->mgtqid = (ctlr->mac_ver == 0x2860 && ctlr->mac_rev == 0x0100) ? + 3 : 5; + + return nil; +fail2: freerx(ctlr, &ctlr->rx); + return err; +fail1: while(--qid >= 0) + freetx(ctlr, &ctlr->tx[qid]); + return err; +} + +static int +rt2860init(Ether *edev) +{ + Ctlr *ctlr; + int ntries; + char *err; + u32int tmp; + + SET(tmp); + ctlr = edev->ctlr; + /* wait for NIC to initialize */ + for(ntries = 0; ntries < 100; ntries++){ + tmp = csr32r(ctlr, AsicVerId); + if(tmp != 0 && tmp != 0xffffffff) + break; + microdelay(10); + } + if(ntries == 100){ + print("timeout waiting for NIC to initialize"); + return -1; + } + ctlr->mac_ver = tmp >> 16; + ctlr->mac_rev = tmp & 0xffff; + + if(ctlr->mac_ver != 0x2860){ + switch(ctlr->pdev->did){ + default: + break; + case RalinkRT2890: + case RalinkRT2790: + case RalinkRT3090: + case AwtRT2890: + ctlr->flags = AdvancedPs; + break; + } + } + /* retrieve RF rev. no and various other things from EEPROM */ + eepromread(edev); + + print("MAC/BBP RT%X (rev 0x%04X), RF %s (MIMO %dT%dR)\n", + ctlr->mac_ver, ctlr->mac_rev, + getrfname(ctlr->rf_rev), ctlr->ntxchains, ctlr->nrxchains); + if((err = initring(ctlr)) != nil){ + print("error: %s", err); + return -1; + } + + return 0; +} + +static Ctlr *rt2860head, *rt2860tail; + +static void +rt2860pci(void) +{ + Pcidev *pdev; + + pdev = nil; + while(pdev = pcimatch(pdev, 0, 0)){ + Ctlr *ctlr; + void *mem; + + if(pdev->ccrb != 2 || pdev->ccru != 0x80) + continue; + if(pdev->vid != 0x1814) /* Ralink */ + continue; + + switch(pdev->did){ + default: + continue; + case RalinkRT2790: + case RalinkRT3090: + break; + } + + pcisetbme(pdev); + pcisetpms(pdev, 0); + + ctlr = malloc(sizeof(Ctlr)); + if(ctlr == nil){ + print("rt2860: unable to alloc Ctlr\n"); + continue; + } + ctlr->port = pdev->mem[0].bar & ~0x0F; + mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size); + if(mem == nil){ + print("rt2860: can't map %8.8luX\n", pdev->mem[0].bar); + free(ctlr); + continue; + } + ctlr->nic = mem; + ctlr->pdev = pdev; + + if(rt2860head != nil) + rt2860tail->link = ctlr; + else + rt2860head = ctlr; + rt2860tail = ctlr; + } +} + +static int +rt2860pnp(Ether* edev) +{ + Ctlr *ctlr; + + if(rt2860head == nil) + rt2860pci(); +again: + for(ctlr = rt2860head; ctlr != nil; ctlr = ctlr->link){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pdev->intl; + edev->tbdf = ctlr->pdev->tbdf; + edev->arg = edev; + edev->interrupt = rt2860interrupt; + edev->attach = rt2860attach; + edev->ifstat = rt2860ifstat; + edev->ctl = rt2860ctl; + edev->promiscuous = rt2860promiscuous; + edev->multicast = nil; + edev->mbps = 10; + + if(rt2860init(edev) < 0){ + edev->ctlr = nil; + goto again; + } + return 0; +} + +void +etherrt2860link(void) +{ + addethercard("rt2860", rt2860pnp); +} --- /sys/src/9/pc/wifi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/pc/wifi.c Tue Apr 22 00:00:00 2014 @@ -0,0 +1,1576 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "wifi.h" + +#include + +typedef struct SNAP SNAP; +struct SNAP +{ + uchar dsap; + uchar ssap; + uchar control; + uchar orgcode[3]; + uchar type[2]; +}; + +enum { + WIFIHDRSIZE = 2+2+3*6+2, + SNAPHDRSIZE = 8, +}; + +static char Sconn[] = "connecting"; +static char Sauth[] = "authenticated"; +static char Sneedauth[] = "need authentication"; +static char Sunauth[] = "unauthenticated"; + +static char Sassoc[] = "associated"; +static char Sunassoc[] = "unassociated"; +static char Sblocked[] = "blocked"; /* no keys negotiated. only pass EAPOL frames */ + +static uchar basicrates[] = { + 0x80 | 2, /* 1.0 Mb/s */ + 0x80 | 4, /* 2.0 Mb/s */ + 0x80 | 11, /* 5.5 Mb/s */ + 0x80 | 22, /* 11.0 Mb/s */ + + 0 +}; + +static Block* wifidecrypt(Wifi *, Wnode *, Block *); +static Block* wifiencrypt(Wifi *, Wnode *, Block *); + +static uchar* +srcaddr(Wifipkt *w) +{ + if((w->fc[1] & 0x02) == 0) + return w->a2; + if((w->fc[1] & 0x01) == 0) + return w->a3; + return w->a4; +} +static uchar* +dstaddr(Wifipkt *w) +{ + if((w->fc[1] & 0x01) != 0) + return w->a3; + return w->a1; +} + +int +wifihdrlen(Wifipkt *w) +{ + int n; + + n = WIFIHDRSIZE; + if((w->fc[0] & 0x0c) == 0x08) + if((w->fc[0] & 0xf0) == 0x80){ /* QOS */ + n += 2; + if(w->fc[1] & 0x80) + n += 4; + } + if((w->fc[1] & 3) == 0x03) + n += Eaddrlen; + return n; +} + +void +wifiiq(Wifi *wifi, Block *b) +{ + SNAP s; + Wifipkt h, *w; + Etherpkt *e; + int hdrlen; + + if(BLEN(b) < WIFIHDRSIZE) + goto drop; + w = (Wifipkt*)b->rp; + hdrlen = wifihdrlen(w); + if(BLEN(b) < hdrlen) + goto drop; + if(w->fc[1] & 0x40){ + /* encrypted */ + qpass(wifi->iq, b); + return; + } + switch(w->fc[0] & 0x0c){ + case 0x00: /* management */ + if((w->fc[1] & 3) != 0x00) /* STA->STA */ + break; + qpass(wifi->iq, b); + return; + case 0x04: /* control */ + break; + case 0x08: /* data */ + b->rp += hdrlen; + switch(w->fc[0] & 0xf0){ + default: + goto drop; + case 0x80: /* QOS */ + case 0x00: + break; + } + if(BLEN(b) < SNAPHDRSIZE) + break; + memmove(&s, b->rp, SNAPHDRSIZE); + if(s.dsap != 0xAA || s.ssap != 0xAA || s.control != 3) + break; + if(s.orgcode[0] != 0 || s.orgcode[1] != 0 || s.orgcode[2] != 0) + break; + b->rp += SNAPHDRSIZE-ETHERHDRSIZE; + h = *w; + e = (Etherpkt*)b->rp; + memmove(e->d, dstaddr(&h), Eaddrlen); + memmove(e->s, srcaddr(&h), Eaddrlen); + memmove(e->type, s.type, 2); + etheriq(wifi->ether, b, 1); + return; + } +drop: + freeb(b); +} + +static void +wifitx(Wifi *wifi, Wnode *wn, Block *b) +{ + Wifipkt *w; + uint seq; + + wn->lastsend = MACHP(0)->ticks; + + seq = incref(&wifi->txseq); + seq <<= 4; + + w = (Wifipkt*)b->rp; + w->dur[0] = 0; + w->dur[1] = 0; + w->seq[0] = seq; + w->seq[1] = seq>>8; + + if((w->fc[0] & 0x0c) != 0x00) + b = wifiencrypt(wifi, wn, b); + + if(b != nil) + (*wifi->transmit)(wifi, wn, b); +} + +static Wnode* +nodelookup(Wifi *wifi, uchar *bssid, int new) +{ + Wnode *wn, *nn; + + 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; + } + } + if((nn = wifi->node) == wn) + nn++; + for(wn = 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((long)(wn->lastseen - nn->lastseen) < 0) + nn = wn; + } + if(!new) + return nil; + memset(nn, 0, sizeof(Wnode)); + memmove(nn->bssid, bssid, Eaddrlen); + nn->lastseen = MACHP(0)->ticks; + return nn; +} + +static uchar* +putrates(uchar *p, uchar *rates) +{ + int n, m; + + n = m = strlen((char*)rates); + if(n > 8) + n = 8; + /* supported rates */ + *p++ = 1; + *p++ = n; + memmove(p, rates, n); + p += n; + if(m > 8){ + /* extended supported rates */ + *p++ = 50; + *p++ = m; + memmove(p, rates, m); + p += m; + } + return p; +} + +static void +wifiprobe(Wifi *wifi, Wnode *wn) +{ + Wifipkt *w; + Block *b; + uchar *p; + int n; + + n = strlen(wifi->essid); + if(n == 0){ + /* no specific essid, just tell driver to tune channel */ + (*wifi->transmit)(wifi, wn, nil); + return; + } + + b = allocb(WIFIHDRSIZE + 512); + w = (Wifipkt*)b->wp; + w->fc[0] = 0x40; /* probe request */ + w->fc[1] = 0x00; /* STA->STA */ + memmove(w->a1, wifi->ether->bcast, Eaddrlen); /* ??? */ + memmove(w->a2, wifi->ether->ea, Eaddrlen); + memmove(w->a3, wifi->ether->bcast, Eaddrlen); + b->wp += WIFIHDRSIZE; + p = b->wp; + + *p++ = 0; /* set */ + *p++ = n; + memmove(p, wifi->essid, n); + p += n; + + p = putrates(p, wifi->rates); + + *p++ = 3; /* ds parameter set */ + *p++ = 1; + *p++ = wn->channel; + + b->wp = p; + wifitx(wifi, wn, b); +} + +static void +sendauth(Wifi *wifi, Wnode *bss) +{ + Wifipkt *w; + Block *b; + uchar *p; + + b = allocb(WIFIHDRSIZE + 3*2); + w = (Wifipkt*)b->wp; + w->fc[0] = 0xB0; /* auth request */ + w->fc[1] = 0x00; /* STA->STA */ + memmove(w->a1, bss->bssid, Eaddrlen); /* ??? */ + memmove(w->a2, wifi->ether->ea, Eaddrlen); + memmove(w->a3, bss->bssid, Eaddrlen); + b->wp += WIFIHDRSIZE; + p = b->wp; + *p++ = 0; /* alg */ + *p++ = 0; + *p++ = 1; /* seq */ + *p++ = 0; + *p++ = 0; /* status */ + *p++ = 0; + b->wp = p; + + bss->aid = 0; + + wifitx(wifi, bss, b); +} + +static void +sendassoc(Wifi *wifi, Wnode *bss) +{ + Wifipkt *w; + Block *b; + uchar *p; + int n; + + b = allocb(WIFIHDRSIZE + 512); + w = (Wifipkt*)b->wp; + w->fc[0] = 0x00; /* assoc request */ + w->fc[1] = 0x00; /* STA->STA */ + memmove(w->a1, bss->bssid, Eaddrlen); /* ??? */ + memmove(w->a2, wifi->ether->ea, Eaddrlen); + memmove(w->a3, bss->bssid, Eaddrlen); + b->wp += WIFIHDRSIZE; + p = b->wp; + *p++ = 1; /* capinfo */ + *p++ = 0; + *p++ = 16; /* interval */ + *p++ = 16>>8; + + n = strlen(bss->ssid); + *p++ = 0; /* SSID */ + *p++ = n; + memmove(p, bss->ssid, n); + p += n; + + p = putrates(p, wifi->rates); + + n = bss->rsnelen; + if(n > 0){ + memmove(p, bss->rsne, n); + p += n; + } + + b->wp = p; + wifitx(wifi, bss, b); +} + +static void +setstatus(Wifi *wifi, Wnode *wn, char *new) +{ + char *old; + + old = wn->status; + wn->status = new; + if(wifi->debug && new != old) + print("#l%d: status %E: %s -> %s (from pc=%#p)\n", + wifi->ether->ctlrno, + wn->bssid, + old, new, + getcallerpc(&wifi)); +} + +static void +recvassoc(Wifi *wifi, Wnode *wn, uchar *d, int len) +{ + uint s; + + if(len < 2+2+2) + return; + + d += 2; /* caps */ + s = d[0] | d[1]<<8; + d += 2; + switch(s){ + case 0x00: + wn->aid = d[0] | d[1]<<8; + if(wn->rsnelen > 0) + setstatus(wifi, wn, Sblocked); + else + setstatus(wifi, wn, Sassoc); + break; + default: + wn->aid = 0; + setstatus(wifi, wn, Sunassoc); + } +} + +static void +recvbeacon(Wifi *wifi, Wnode *wn, uchar *d, int len) +{ + static uchar wpa1oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; + uchar *e, *x, *p; + uchar t, m[256/8]; + + if(len < 8+2+2) + return; + + d += 8; /* timestamp */ + wn->ival = d[0] | d[1]<<8; + d += 2; + wn->cap = d[0] | d[1]<<8; + d += 2; + + memset(m, 0, sizeof(m)); + for(e = d + len; d+2 <= e; d = x){ + d += 2; + x = d + d[-1]; + t = d[-2]; + + /* skip double entries */ + if(m[t/8] & 1<<(t%8)) + continue; + m[t/8] |= 1<<(t%8); + + switch(t){ + case 0: /* SSID */ + len = 0; + while(len < Essidlen && d+len < x && d[len] != 0) + len++; + if(len == 0) + continue; + if(len != strlen(wn->ssid) || strncmp(wn->ssid, (char*)d, len) != 0){ + strncpy(wn->ssid, (char*)d, len); + wn->ssid[len] = 0; + } + break; + case 1: /* supported rates */ + case 50: /* extended rates */ + if(wn->minrate != nil || wn->maxrate != nil || wifi->rates == nil) + break; /* already set */ + while(d < x){ + t = *d++ & 0x7f; + for(p = wifi->rates; *p != 0; p++){ + if((*p & 0x7f) == t){ + if(wn->minrate == nil || t < (*wn->minrate & 0x7f)) + wn->minrate = p; + if(wn->maxrate == nil || t > (*wn->maxrate & 0x7f)) + wn->maxrate = p; + break; + } + } + } + break; + case 3: /* DSPARAMS */ + if(d != x) + wn->channel = d[0]; + break; + case 221: /* vendor specific */ + len = x - d; + if(len < sizeof(wpa1oui) || memcmp(d, wpa1oui, sizeof(wpa1oui)) != 0) + break; + /* no break */ + case 48: /* RSN information */ + len = x - &d[-2]; + memmove(wn->brsne, &d[-2], len); + wn->brsnelen = len; + break; + } + } +} + +static void +wifideauth(Wifi *wifi, Wnode *wn) +{ + Ether *ether; + Netfile *f; + int i; + + /* deassociate node, clear keys */ + setstatus(wifi, wn, Sunauth); + memset(wn->rxkey, 0, sizeof(wn->rxkey)); + memset(wn->txkey, 0, sizeof(wn->txkey)); + wn->aid = 0; + + if(wn == wifi->bss){ + /* notify driver about node aid association */ + (*wifi->transmit)(wifi, wn, nil); + + /* notify aux/wpa with a zero length write that we got deassociated from the ap */ + ether = wifi->ether; + for(i=0; infile; i++){ + f = ether->f[i]; + if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e) + continue; + qwrite(f->in, 0, 0); + } + } +} + +/* check if a node qualifies as our bss matching bssid and essid */ +static int +goodbss(Wifi *wifi, Wnode *wn) +{ + if(memcmp(wifi->bssid, wifi->ether->bcast, Eaddrlen) != 0){ + if(memcmp(wifi->bssid, wn->bssid, Eaddrlen) != 0) + return 0; /* bssid doesnt match */ + } else if(wifi->essid[0] == 0) + return 0; /* both bssid and essid unspecified */ + if(wifi->essid[0] != 0 && strcmp(wifi->essid, wn->ssid) != 0) + return 0; /* essid doesnt match */ + return 1; +} + +static void +wifiproc(void *arg) +{ + Wifi *wifi; + Wifipkt *w; + Wnode *wn; + Block *b; + + b = nil; + wifi = arg; + while(waserror()) + ; + for(;;){ + if(b != nil){ + freeb(b); + b = nil; + continue; + } + if((b = qbread(wifi->iq, 100000)) == nil) + break; + w = (Wifipkt*)b->rp; + if(w->fc[1] & 0x40){ + /* encrypted */ + if((wn = nodelookup(wifi, w->a2, 0)) == nil) + continue; + if((b = wifidecrypt(wifi, wn, b)) != nil){ + w = (Wifipkt*)b->rp; + if(w->fc[1] & 0x40) + continue; + wifiiq(wifi, b); + b = nil; + } + continue; + } + /* management */ + if((w->fc[0] & 0x0c) != 0x00) + continue; + + switch(w->fc[0] & 0xf0){ + case 0x50: /* probe response */ + if(wifi->debug) + print("#l%d: got probe from %E\n", wifi->ether->ctlrno, w->a3); + /* no break */ + case 0x80: /* beacon */ + if((wn = nodelookup(wifi, w->a3, 1)) == nil) + continue; + b->rp += wifihdrlen(w); + recvbeacon(wifi, wn, b->rp, BLEN(b)); + + if(wifi->bss == nil + && TK2MS(MACHP(0)->ticks - wn->lastsend) > 1000 + && goodbss(wifi, wn)){ + setstatus(wifi, wn, Sconn); + sendauth(wifi, wn); + } + continue; + } + + if(memcmp(w->a1, wifi->ether->ea, Eaddrlen)) + continue; + if((wn = nodelookup(wifi, w->a3, 0)) == nil) + continue; + switch(w->fc[0] & 0xf0){ + case 0x10: /* assoc response */ + case 0x30: /* reassoc response */ + b->rp += wifihdrlen(w); + recvassoc(wifi, wn, b->rp, BLEN(b)); + /* notify driver about node aid association */ + if(wn == wifi->bss) + (*wifi->transmit)(wifi, wn, nil); + break; + case 0xb0: /* auth */ + if(wifi->debug) + print("#l%d: got auth from %E\n", wifi->ether->ctlrno, wn->bssid); + if(wn->brsnelen > 0 && wn->rsnelen == 0) + setstatus(wifi, wn, Sneedauth); + else + setstatus(wifi, wn, Sauth); + if(wifi->bss == nil && goodbss(wifi, wn)){ + wifi->bss = wn; + if(wn->status == Sauth) + sendassoc(wifi, wn); + } + break; + case 0xc0: /* deauth */ + if(wifi->debug) + print("#l%d: got deauth from %E\n", wifi->ether->ctlrno, wn->bssid); + wifideauth(wifi, wn); + break; + } + } + pexit("wifi in queue closed", 1); +} + +static void +wifietheroq(Wifi *wifi, Block *b) +{ + Etherpkt e; + Wifipkt h; + int hdrlen; + Wnode *wn; + SNAP *s; + + if(BLEN(b) < ETHERHDRSIZE) + goto drop; + if((wn = wifi->bss) == nil) + goto drop; + + memmove(&e, b->rp, ETHERHDRSIZE); + b->rp += ETHERHDRSIZE; + + if(wn->status == Sblocked){ + /* only pass EAPOL frames when port is blocked */ + if((e.type[0]<<8 | e.type[1]) != 0x888e) + goto drop; + } else if(wn->status != Sassoc) + goto drop; + + h.fc[0] = 0x08; /* data */ + memmove(h.a1, wn->bssid, Eaddrlen); + if(memcmp(e.s, wifi->ether->ea, Eaddrlen) == 0) { + h.fc[1] = 0x01; /* STA->AP */ + } else { + h.fc[1] = 0x03; /* AP->AP (WDS) */ + memmove(h.a2, wifi->ether->ea, Eaddrlen); + } + memmove(dstaddr(&h), e.d, Eaddrlen); + memmove(srcaddr(&h), e.s, Eaddrlen); + + hdrlen = wifihdrlen(&h); + b = padblock(b, hdrlen + SNAPHDRSIZE); + memmove(b->rp, &h, hdrlen); + s = (SNAP*)(b->rp + hdrlen); + s->dsap = s->ssap = 0xAA; + s->control = 0x03; + s->orgcode[0] = 0; + s->orgcode[1] = 0; + s->orgcode[2] = 0; + memmove(s->type, e.type, 2); + + wifitx(wifi, wn, b); + return; +drop: + freeb(b); +} + +static void +wifoproc(void *arg) +{ + Ether *ether; + Wifi *wifi; + Block *b; + + wifi = arg; + ether = wifi->ether; + while(waserror()) + ; + while((b = qbread(ether->oq, 1000000)) != nil) + wifietheroq(wifi, b); + pexit("ether out queue closed", 1); +} + +static void +wifsproc(void *arg) +{ + Ether *ether; + Wifi *wifi; + Wnode wnscan; + Wnode *wn; + ulong now, tmout; + uchar *rate; + + wifi = arg; + ether = wifi->ether; + + wn = &wnscan; + memset(wn, 0, sizeof(*wn)); + memmove(wn->bssid, ether->bcast, Eaddrlen); + + while(waserror()) + ; +Scan: + /* scan for access point */ + while(wifi->bss == nil){ + ether->link = 0; + wnscan.channel = 1 + wnscan.channel % 11; + wifiprobe(wifi, &wnscan); + tsleep(&up->sleep, return0, 0, 1000); + } + + /* maintain access point */ + tmout = 0; + while((wn = wifi->bss) != nil){ + ether->link = (wn->status == Sassoc) || (wn->status == Sblocked); + if(ether->link && (rate = wn->maxrate) != nil) + ether->mbps = ((*rate & 0x7f)+1)/2; + now = MACHP(0)->ticks; + if(wn->status != Sneedauth && TK2SEC(now - wn->lastseen) > 60 || goodbss(wifi, wn) == 0){ + wifideauth(wifi, wn); + wifi->bss = nil; + break; + } + if(TK2MS(now - wn->lastsend) > 1000){ + if(wn->status == Sauth && (++tmout & 7) == 0) + wifideauth(wifi, wn); /* stuck in auth, start over */ + if(wn->status == Sconn || wn->status == Sunauth) + sendauth(wifi, wn); + if(wn->status == Sauth) + sendassoc(wifi, wn); + } + tsleep(&up->sleep, return0, 0, 500); + } + goto Scan; +} + +Wifi* +wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*)) +{ + char name[32]; + Wifi *wifi; + + wifi = malloc(sizeof(Wifi)); + if(wifi == nil) + error(Enomem); + wifi->iq = qopen(ether->limit, 0, 0, 0); + if(wifi->iq == nil){ + free(wifi); + error(Enomem); + } + wifi->ether = ether; + wifi->transmit = transmit; + + wifi->rates = basicrates; + + wifi->essid[0] = 0; + memmove(wifi->bssid, ether->bcast, Eaddrlen); + + snprint(name, sizeof(name), "#l%dwifi", ether->ctlrno); + kproc(name, wifiproc, wifi); + snprint(name, sizeof(name), "#l%dwifo", ether->ctlrno); + kproc(name, wifoproc, wifi); + snprint(name, sizeof(name), "#l%dwifs", ether->ctlrno); + kproc(name, wifsproc, wifi); + + return wifi; +} + +static int +hextob(char *s, char **sp, uchar *b, int n) +{ + int r; + + n <<= 1; + for(r = 0; r < n && *s; s++){ + *b <<= 4; + if(*s >= '0' && *s <= '9') + *b |= (*s - '0'); + else if(*s >= 'a' && *s <= 'f') + *b |= 10+(*s - 'a'); + else if(*s >= 'A' && *s <= 'F') + *b |= 10+(*s - 'A'); + else break; + if((++r & 1) == 0) + b++; + } + if(sp != nil) + *sp = s; + return r >> 1; +} + +static char *ciphers[] = { + [0] "clear", + [TKIP] "tkip", + [CCMP] "ccmp", +}; + +static int +parsekey(Wkey *k, char *s) +{ + char buf[256], *p; + int i; + + strncpy(buf, s, sizeof(buf)-1); + buf[sizeof(buf)-1] = 0; + if((p = strchr(buf, ':')) != nil) + *p++ = 0; + else + p = buf; + for(i=0; i= nelem(ciphers)) + return -1; + memset(k, 0, sizeof(Wkey)); + k->len = hextob(p, &p, k->key, sizeof(k->key)); + if(*p == '@') + k->tsc = strtoull(++p, nil, 16); + k->cipher = i; + return 0; +} + +enum { + CMdebug, + CMessid, + CMauth, + CMbssid, + CMrxkey0, + CMrxkey1, + CMrxkey2, + CMrxkey3, + CMrxkey4, + CMtxkey0, +}; + +static Cmdtab wifictlmsg[] = +{ + CMdebug, "debug", 0, + CMessid, "essid", 0, + CMauth, "auth", 0, + CMbssid, "bssid", 0, + + CMrxkey0, "rxkey0", 0, /* group keys */ + CMrxkey1, "rxkey1", 0, + CMrxkey2, "rxkey2", 0, + CMrxkey3, "rxkey3", 0, + + CMrxkey4, "rxkey", 0, /* peerwise keys */ + CMtxkey0, "txkey", 0, + + CMtxkey0, "txkey0", 0, +}; + +long +wifictl(Wifi *wifi, void *buf, long n) +{ + uchar addr[Eaddrlen]; + Cmdbuf *cb; + Cmdtab *ct; + Wnode *wn; + Wkey *k; + + cb = nil; + if(waserror()){ + free(cb); + nexterror(); + } + if(wifi->debug) + print("#l%d: wifictl: %.*s\n", wifi->ether->ctlrno, (int)n, buf); + memmove(addr, wifi->ether->bcast, Eaddrlen); + wn = wifi->bss; + cb = parsecmd(buf, n); + ct = lookupcmd(cb, wifictlmsg, nelem(wifictlmsg)); + if(ct->index >= CMauth){ + if(cb->nf > 1 && (ct->index == CMbssid || ct->index >= CMrxkey0)){ + if(parseether(addr, cb->f[1]) == 0){ + cb->f++; + cb->nf--; + wn = nodelookup(wifi, addr, 0); + } + } + if(wn == nil && ct->index != CMbssid) + error("missing node"); + } + switch(ct->index){ + case CMdebug: + if(cb->f[1] != nil) + wifi->debug = atoi(cb->f[1]); + else + wifi->debug ^= 1; + print("#l%d: debug: %d\n", wifi->ether->ctlrno, wifi->debug); + break; + case CMessid: + if(cb->f[1] != nil) + strncpy(wifi->essid, cb->f[1], Essidlen); + else + wifi->essid[0] = 0; + Findbss: + wn = wifi->bss; + if(wn != nil){ + if(goodbss(wifi, wn)) + break; + wifideauth(wifi, wn); + } + wifi->bss = nil; + if(wifi->essid[0] == 0 && memcmp(wifi->bssid, wifi->ether->bcast, Eaddrlen) == 0) + break; + for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++) + if(goodbss(wifi, wn)){ + setstatus(wifi, wn, Sconn); + sendauth(wifi, wn); + } + break; + case CMbssid: + memmove(wifi->bssid, addr, Eaddrlen); + goto Findbss; + case CMauth: + memset(wn->rxkey, 0, sizeof(wn->rxkey)); + memset(wn->txkey, 0, sizeof(wn->txkey)); + if(cb->f[1] == nil) + wn->rsnelen = 0; + else + wn->rsnelen = hextob(cb->f[1], nil, wn->rsne, sizeof(wn->rsne)); + if(wn->aid == 0){ + setstatus(wifi, wn, Sconn); + sendauth(wifi, wn); + } else { + setstatus(wifi, wn, Sauth); + sendassoc(wifi, wn); + } + break; + case CMrxkey0: case CMrxkey1: case CMrxkey2: case CMrxkey3: case CMrxkey4: + case CMtxkey0: + if(ct->index < CMtxkey0) + k = &wn->rxkey[ct->index - CMrxkey0]; + else + k = &wn->txkey[ct->index - CMtxkey0]; + if(cb->f[1] == nil || parsekey(k, cb->f[1]) != 0) + error("bad key"); + if(ct->index >= CMtxkey0 && wn->status == Sblocked) + setstatus(wifi, wn, Sassoc); + break; + } + poperror(); + free(cb); + return n; +} + +long +wifistat(Wifi *wifi, void *buf, long n, ulong off) +{ + static uchar zeros[Eaddrlen]; + char *s, *p, *e; + Wnode *wn; + long now; + int i; + + p = s = smalloc(4096); + e = s + 4096; + + wn = wifi->bss; + if(wn != nil){ + p = seprint(p, e, "essid: %s\n", wn->ssid); + p = seprint(p, e, "bssid: %E\n", wn->bssid); + p = seprint(p, e, "status: %s\n", wn->status); + p = seprint(p, e, "channel: %.2d\n", wn->channel); + + /* only print key ciphers and key length */ + for(i = 0; irxkey); i++) + p = seprint(p, e, "rxkey%d: %s:[%d]\n", i, + ciphers[wn->rxkey[i].cipher], wn->rxkey[i].len); + for(i = 0; itxkey); i++) + p = seprint(p, e, "txkey%d: %s:[%d]\n", i, + ciphers[wn->txkey[i].cipher], wn->txkey[i].len); + + if(wn->brsnelen > 0){ + p = seprint(p, e, "brsne: "); + for(i=0; ibrsnelen; i++) + p = seprint(p, e, "%.2X", wn->brsne[i]); + p = seprint(p, e, "\n"); + } + } else { + p = seprint(p, e, "essid: %s\n", wifi->essid); + p = seprint(p, e, "bssid: %E\n", wifi->bssid); + } + + now = MACHP(0)->ticks; + for(wn = wifi->node; wn != &wifi->node[nelem(wifi->node)]; wn++){ + if(wn->lastseen == 0) + continue; + p = seprint(p, e, "node: %E %.4x %-11ld %.2d %s\n", + wn->bssid, wn->cap, TK2MS(now - wn->lastseen), wn->channel, wn->ssid); + } + n = readstr(off, buf, n, s); + free(s); + return n; +} + +static void tkipencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc); +static int tkipdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc); +static void ccmpencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc); +static int ccmpdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc); + +static Block* +wifiencrypt(Wifi *, Wnode *wn, Block *b) +{ + uvlong tsc; + int n, kid; + Wifipkt *w; + Wkey *k; + + kid = 0; + k = &wn->txkey[kid]; + if(k->cipher == 0) + return b; + + n = wifihdrlen((Wifipkt*)b->rp); + + b = padblock(b, 8); + b = padblock(b, -(8+4)); + + w = (Wifipkt*)b->rp; + memmove(w, b->rp+8, n); + b->rp += n; + + tsc = ++k->tsc; + + switch(k->cipher){ + case TKIP: + b->rp[0] = tsc>>8; + b->rp[1] = (b->rp[0] | 0x20) & 0x7f; + b->rp[2] = tsc; + b->rp[3] = kid<<6 | 0x20; + b->rp[4] = tsc>>16; + b->rp[5] = tsc>>24; + b->rp[6] = tsc>>32; + b->rp[7] = tsc>>40; + b->rp += 8; + if(k->len != 32) + goto drop; + tkipencrypt(k, w, b, tsc); + break; + case CCMP: + b->rp[0] = tsc; + b->rp[1] = tsc>>8; + b->rp[2] = 0; + b->rp[3] = kid<<6 | 0x20; + b->rp[4] = tsc>>16; + b->rp[5] = tsc>>24; + b->rp[6] = tsc>>32; + b->rp[7] = tsc>>40; + b->rp += 8; + if(k->len != 16) + goto drop; + ccmpencrypt(k, w, b, tsc); + break; + default: + drop: + free(b); + return nil; + } + + b->rp = (uchar*)w; + w->fc[1] |= 0x40; + return b; +} + +static Block* +wifidecrypt(Wifi *, Wnode *wn, Block *b) +{ + uvlong tsc; + int n, kid; + Wifipkt *w; + Wkey *k; + + w = (Wifipkt*)b->rp; + n = wifihdrlen(w); + b->rp += n; + if(BLEN(b) < 8+8) + goto drop; + + kid = b->rp[3]>>6; + if((b->rp[3] & 0x20) == 0) + goto drop; + if((w->a1[0] & 1) == 0) + kid = 4; /* use peerwise key for non-unicast */ + + k = &wn->rxkey[kid]; + switch(k->cipher){ + case TKIP: + tsc = (uvlong)b->rp[7]<<40 | + (uvlong)b->rp[6]<<32 | + (uvlong)b->rp[5]<<24 | + (uvlong)b->rp[4]<<16 | + (uvlong)b->rp[0]<<8 | + (uvlong)b->rp[2]; + b->rp += 8; + if(tsc <= k->tsc || k->len != 32) + goto drop; + if(tkipdecrypt(k, w, b, tsc) != 0) + goto drop; + break; + case CCMP: + tsc = (uvlong)b->rp[7]<<40 | + (uvlong)b->rp[6]<<32 | + (uvlong)b->rp[5]<<24 | + (uvlong)b->rp[4]<<16 | + (uvlong)b->rp[1]<<8 | + (uvlong)b->rp[0]; + b->rp += 8; + if(tsc <= k->tsc || k->len != 16) + goto drop; + if(ccmpdecrypt(k, w, b, tsc) != 0) + goto drop; + break; + default: + drop: + freeb(b); + return nil; + } + + k->tsc = tsc; + b->rp -= n; + memmove(b->rp, w, n); + w = (Wifipkt*)b->rp; + w->fc[1] &= ~0x40; + return b; +} + +static u16int Sbox[256] = { + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A +}; + +static void +tkipk2tk(uchar key[16], u16int tk[8]) +{ + tk[0] = (u16int)key[1]<<8 | key[0]; + tk[1] = (u16int)key[3]<<8 | key[2]; + tk[2] = (u16int)key[5]<<8 | key[4]; + tk[3] = (u16int)key[7]<<8 | key[6]; + tk[4] = (u16int)key[9]<<8 | key[8]; + tk[5] = (u16int)key[11]<<8 | key[10]; + tk[6] = (u16int)key[13]<<8 | key[12]; + tk[7] = (u16int)key[15]<<8 | key[14]; +} + +static void +tkipphase1(u32int tscu, uchar ta[Eaddrlen], u16int tk[8], u16int p1k[5]) +{ + u16int *k, i, x0, x1, x2; + + p1k[0] = tscu; + p1k[1] = tscu>>16; + p1k[2] = (u16int)ta[1]<<8 | ta[0]; + p1k[3] = (u16int)ta[3]<<8 | ta[2]; + p1k[4] = (u16int)ta[5]<<8 | ta[4]; + + for(i=0; i<8; i++){ + k = &tk[i & 1]; + + x0 = p1k[4] ^ k[0]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + p1k[0] += x2; + x0 = p1k[0] ^ k[2]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + p1k[1] += x2; + x0 = p1k[1] ^ k[4]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + p1k[2] += x2; + x0 = p1k[2] ^ k[6]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + p1k[3] += x2; + x0 = p1k[3] ^ k[0]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + p1k[4] += x2; + + p1k[4] += i; + } +} + +static void +tkipphase2(u16int tscl, u16int p1k[5], u16int tk[8], uchar rc4key[16]) +{ + u16int ppk[6], x0, x1, x2; + + ppk[0] = p1k[0]; + ppk[1] = p1k[1]; + ppk[2] = p1k[2]; + ppk[3] = p1k[3]; + ppk[4] = p1k[4]; + ppk[5] = p1k[4] + tscl; + + x0 = ppk[5] ^ tk[0]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + ppk[0] += x2; + x0 = ppk[0] ^ tk[1]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + ppk[1] += x2; + x0 = ppk[1] ^ tk[2]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + ppk[2] += x2; + x0 = ppk[2] ^ tk[3]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + ppk[3] += x2; + x0 = ppk[3] ^ tk[4]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + ppk[4] += x2; + x0 = ppk[4] ^ tk[5]; + x1 = Sbox[x0 >> 8]; + x2 = Sbox[x0 & 0xFF] ^ ((x1>>8) | (x1<<8)); + ppk[5] += x2; + + x2 = ppk[5] ^ tk[6]; + ppk[0] += (x2 >> 1) | (x2 << 15); + x2 = ppk[0] ^ tk[7]; + ppk[1] += (x2 >> 1) | (x2 << 15); + + x2 = ppk[1]; + ppk[2] += (x2 >> 1) | (x2 << 15); + x2 = ppk[2]; + ppk[3] += (x2 >> 1) | (x2 << 15); + x2 = ppk[3]; + ppk[4] += (x2 >> 1) | (x2 << 15); + x2 = ppk[4]; + ppk[5] += (x2 >> 1) | (x2 << 15); + + rc4key[0] = tscl >> 8; + rc4key[1] = (rc4key[0] | 0x20) & 0x7F; + rc4key[2] = tscl; + rc4key[3] = (ppk[5] ^ tk[0]) >> 1; + rc4key[4] = ppk[0]; + rc4key[5] = ppk[0] >> 8; + rc4key[6] = ppk[1]; + rc4key[7] = ppk[1] >> 8; + rc4key[8] = ppk[2]; + rc4key[9] = ppk[2] >> 8; + rc4key[10] = ppk[3]; + rc4key[11] = ppk[3] >> 8; + rc4key[12] = ppk[4]; + rc4key[13] = ppk[4] >> 8; + rc4key[14] = ppk[5]; + rc4key[15] = ppk[5] >> 8; +} + +typedef struct MICstate MICstate; +struct MICstate +{ + u32int l; + u32int r; + u32int m; + u32int n; +}; + +static void +micsetup(MICstate *s, uchar key[8]) +{ + s->l = (u32int)key[0] | + (u32int)key[1]<<8 | + (u32int)key[2]<<16 | + (u32int)key[3]<<24; + s->r = (u32int)key[4] | + (u32int)key[5]<<8 | + (u32int)key[6]<<16 | + (u32int)key[7]<<24; + s->m = 0; + s->n = 0; +} + +static void +micupdate(MICstate *s, uchar *data, ulong len) +{ + u32int l, r, m, n, e; + + l = s->l; + r = s->r; + m = s->m; + n = s->n; + e = n + len; + while(n != e){ + m >>= 8; + m |= (u32int)*data++ << 24; + if(++n & 3) + continue; + l ^= m; + r ^= (l << 17) | (l >> 15); + l += r; + r ^= ((l & 0x00FF00FFUL)<<8) | ((l & 0xFF00FF00UL)>>8); + l += r; + r ^= (l << 3) | (l >> 29); + l += r; + r ^= (l >> 2) | (l << 30); + l += r; + } + s->l = l; + s->r = r; + s->m = m; + s->n = n; +} + +static void +micfinish(MICstate *s, uchar mic[8]) +{ + static uchar pad[8] = { 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + + micupdate(s, pad, sizeof(pad)); + + mic[0] = s->l; + mic[1] = s->l>>8; + mic[2] = s->l>>16; + mic[3] = s->l>>24; + mic[4] = s->r; + mic[5] = s->r>>8; + mic[6] = s->r>>16; + mic[7] = s->r>>24; +} + +static uchar pad4[4] = { 0x00, 0x00, 0x00, 0x00, }; + +static void +tkipencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) +{ + u16int tk[8], p1k[5]; + uchar seed[16]; + RC4state rs; + MICstate ms; + ulong crc; + + micsetup(&ms, k->key+24); + micupdate(&ms, dstaddr(w), Eaddrlen); + micupdate(&ms, srcaddr(w), Eaddrlen); + micupdate(&ms, pad4, 4); + micupdate(&ms, b->rp, BLEN(b)); + micfinish(&ms, b->wp); + b->wp += 8; + + crc = ethercrc(b->rp, BLEN(b)); + crc = ~crc; + b->wp[0] = crc; + b->wp[1] = crc>>8; + b->wp[2] = crc>>16; + b->wp[3] = crc>>24; + b->wp += 4; + + tkipk2tk(k->key, tk); + tkipphase1(tsc >> 16, w->a2, tk, p1k); + tkipphase2(tsc & 0xFFFF, p1k, tk, seed); + setupRC4state(&rs, seed, sizeof(seed)); + rc4(&rs, b->rp, BLEN(b)); +} + +static int +tkipdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) +{ + uchar seed[16], mic[8]; + u16int tk[8], p1k[5]; + RC4state rs; + MICstate ms; + ulong crc; + + if(BLEN(b) < 8+4) + return -1; + + tkipk2tk(k->key, tk); + tkipphase1(tsc >> 16, w->a2, tk, p1k); + tkipphase2(tsc & 0xFFFF, p1k, tk, seed); + setupRC4state(&rs, seed, sizeof(seed)); + rc4(&rs, b->rp, BLEN(b)); + + b->wp -= 4; + crc = (ulong)b->wp[0] | + (ulong)b->wp[1]<<8 | + (ulong)b->wp[2]<<16 | + (ulong)b->wp[3]<<24; + crc = ~crc; + if(ethercrc(b->rp, BLEN(b)) != crc) + return -1; + + b->wp -= 8; + micsetup(&ms, k->key+16); + micupdate(&ms, dstaddr(w), Eaddrlen); + micupdate(&ms, srcaddr(w), Eaddrlen); + micupdate(&ms, pad4, 4); + micupdate(&ms, b->rp, BLEN(b)); + micfinish(&ms, mic); + + return memcmp(b->wp, mic, 8) != 0; +} + +static uchar* +putbe(uchar *p, int L, uint v) +{ + while(--L >= 0) + *p++ = (v >> L*8) & 0xFF; + return p; +} + +static void +xblock(int L, int M, uchar *N, uchar *a, int la, int lm, uchar t[16], AESstate *s) +{ + uchar l[8], *p, *x, *e; + + assert(M >= 4 && M <= 16); + assert(L >= 2 && L <= 4); + + t[0] = ((la > 0)<<6) | ((M-2)/2)<<3 | (L-1); /* flags */ + memmove(&t[1], N, 15-L); + putbe(&t[16-L], L, lm); + aes_encrypt(s->ekey, s->rounds, t, t); + + if(la > 0){ + assert(la < 0xFF00); + for(p = l, e = putbe(l, 2, la), x = t; p < e; x++, p++) + *x ^= *p; + for(e = a + la; a < e; x = t){ + for(; a < e && x < &t[16]; x++, a++) + *x ^= *a; + aes_encrypt(s->ekey, s->rounds, t, t); + } + } +} + +static uchar* +sblock(int L, uchar *N, uint i, uchar b[16], AESstate *s) +{ + b[0] = L-1; /* flags */ + memmove(&b[1], N, 15-L); + putbe(&b[16-L], L, i); + aes_encrypt(s->ekey, s->rounds, b, b); + return b; +}; + +static void +aesCCMencrypt(int L, int M, uchar *N /* N[15-L] */, + uchar *a /* a[la] */, int la, + uchar *m /* m[lm+M] */, int lm, + AESstate *s) +{ + uchar t[16], b[16], *p, *x; + uint i; + + xblock(L, M, N, a, la, lm, t, s); + + for(i = 1; lm >= 16; i++, m += 16, lm -= 16){ + sblock(L, N, i, b, s); + + *((u32int*)&t[0]) ^= *((u32int*)&m[0]); + *((u32int*)&m[0]) ^= *((u32int*)&b[0]); + *((u32int*)&t[4]) ^= *((u32int*)&m[4]); + *((u32int*)&m[4]) ^= *((u32int*)&b[4]); + *((u32int*)&t[8]) ^= *((u32int*)&m[8]); + *((u32int*)&m[8]) ^= *((u32int*)&b[8]); + *((u32int*)&t[12]) ^= *((u32int*)&m[12]); + *((u32int*)&m[12]) ^= *((u32int*)&b[12]); + + aes_encrypt(s->ekey, s->rounds, t, t); + } + if(lm > 0){ + for(p = sblock(L, N, i, b, s), x = t; p < &b[lm]; x++, m++, p++){ + *x ^= *m; + *m ^= *p; + } + aes_encrypt(s->ekey, s->rounds, t, t); + } + + for(p = sblock(L, N, 0, b, s), x = t; p < &b[M]; x++, p++) + *x ^= *p; + + memmove(m, t, M); +} + +static int +aesCCMdecrypt(int L, int M, uchar *N /* N[15-L] */, + uchar *a /* a[la] */, int la, + uchar *m /* m[lm+M] */, int lm, + AESstate *s) +{ + uchar t[16], b[16], *p, *x; + uint i; + + xblock(L, M, N, a, la, lm, t, s); + + for(i = 1; lm >= 16; i++, m += 16, lm -= 16){ + sblock(L, N, i, b, s); + + *((u32int*)&m[0]) ^= *((u32int*)&b[0]); + *((u32int*)&t[0]) ^= *((u32int*)&m[0]); + *((u32int*)&m[4]) ^= *((u32int*)&b[4]); + *((u32int*)&t[4]) ^= *((u32int*)&m[4]); + *((u32int*)&m[8]) ^= *((u32int*)&b[8]); + *((u32int*)&t[8]) ^= *((u32int*)&m[8]); + *((u32int*)&m[12]) ^= *((u32int*)&b[12]); + *((u32int*)&t[12]) ^= *((u32int*)&m[12]); + + aes_encrypt(s->ekey, s->rounds, t, t); + } + if(lm > 0){ + for(p = sblock(L, N, i, b, s), x = t; p < &b[lm]; x++, m++, p++){ + *m ^= *p; + *x ^= *m; + } + aes_encrypt(s->ekey, s->rounds, t, t); + } + + for(p = sblock(L, N, 0, b, s), x = t; p < &b[M]; x++, p++) + *x ^= *p; + + return memcmp(m, t, M) != 0; +} + +static int +setupCCMP(Wkey *k, Wifipkt *w, uvlong tsc, uchar nonce[13], uchar auth[32], AESstate *as) +{ + uchar *p; + + setupAESstate(as, k->key, k->len, nil); + + nonce[0] = ((w->fc[0] & 0x0c) == 0x00) << 4; + memmove(&nonce[1], w->a2, Eaddrlen); + nonce[7] = tsc >> 40; + nonce[8] = tsc >> 32; + nonce[9] = tsc >> 24; + nonce[10] = tsc >> 16; + nonce[11] = tsc >> 8; + nonce[12] = tsc; + + p = auth; + *p++ = (w->fc[0] & (((w->fc[0] & 0x0c) == 0x08) ? 0x0f : 0xff)); + *p++ = (w->fc[1] & ~0x38) | 0x40; + memmove(p, w->a1, Eaddrlen); p += Eaddrlen; + memmove(p, w->a2, Eaddrlen); p += Eaddrlen; + memmove(p, w->a3, Eaddrlen); p += Eaddrlen; + *p++ = w->seq[0] & 0x0f; + *p++ = 0; + if((w->fc[1] & 3) == 0x03) { + memmove(p, w->a4, Eaddrlen); + p += Eaddrlen; + } + + return p - auth; +} + +static void +ccmpencrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) +{ + uchar auth[32], nonce[13]; + AESstate as; + + aesCCMencrypt(2, 8, nonce, auth, + setupCCMP(k, w, tsc, nonce, auth, &as), + b->rp, BLEN(b), &as); + b->wp += 8; +} + +static int +ccmpdecrypt(Wkey *k, Wifipkt *w, Block *b, uvlong tsc) +{ + uchar auth[32], nonce[13]; + AESstate as; + + if(BLEN(b) < 8) + return -1; + + b->wp -= 8; + return aesCCMdecrypt(2, 8, nonce, auth, + setupCCMP(k, w, tsc, nonce, auth, &as), + b->rp, BLEN(b), &as); +} --- /sys/src/9/pc/wifi.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/pc/wifi.h Tue Apr 22 00:00:00 2014 @@ -0,0 +1,91 @@ +typedef struct Wkey Wkey; +typedef struct Wnode Wnode; +typedef struct Wifi Wifi; +typedef struct Wifipkt Wifipkt; + +enum { + Essidlen = 32, +}; + +/* cipher */ +enum { + TKIP = 1, + CCMP = 2, +}; + +struct Wkey +{ + int cipher; + int len; + uchar key[32]; + uvlong tsc; +}; + +struct Wnode +{ + uchar bssid[Eaddrlen]; + char ssid[Essidlen+2]; + + char *status; + + int rsnelen; + uchar rsne[258]; + Wkey txkey[1]; + Wkey rxkey[5]; + + int aid; /* association id */ + ulong lastsend; + ulong lastseen; + + uchar *minrate; /* pointers into wifi->rates */ + uchar *maxrate; + + /* stuff from beacon */ + int ival; + int cap; + int channel; + int brsnelen; + uchar brsne[258]; +}; + +struct Wifi +{ + Ether *ether; + + int debug; + + Queue *iq; + ulong watchdog; + Ref txseq; + void (*transmit)(Wifi*, Wnode*, Block*); + + /* for searching */ + uchar bssid[Eaddrlen]; + char essid[Essidlen+2]; + + /* supported data rates by hardware */ + uchar *rates; + + /* effective base station */ + Wnode *bss; + + Wnode node[32]; +}; + +struct Wifipkt +{ + uchar fc[2]; + uchar dur[2]; + uchar a1[Eaddrlen]; + uchar a2[Eaddrlen]; + uchar a3[Eaddrlen]; + uchar seq[2]; + uchar a4[Eaddrlen]; +}; + +Wifi *wifiattach(Ether *ether, void (*transmit)(Wifi*, Wnode*, Block*)); +void wifiiq(Wifi*, Block*); +int wifihdrlen(Wifipkt*); + +long wifistat(Wifi*, void*, long, ulong); +long wifictl(Wifi*, void*, long);