X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=sys%2Fsrc%2F9%2Fpc%2Fetheriwl.c;h=809532e12d7def065de4f8c029331fb40338fa66;hb=5a15acc7f0cbbcddf80e445cf1721cba6de31d4b;hp=9bf90ef07d50adeeef21c9f8a1f02a6320e02c5a;hpb=0e9a80d129306202e31e9ed253b75b9b06703a86;p=plan9front.git diff --git a/sys/src/9/pc/etheriwl.c b/sys/src/9/pc/etheriwl.c index 9bf90ef07..809532e12 100644 --- a/sys/src/9/pc/etheriwl.c +++ b/sys/src/9/pc/etheriwl.c @@ -1,9 +1,10 @@ /* * 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. + * Written without any documentation but Damien Bergamini's + * iwn(4) and Stefan Sperling's iwm(4) OpenBSD driver sources. + * Requires Intel firmware to be present in /lib/firmware/iw[nm]-* + * on attach. */ #include "u.h" @@ -12,37 +13,61 @@ #include "dat.h" #include "fns.h" #include "io.h" +#include "../port/pci.h" #include "../port/error.h" #include "../port/netif.h" - -#include "etherif.h" -#include "wifi.h" +#include "../port/etherif.h" +#include "../port/wifi.h" enum { + MaxQueue = 24*1024, /* total buffer is 2*MaxQueue: 48k at 22Mbit ≅ 20ms */ + Ntxlog = 8, Ntx = 1<> 32); +} + static u32int memread(Ctlr *ctlr, uint off) { @@ -491,20 +786,121 @@ 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); -}; + switch(len){ + case 2+2 + 1+1+2+1+1 + 1+1 + 1+1 + 2 + 4+4+4+4+4+4 + 4: + case 2+2 + 1+1+2+1+1 + 1+1 + 1+1 + 2 + 4+4+4+4+4+4 + 4 + 4+4 + 1+1 + 2 + 4+4: + i->status = get16(d); d += 2; + i->flags = get16(d); d += 2; + + i->minor = *d++; + i->major = *d++; + d += 2; // id + d++; // api minor + d++; // api major + i->subtype = *d++; + i->type = *d++; + d++; // mac + d++; // opt + d += 2; // reserved2 + + i->tstamp = get32(d); d += 4; + i->errptr = get32(d); d += 4; + i->logptr = get32(d); d += 4; + i->regptr = get32(d); d += 4; + d += 4; // dbgm_config_ptr + d += 4; // alive counter ptr + + i->scdptr = get32(d); d += 4; + + if(len < 1+1+2+1+1+1+1+1+1+2+4+4+4+4+4 + 4+4+4+1+1+2+4+4) + break; + + d += 4; // fwrd addr + d += 4; // fwrd size + + i->umac.minor = *d++; + i->umac.major = *d++; + d++; // id + d += 2; + i->umac.errptr = get32(d); d += 4; + i->umac.logptr = get32(d); d += 4; + + i->valid = (i->status == 0xcafe); + break; + + case 2+2 + 4+4 + 1+1 + 1+1 + 4+4+4+4+4+4 + 4 + 4+4 + 4+4+4+4: + i->status = get16(d); d += 2; + i->flags = get16(d); d += 2; + + i->minor = get32(d); + d += 4; + i->major = get32(d); + d += 4; + i->subtype = *d++; + i->type = *d++; + d++; // mac + d++; // opt + + i->tstamp = get32(d); d += 4; + i->errptr = get32(d); d += 4; + i->logptr = get32(d); d += 4; + i->regptr = get32(d); d += 4; + d += 4; // dbgm_config_ptr + d += 4; // alive counter ptr + + i->scdptr = get32(d); + d += 4; + + d += 4; // fwrd addr + d += 4; // fwrd size + + i->umac.minor = get32(d); d += 4; + i->umac.major = get32(d); d += 4; + i->umac.errptr = get32(d); d += 4; + i->umac.logptr = get32(d); d += 4; + + i->valid = (i->status == 0xcafe); + break; + + default: + if(len < 32) + break; + i->minor = *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 = 1; + } + USED(d); +} + +static void +printfwinfo(Ctlr *ctlr) +{ + FWInfo *i = &ctlr->fwinfo; + + print("fwinfo: status=%.4ux flags=%.4ux\n", + i->status, i->flags); + + print("fwinfo: ver %ud.%ud type %ud.%ud\n", + i->major, i->minor, i->type, i->subtype); + print("fwinfo: scdptr=%.8ux\n", i->scdptr); + print("fwinfo: regptr=%.8ux\n", i->regptr); + print("fwinfo: logptr=%.8ux\n", i->logptr); + print("fwinfo: errptr=%.8ux\n", i->errptr); + + print("fwinfo: ts=%.8ux\n", i->tstamp); + + print("fwinfo: umac ver %ud.%ud\n", i->umac.major, i->umac.minor); + print("fwinfo: umac errptr %.8ux\n", i->umac.errptr); + print("fwinfo: umac logptr %.8ux\n", i->umac.logptr); +} static void dumpctlr(Ctlr *ctlr) @@ -513,18 +909,29 @@ dumpctlr(Ctlr *ctlr) int i; print("lastcmd: %ud (0x%ux)\n", ctlr->tx[4].lastcmd, ctlr->tx[4].lastcmd); - if(ctlr->fwinfo.errptr == 0){ + + if(!ctlr->fwinfo.valid || 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]); + + if(ctlr->family >= 7000){ + print( "error:\tid %ux, trm_hw_status %.8ux %.8ux,\n" + "\tbranchlink2 %.8ux, interruptlink %.8ux %.8ux,\n" + "\terrordata %.8ux %.8ux %.8ux\n", + dump[1], dump[2], dump[3], + dump[4], dump[5], dump[6], + dump[7], dump[8], dump[9]); + } else { + 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* @@ -588,9 +995,14 @@ handover(Ctlr *ctlr) csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | NicReady); for(i=0; i<5; i++){ if(csr32r(ctlr, Cfg) & NicReady) - return 0; + goto Ready; delay(10); } + if(ctlr->family >= 7000){ + csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) | (1<<31)); + delay(1); + } + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | Prepare); for(i=0; i<15000; i++){ if((csr32r(ctlr, Cfg) & PrepareDone) == 0) @@ -599,13 +1011,18 @@ handover(Ctlr *ctlr) } 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; + goto Ready; delay(10); } return "handover: timeout"; +Ready: + if(ctlr->family >= 7000) + csr32w(ctlr, MboxSet, csr32r(ctlr, MboxSet) | MboxSetOsAlive); + return nil; } static char* @@ -629,8 +1046,17 @@ poweron(Ctlr *ctlr) int capoff; char *err; - /* Disable L0s exit timer (NMI bug workaround). */ - csr32w(ctlr, Giochicken, csr32r(ctlr, Giochicken) | DisL0Stimer); + + if(ctlr->family >= 7000){ + /* Reset entire device */ + csr32w(ctlr, Reset, (1<<7)); + delay(5); + } + + if(ctlr->family < 8000){ + /* 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); @@ -650,27 +1076,56 @@ poweron(Ctlr *ctlr) csr32w(ctlr, Gio, csr32r(ctlr, Gio) & ~EnaL0S); } - if(ctlr->type != Type4965 && ctlr->type <= Type1000) - csr32w(ctlr, AnaPll, csr32r(ctlr, AnaPll) | 0x00880300); + if(ctlr->family < 7000){ + 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; + if(ctlr->mqrx){ + /* Newer cards default to MSIX? */ + if((err = niclock(ctlr)) != nil) + return err; + prphwrite(ctlr, UregChick, UregChickMsiEnable); + nicunlock(ctlr); + } - /* Enable DMA and BSM (Bootstrap State Machine). */ - if(ctlr->type == Type4965) - prphwrite(ctlr, ApmgClkEna, DmaClkRqt | BsmClkRqt); - else - prphwrite(ctlr, ApmgClkEna, DmaClkRqt); - delay(20); + /* Enable the oscillator to count wake up time for L1 exit. (weird W/A) */ + if(ctlr->type == Type7260){ + if((err = niclock(ctlr)) != nil) + return err; - /* Disable L1-Active. */ - prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11)); + prphread(ctlr, OscClk); + prphread(ctlr, OscClk); + delay(20); - nicunlock(ctlr); + prphwrite(ctlr, OscClk, prphread(ctlr, OscClk) | OscClkCtrl); + + prphread(ctlr, OscClk); + prphread(ctlr, OscClk); + + nicunlock(ctlr); + } + + if(ctlr->family < 8000){ + 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; @@ -691,14 +1146,14 @@ poweroff(Ctlr *ctlr) csr32w(ctlr, FhIsr, ~0); /* Stop scheduler */ - if(ctlr->type != Type4965) - prphwrite(ctlr, SchedTxFact5000, 0); + if(ctlr->family >= 7000 || ctlr->type != Type4965) + prphwrite(ctlr, SchedTxFact, 0); else prphwrite(ctlr, SchedTxFact4965, 0); /* Stop TX ring */ if(niclock(ctlr) == nil){ - for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--){ + for(i = 0; i < ctlr->ndma; i++){ csr32w(ctlr, FhTxConfig + i*32, 0); for(j = 0; j < 200; j++){ if(csr32r(ctlr, FhTxStatus) & (0x10000<mqrx){ + prphwrite(ctlr, RfhDmaCfg, 0); + for(j = 0; j < 200; j++){ + if(prphread(ctlr, RfhGenStatus) & RfhGenStatusDmaIdle) + break; + delay(10); + } + } else { + csr32w(ctlr, FhRxConfig, 0); + for(j = 0; j < 200; j++){ + if(csr32r(ctlr, FhRxStatus) & 0x1000000) + break; + delay(10); + } } nicunlock(ctlr); } - /* Disable DMA */ - if(niclock(ctlr) == nil){ - prphwrite(ctlr, ApmgClkDis, DmaClkRqt); - nicunlock(ctlr); + if(ctlr->family <= 7000){ + /* Disable DMA */ + if(niclock(ctlr) == nil){ + prphwrite(ctlr, ApmgClkDis, DmaClkRqt); + nicunlock(ctlr); + } + delay(5); + } + + if(ctlr->family >= 7000){ + csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) | (1<<31)); + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | Prepare|EnablePme); + delay(1); + csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) & ~(1<<31)); + delay(5); } - delay(5); /* Stop busmaster DMA activity. */ csr32w(ctlr, Reset, csr32r(ctlr, Reset) | (1<<9)); @@ -814,10 +1288,42 @@ iwlinit(Ether *edev) uint u, caloff, regoff; ctlr = edev->ctlr; + + /* Clear device-specific "PCI retry timeout" register (41h). */ + if(pcicfgr8(ctlr->pdev, 0x41) != 0) + pcicfgw8(ctlr->pdev, 0x41, 0); + + /* Clear interrupt disable bit. Hardware bug workaround. */ + if(ctlr->pdev->pcr & 0x400){ + ctlr->pdev->pcr &= ~0x400; + pcicfgw16(ctlr->pdev, PciPCR, ctlr->pdev->pcr); + } + + ctlr->type = csr32r(ctlr, Rev); + if(ctlr->family >= 8000){ + ctlr->type &= 0xFFFF; + ctlr->dash = 0; + ctlr->step = ctlr->type & 15, ctlr->type >>= 4; + } else { + ctlr->type &= 0x1FF; + ctlr->dash = ctlr->type & 3, ctlr->type >>= 2; + ctlr->step = ctlr->type & 3, ctlr->type >>= 2; + if(fwname[ctlr->type] == nil){ + print("iwl: unsupported controller type %d\n", ctlr->type); + return -1; + } + } + if((err = handover(ctlr)) != nil) goto Err; + + /* >= 7000 family needs firmware loaded to access NVM */ + if(ctlr->family >= 7000) + return 0; + if((err = poweron(ctlr)) != nil) goto Err; + if((csr32r(ctlr, EepromGp) & 0x7) == 0){ err = "bad rom signature"; goto Err; @@ -856,6 +1362,18 @@ iwlinit(Ether *edev) ctlr->eeprom.version = b[0]; ctlr->eeprom.type = b[1]; ctlr->eeprom.volt = get16(b+2); + + ctlr->eeprom.temp = 0; + ctlr->eeprom.rawtemp = 0; + if(ctlr->type == Type2030 || ctlr->type == Type2000){ + if((err = eepromread(ctlr, b, 2, caloff + 0x12a)) != nil) + goto Err2; + ctlr->eeprom.temp = get16(b); + if((err = eepromread(ctlr, b, 2, caloff + 0x12b)) != nil) + goto Err2; + ctlr->eeprom.rawtemp = get16(b); + } + if(ctlr->type != Type4965 && ctlr->type != Type5150){ if((err = eepromread(ctlr, b, 4, caloff + 0x128)) != nil) goto Err2; @@ -892,6 +1410,7 @@ crackfw(FWImage *i, uchar *data, uint size, int alt) { uchar *p, *e; FWSect *s; + uint t, l; memset(i, 0, sizeof(*i)); if(size < 4){ @@ -918,28 +1437,106 @@ Tooshort: altmask |= (uvlong)get32(p) << 32; p += 4; while(alt > 0 && (altmask & (1ULL< e) + goto Tooshort; - if((p + 2+2+4) > e) + t = get32(p), p += 4; + l = get32(p), p += 4; + if(p + l > 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; + + if((t >> 16) != 0 && (t >> 16) != alt) + continue; + + switch(t & 0xFFFF){ + case 1: + s = &i->main.text; + if(i->main.nsect < 1) + i->main.nsect = 1; + s->addr = 0x00000000; + goto Sect; + case 2: + s = &i->main.data; + if(i->main.nsect < 2) + i->main.nsect = 2; + s->addr = 0x00800000; + goto Sect; + case 3: + s = &i->init.text; + if(i->init.nsect < 1) + i->init.nsect = 1; + s->addr = 0x00000000; + goto Sect; + case 4: + s = &i->init.data; + if(i->init.nsect < 2) + i->init.nsect = 2; + s->addr = 0x00800000; + goto Sect; + case 5: + s = &i->boot.text; + s->addr = 0x00000000; + goto Sect; + case 19: + if(i->main.nsect >= nelem(i->main.sect)) + return "too many main sections"; + s = &i->main.sect[i->main.nsect++]; + goto Chunk; + case 20: + if(i->init.nsect >= nelem(i->init.sect)) + return "too many init sections"; + s = &i->init.sect[i->init.nsect++]; + Chunk: + if(l < 4) + goto Tooshort; + s->addr = get32(p); + p += 4, l -= 4; + Sect: + s->size = l; + s->data = p; + break; + case 22: + if(l < 12) + goto Tooshort; + switch(get32(p)){ + case 0: + i->main.defcalib.flowmask = get32(p+4); + i->main.defcalib.eventmask = get32(p+8); + break; + case 1: + i->init.defcalib.flowmask = get32(p+4); + i->init.defcalib.eventmask = get32(p+8); + break; + } + break; + case 23: + if(l < 4) + goto Tooshort; + i->physku = get32(p); + break; + case 29: + if(l < 8) + goto Tooshort; + t = get32(p); + if(t >= nelem(i->api)) + goto Tooshort; + i->api[t] = get32(p+4); + break; + case 30: + if(l < 8) + goto Tooshort; + t = get32(p); + if(t >= nelem(i->capa)) + goto Tooshort; + i->capa[t] = get32(p+4); + break; + case 32: + if(l < 4) + goto Tooshort; + i->pagedmemsize = get32(p) & -FWPagesize; + break; } - 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) @@ -949,11 +1546,13 @@ Tooshort: } 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; @@ -961,6 +1560,13 @@ Tooshort: i->boot.text.data = p; p += i->boot.text.size; if(p > e) goto Tooshort; + + i->main.nsect = 2; + i->init.nsect = 2; + i->main.text.addr = 0x00000000; + i->main.data.addr = 0x00800000; + i->init.text.addr = 0x00000000; + i->init.data.addr = 0x00800000; } return 0; } @@ -1014,75 +1620,116 @@ readfirmware(char *name) return fw; } -typedef struct Irqwait Irqwait; -struct Irqwait { - Ctlr *ctlr; - u32int mask; -}; static int gotirq(void *arg) { - Irqwait *w; - Ctlr *ctlr; - - w = arg; - ctlr = w->ctlr; - ctlr->wait.r = ctlr->wait.m & w->mask; - if(ctlr->wait.r){ - ctlr->wait.m &= ~ctlr->wait.r; - return 1; - } - ctlr->wait.w = w->mask; - return 0; + Ctlr *ctlr = arg; + return (ctlr->wait.m & ctlr->wait.w) != 0; } static u32int irqwait(Ctlr *ctlr, u32int mask, int timeout) { - Irqwait w; + u32int r; - w.ctlr = ctlr; - w.mask = mask; - tsleep(&ctlr->wait, gotirq, &w, timeout); - ctlr->wait.w = 0; - return ctlr->wait.r & mask; + 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) +rbplant(Ctlr *ctlr, uint i) { Block *b; - b = iallocb(Rbufsize + 256); + assert(i < Nrx); + + b = iallocb(Rbufsize*2); if(b == nil) return -1; - b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256); + b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, Rbufsize); memset(b->rp, 0, Rdscsize); + ctlr->rx.b[i] = b; - ctlr->rx.p[i] = PCIWADDR(b->rp) >> 8; + + if(ctlr->mqrx) + put64(ctlr->rx.p + (i<<3), PCIWADDR(b->rp)); + else + put32(ctlr->rx.p + (i<<2), PCIWADDR(b->rp) >> 8); + return 0; } static char* -initring(Ctlr *ctlr) +initmem(Ctlr *ctlr) { RXQ *rx; TXQ *tx; int i, q; + if(ctlr->fw->pagedmemsize > 0){ + ctlr->fwmem.npage = ctlr->fw->pagedmemsize >> FWPageshift; + ctlr->fwmem.nblock = ctlr->fwmem.npage >> FWBlockshift; + if(ctlr->fwmem.nblock >= nelem(ctlr->fwmem.block)-1) + return "paged memory size too big"; + for(i = 0; i < ctlr->fwmem.nblock; i++) + ctlr->fwmem.block[i].size = FWBlocksize; + ctlr->fwmem.block[i].size = (ctlr->fwmem.npage % FWBlockpages) << FWPageshift; + if(ctlr->fwmem.block[i].size != 0) + ctlr->fwmem.nblock++; + for(i = 0; i < ctlr->fwmem.nblock; i++){ + if(ctlr->fwmem.block[i].p == nil){ + ctlr->fwmem.block[i].p = mallocalign(ctlr->fwmem.block[i].size, FWPagesize, 0, 0); + if(ctlr->fwmem.block[i].p == nil) + return "no memory for firmware block"; + } + } + if(ctlr->fwmem.css == nil){ + if((ctlr->fwmem.css = mallocalign(FWPagesize, FWPagesize, 0, 0)) == nil) + return "no memory for firmware css page"; + } + } + rx = &ctlr->rx; + if(ctlr->mqrx){ + if(rx->u == nil) + rx->u = mallocalign(4 * Nrx, 4096, 0, 0); + if(rx->p == nil) + rx->p = mallocalign(8 * Nrx, 4096, 0, 0); + if(rx->u == nil || rx->p == nil) + return "no memory for rx rings"; + memset(rx->u, 0, 4 * Nrx); + memset(rx->p, 0, 8 * Nrx); + } else { + rx->u = nil; + if(rx->p == nil) + rx->p = mallocalign(4 * Nrx, 256, 0, 0); + if(rx->p == nil) + return "no memory for rx rings"; + memset(rx->p, 0, 4 * Nrx); + } + if(rx->s == nil) + rx->s = mallocalign(Rstatsize, 4096, 0, 0); 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) + if(rx->b == nil || rx->s == nil) return "no memory for rx ring"; - memset(ctlr->rx.s, 0, Rstatsize); + memset(rx->s, 0, Rstatsize); for(i=0; ip[i] = 0; if(rx->b[i] != nil){ freeb(rx->b[i]); rx->b[i] = nil; @@ -1092,131 +1739,1350 @@ initring(Ctlr *ctlr) } rx->i = 0; + ctlr->ndma = 8; + ctlr->ntxq = 20; + if(ctlr->family >= 7000) { + ctlr->ntxq = 31; + } else { + if(ctlr->type == Type4965) { + ctlr->ndma = 7; + ctlr->ntxq = 16; + } + } + + if(ctlr->sched.s == nil) + ctlr->sched.s = mallocalign((256+64)*2 * ctlr->ntxq, 4096, 0, 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)); + memset(ctlr->sched.s, 0, (256+64)*2 * ctlr->ntxq); - for(q=0; qtx); q++){ + for(q=0; q < nelem(ctlr->tx); 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); + tx->d = mallocalign(Tdscsize * Ntx, 4096, 0, 0); if(tx->c == nil) - tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0); + tx->c = mallocalign(Tcmdsize * Ntx, 4096, 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]); + freeblist(tx->b[i]); tx->b[i] = nil; } } - tx->i = 0; - tx->n = 0; - tx->lastcmd = 0; - } + 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 q, i; + + if(ctlr->power) + poweroff(ctlr); + if((err = initmem(ctlr)) != nil) + return err; + if((err = poweron(ctlr)) != nil) + return err; + + if(ctlr->family <= 7000){ + if((err = niclock(ctlr)) != nil) + return err; + prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain); + nicunlock(ctlr); + } + + if(ctlr->family >= 7000){ + u32int u; + + u = csr32r(ctlr, Cfg); + + u &= ~(RadioSi|MacSi|CfgMacDashMask|CfgMacStepMask|CfgPhyTypeMask|CfgPhyStepMask|CfgPhyDashMask); + + u |= (ctlr->step << CfgMacStepShift) & CfgMacStepMask; + u |= (ctlr->dash << CfgMacDashShift) & CfgMacDashMask; + + u |= ctlr->rfcfg.type << CfgPhyTypeShift; + u |= ctlr->rfcfg.step << CfgPhyStepShift; + u |= ctlr->rfcfg.dash << CfgPhyDashShift; + + csr32w(ctlr, Cfg, u); + + } else { + csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi); + } + + if(ctlr->family < 8000){ + if((err = niclock(ctlr)) != nil) + return err; + if(ctlr->family == 7000 || ctlr->type != Type4965) + prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | EarlyPwroffDis); + nicunlock(ctlr); + } + if(ctlr->family < 7000){ + if((err = niclock(ctlr)) != nil) + return err; + 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)); + } + 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); + if(ctlr->type == Type2030 || ctlr->type == Type2000) + csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrvRadioIqInvert); + nicunlock(ctlr); + } + + if((err = niclock(ctlr)) != nil) + return err; + + if(ctlr->mqrx){ + /* Stop RX DMA. */ + prphwrite(ctlr, RfhDmaCfg, 0); + /* Disable RX used and free queue operation. */ + prphwrite(ctlr, RfhRxqActive, 0); + + prphwrite64(ctlr, RfhQ0SttsBase, PCIWADDR(ctlr->rx.s)); + prphwrite64(ctlr, RfhQ0FreeBase, PCIWADDR(ctlr->rx.p)); + prphwrite64(ctlr, RfhQ0UsedBase, PCIWADDR(ctlr->rx.u)); + + prphwrite(ctlr, RfhQ0FreeWptr, 0); + prphwrite(ctlr, RfhQ0FreeRptr, 0); + prphwrite(ctlr, RfhQ0UsedWptr, 0); + + /* Enable RX DMA */ + prphwrite(ctlr, RfhDmaCfg, + RfhDmaEnable | + RfhDmaDropTooLarge | + ((Rbufsize/1024) << RfhDma1KSizeShift) | + (3 << RfhDmaMinRbSizeShift) | + (Nrxlog << RfhDmaNrbdShift)); + + /* Enable RX DMA snooping. */ + prphwrite(ctlr, RfhGenCfg, + RfhGenServiceDmaSnoop | + RfhGenRfhDmaSnoop | + RfhGenRbChunkSize128); + + /* Enable Q0 */ + prphwrite(ctlr, RfhRxqActive, (1 << 16) | 1); + delay(1); + + csr32w(ctlr, FhRxQ0Wptr, (Nrx-1) & ~7); + delay(1); + } else { + 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); + } + + for(i = 0; i < ctlr->ndma; i++) + csr32w(ctlr, FhTxConfig + i*32, 0); + + if(ctlr->family >= 7000 || ctlr->type != Type4965) + prphwrite(ctlr, SchedTxFact, 0); + else + prphwrite(ctlr, SchedTxFact4965, 0); + + if(ctlr->family >= 7000){ + prphwrite(ctlr, SchedEnCtrl, 0); + prphwrite(ctlr, SchedGpCtrl, prphread(ctlr, SchedGpCtrl) + | Enable31Queues*(ctlr->ntxq == 31) + | AutoActiveMode); + for(q = 0; q < ctlr->ntxq; q++) + prphwrite(ctlr, (q<20? SchedQueueStatus: SchedQueueStatus20) + q*4, 1 << 19); + } + + csr32w(ctlr, FhKwAddr, PCIWADDR(ctlr->kwpage) >> 4); + for(q = 0; q < ctlr->ntxq; q++){ + i = q < nelem(ctlr->tx) ? q : nelem(ctlr->tx)-1; + if(q < 16) + csr32w(ctlr, FhCbbcQueue0 + q*4, PCIWADDR(ctlr->tx[i].d) >> 8); + else if(q < 20) + csr32w(ctlr, FhCbbcQueue16 + (q-16)*4, PCIWADDR(ctlr->tx[i].d) >> 8); + else + csr32w(ctlr, FhCbbcQueue20 + (q-20)*4, PCIWADDR(ctlr->tx[i].d) >> 8); + } + + if(ctlr->family >= 7000 || ctlr->type >= Type6000) + csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff); + + nicunlock(ctlr); + + csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill); + csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked); + + ctlr->systime = 0; + + ctlr->broken = 0; + ctlr->wait.m = 0; + ctlr->wait.w = 0; + + ctlr->bcast.id = -1; + ctlr->bss.id = -1; + + ctlr->phyid = -1; + ctlr->macid = -1; + ctlr->bindid = -1; + ctlr->te.id = -1; + ctlr->te.active = 0; + ctlr->aid = 0; + + if(ctlr->family >= 9000) + csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | 0x4000000); + + ctlr->ie = Idefmask; + csr32w(ctlr, Imr, ctlr->ie); + csr32w(ctlr, Isr, ~0); + + csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill); + csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill); + csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill); + + return nil; +} + +static char* +sendmccupdate(Ctlr *ctlr, char *mcc) +{ + uchar c[2+1+1+4+5*4], *p; + + memset(p = c, 0, sizeof(c)); + *p++ = mcc[1]; + *p++ = mcc[0]; + *p++ = 0; + *p++ = 0; // reserved + if(1){ + p += 4; + p += 5*4; + } + return cmd(ctlr, 200, c, p - c); +} + +static char* +sendbtcoexadv(Ctlr *ctlr) +{ + static u32int btcoex3wire[12] = { + 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, + 0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa, + 0xc0004000, 0x00004000, 0xf0005000, 0xf0005000, + }; + + uchar c[Tcmdsize], *p; + char *err; + int i; + + /* set BT config */ + memset(c, 0, sizeof(c)); + p = c; + + if(ctlr->family >= 7000){ + put32(p, 3); + p += 4; + put32(p, (1<<4)); + p += 4; + } else if(ctlr->type == Type2030){ + *p++ = 145; /* flags */ + p++; /* lead time */ + *p++ = 5; /* max kill */ + *p++ = 1; /* bt3 t7 timer */ + put32(p, 0xffff0000); /* kill ack */ + p += 4; + put32(p, 0xffff0000); /* kill cts */ + p += 4; + *p++ = 2; /* sample time */ + *p++ = 0xc; /* bt3 t2 timer */ + p += 2; /* bt4 reaction */ + for (i = 0; i < nelem(btcoex3wire); i++){ + put32(p, btcoex3wire[i]); + p += 4; + } + p += 2; /* bt4 decision */ + put16(p, 0xff); /* valid */ + p += 2; + put32(p, 0xf0); /* prio boost */ + p += 4; + p++; /* reserved */ + p++; /* tx prio boost */ + p += 2; /* rx prio boost */ + } + if((err = cmd(ctlr, 155, c, p-c)) != nil) + return err; + + if(ctlr->family >= 7000) + return nil; + + /* set BT priority */ + memset(c, 0, sizeof(c)); + p = c; + + *p++ = 0x6; /* init1 */ + *p++ = 0x7; /* init2 */ + *p++ = 0x2; /* periodic low1 */ + *p++ = 0x3; /* periodic low2 */ + *p++ = 0x4; /* periodic high1 */ + *p++ = 0x5; /* periodic high2 */ + *p++ = 0x6; /* dtim */ + *p++ = 0x8; /* scan52 */ + *p++ = 0xa; /* scan24 */ + p += 7; /* reserved */ + if((err = cmd(ctlr, 204, c, p-c)) != nil) + return err; + + /* force BT state machine change */ + memset(c, 0, sizeof(c)); + p = c; + + *p++ = 1; /* open */ + *p++ = 1; /* type */ + p += 2; /* reserved */ + if((err = cmd(ctlr, 205, c, p-c)) != nil) + return err; + + c[0] = 0; /* open */ + return cmd(ctlr, 205, c, p-c); +} + +static char* +sendpagingcmd(Ctlr *ctlr) +{ + uchar c[3*4 + 4 + 32*4], *p; + int i; + + p = c; + put32(p, (3<<8) | (ctlr->fwmem.npage % FWBlockpages)); + p += 4; + put32(p, FWPageshift + FWBlockshift); + p += 4; + put32(p, ctlr->fwmem.nblock); + p += 4; + + put32(p, PCIWADDR(ctlr->fwmem.css) >> FWPageshift); + p += 4; + + for(i = 0; i < ctlr->fwmem.nblock; i++){ + put32(p, PCIWADDR(ctlr->fwmem.block[i].p) >> FWPageshift); + p += 4; + } + + for(; i < 32; i++){ + put32(p, 0); + p += 4; + } + + return cmd(ctlr, 79 | (1<<8), c, p-c); +} + +static char* +enablepaging(Ctlr *ctlr) +{ + FWSect *sect; + int nsect; + int i, j, o, n; + + if(ctlr->fwmem.css == nil) + return nil; + + if(1){ + /* clear everything */ + memset(ctlr->fwmem.css, 0, FWPagesize); + for(i = 0; i < ctlr->fwmem.nblock; i++) + memset(ctlr->fwmem.block[i].p, 0, ctlr->fwmem.block[i].size); + } + + if(ctlr->calib.done == 0){ + sect = ctlr->fw->init.sect; + nsect = ctlr->fw->init.nsect; + } else { + sect = ctlr->fw->main.sect; + nsect = ctlr->fw->main.nsect; + } + + /* first CSS segment */ + for(i = 0; i < nsect; i++) { + if(sect[i].addr == 0xAAAABBBB){ + i++; + break; + } + } + + if(i+1 >= nsect) + return "firmware misses CSS+paging sections"; + + if(sect[i].size > FWPagesize) + return "CSS section too big"; + if(sect[i+1].size > (ctlr->fwmem.npage << FWPageshift)) + return "paged section too big"; + + memmove(ctlr->fwmem.css, sect[i].data, sect[i].size); + + for(j = 0, o = 0; o < sect[i+1].size; o += n, j++){ + n = sect[i+1].size - o; + if(n > ctlr->fwmem.block[j].size) + n = ctlr->fwmem.block[j].size; + memmove(ctlr->fwmem.block[j].p, sect[i+1].data + o, n); + } + + return sendpagingcmd(ctlr); +} + +static int +readnvmsect1(Ctlr *ctlr, int type, void *data, int len, int off) +{ + uchar c[2+2+2+2], *p; + char *err; + + p = c; + *p++ = 0; // read op + *p++ = 0; // target + put16(p, type); + p += 2; + put16(p, off); + p += 2; + put16(p, len); + p += 2; + + ctlr->nvm.off = -1; + ctlr->nvm.ret = -1; + ctlr->nvm.type = -1; + ctlr->nvm.sts = -1; + + ctlr->nvm.buf = data; + ctlr->nvm.len = len; + + if((err = cmd(ctlr, 136, c, p - c)) != nil){ + ctlr->nvm.buf = nil; + ctlr->nvm.len = 0; + print("readnvmsect: %s\n", err); + return -1; + } + + if(ctlr->nvm.ret < len) + len = ctlr->nvm.ret; + + if(ctlr->nvm.sts != 0 || ctlr->nvm.off != off || (ctlr->nvm.type & 0xFF) != type) + return -1; + + return len; +} + +static int +readnvmsect(Ctlr *ctlr, int type, void *data, int len, int off) +{ + int n, r, o; + + for(o = 0; o < len; o += n){ + r = len - o; + if(r > 256) + r = 256; + if((n = readnvmsect1(ctlr, type, (char*)data + o, r, o+off)) < 0) + return -1; + if(n < r){ + o += n; + break; + } + } + return o; +} + +static char* +readnvmconfig(Ctlr *ctlr) +{ + uchar *ea = ctlr->edev->ea; + uchar buf[8]; + uint u; + char *err; + + if(readnvmsect(ctlr, 1, buf, 8, 0) != 8) + return "can't read nvm version"; + + ctlr->nvm.version = get16(buf); + if (ctlr->family == 7000) { + u = get16(buf + 2); + + ctlr->rfcfg.type = (u >> 4) & 3; + ctlr->rfcfg.step = (u >> 2) & 3; + ctlr->rfcfg.dash = (u >> 0) & 3; + ctlr->rfcfg.pnum = (u >> 6) & 3; + + ctlr->rfcfg.txantmask = (u >> 8) & 15; + ctlr->rfcfg.rxantmask = (u >> 12) & 15; + + } else { + if(readnvmsect(ctlr, 12, buf, 8, 0) != 8) + return "can't read nvm phy config"; + + u = get32(buf); + + ctlr->rfcfg.type = (u >> 12) & 0xFFF; + ctlr->rfcfg.step = (u >> 8) & 15; + ctlr->rfcfg.dash = (u >> 4) & 15; + ctlr->rfcfg.pnum = (u >> 6) & 3; + + ctlr->rfcfg.txantmask = (u >> 24) & 15; + ctlr->rfcfg.rxantmask = (u >> 28) & 15; + } + if(ctlr->family >= 8000){ + if(readnvmsect(ctlr, 11, ea, Eaddrlen, 0x01<<1) != Eaddrlen){ + u32int a0, a1; + + if((err = niclock(ctlr)) != nil) + return err; + a0 = prphread(ctlr, 0xa03080); + a1 = prphread(ctlr, 0xa03084); + nicunlock(ctlr); + + ea[0] = a0 >> 24; + ea[1] = a0 >> 16; + ea[2] = a0 >> 8; + ea[3] = a0 >> 0; + ea[4] = a1 >> 8; + ea[5] = a1 >> 0; + } + } else { + readnvmsect(ctlr, 0, ea, Eaddrlen, 0x15<<1); + } + memmove(ctlr->edev->addr, ea, Eaddrlen); + + return nil; +} + +static char* +sendtxantconfig(Ctlr *ctlr, uint val) +{ + uchar c[4]; + + put32(c, val); + return cmd(ctlr, 152, c, 4); +} + +static char* +sendphyconfig(Ctlr *ctlr, u32int physku, u32int flowmask, u32int eventmask) +{ + uchar c[3*4]; + + put32(c+0, physku); + put32(c+4, flowmask); + put32(c+8, eventmask); + return cmd(ctlr, 106, c, 3*4); +} + +static char* +delstation(Ctlr *ctlr, Station *sta) +{ + uchar c[4], *p; + char *err; + + if(sta->id < 0) + return nil; + + memset(p = c, 0, sizeof(c)); + *p = sta->id; + + if((err = cmd(ctlr, 25, c, 4)) != nil) + return err; + + sta->id = -1; + return nil; +} + +enum { + StaTypeLink = 0, + StaTypeGeneralPurpose, + StaTypeMulticast, + StaTypeTdlsLink, + StaTypeAux, +}; + +static char* +setstation(Ctlr *ctlr, int id, int type, uchar addr[6], Station *sta) +{ + uchar c[Tcmdsize], *p; + char *err; + + memset(p = c, 0, sizeof(c)); + + *p++ = 0; /* control (1 = update) */ + p++; /* reserved */ + if(ctlr->family >= 7000){ + put16(p, 0xffff); + p += 2; + put32(p, ctlr->macid); + p += 4; + } else { + p += 2; /* reserved */ + } + + memmove(p, addr, 6); + p += 8; + + *p++ = id; /* sta id */ + + if(ctlr->family >= 7000){ + *p++ = 1 << 1; /* modify mask */ + p += 2; /* reserved */ + + put32(p, 0<<26 | 0<<28); + p += 4; /* station_flags */ + + put32(p, 3<<26 | 3<<28); + p += 4; /* station_flags_mask */ + + p++; /* add_immediate_ba_tid */ + p++; /* remove_immediate_ba_tid */ + p += 2; /* add_immediate_ba_ssn */ + p += 2; /* sleep_tx_count */ + p++; /* sleep state flags */ + + *p++ = (ctlr->fw->api[0] & (1<<30)) != 0 ? type : 0; /* station_type */ + + p += 2; /* assoc id */ + + p += 2; /* beamform flags */ + + put32(p, 1<<0); + p += 4; /* tfd_queue_mask */ + + if(1){ + p += 2; /* rx_ba_window */ + p++; /* sp_length */ + p++; /* uapsd_acs */ + } + } else { + p += 3; + 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 */ + } + + if((err = cmd(ctlr, 24, c, p - c)) != nil) + return err; + sta->id = id; + return nil; +} + +static char* +setphycontext(Ctlr *ctlr, int amr) +{ + uchar c[Tcmdsize], *p; + int phyid; + char *err; + + phyid = ctlr->phyid; + if(phyid < 0){ + if(amr == CmdRemove) + return nil; + amr = CmdAdd; + phyid = 0; + } else if(amr == CmdAdd) + amr = CmdModify; + + memset(p = c, 0, sizeof(c)); + put32(p, phyid); // id and color + p += 4; + put32(p, amr); + p += 4; + put32(p, 0); // apply time 0 = immediate + p += 4; + put32(p, 0); // tx param color ???? + p += 4; + + *p++ = (ctlr->rxflags & RFlag24Ghz) != 0; + *p++ = ctlr->channel; // channel number + *p++ = 0; // channel width (20MHz<rfcfg.txantmask); + p += 4; + put32(p, ctlr->rfcfg.rxantmask<<1 | (1<<10) | (1<<12)); + p += 4; + put32(p, 0); // acquisition_data ???? + p += 4; + put32(p, 0); // dsp_cfg_flags + p += 4; + + if((err = cmd(ctlr, 8, c, p - c)) != nil) + return err; + + if(amr == CmdRemove) + phyid = -1; + ctlr->phyid = phyid; + return nil; +} + +static u32int +reciprocal(u32int v) +{ + return v != 0 ? 0xFFFFFFFFU / v : 0; +} + +static char* +setmaccontext(Ether *edev, Ctlr *ctlr, int amr, Wnode *bss) +{ + uchar c[4+4 + 4+4 + 8+8 + 4+4+4+4+4+4+4 + 5*8 + 12*4], *p; + int macid, i; + char *err; + + macid = ctlr->macid; + if(macid < 0){ + if(amr == CmdRemove) + return nil; + amr = CmdAdd; + macid = 0; + } else if(amr == CmdAdd) + amr = CmdModify; + + memset(p = c, 0, sizeof(c)); + put32(p, macid); + p += 4; + put32(p, amr); + p += 4; + + put32(p, 5); // mac type 5 = bss + p += 4; + + put32(p, 0); // tsf id ??? + p += 4; + + memmove(p, edev->ea, 6); + p += 8; + + memmove(p, ctlr->bssid, 6); + p += 8; + + put32(p, bss == nil? 0xF : (bss->validrates & 0xF)); + p += 4; + put32(p, bss == nil? 0xFF : (bss->validrates >> 4)); + p += 4; + + put32(p, 0); // protection flags + p += 4; + + put32(p, ctlr->rxflags & RFlagShPreamble); + p += 4; + put32(p, ctlr->rxflags & RFlagShSlot); + p += 4; + put32(p, ctlr->rxfilter); + p += 4; + + put32(p, 0); // qos flags + p += 4; + + for(i = 0; i < 4; i++){ + put16(p, 0x07); // cw_min + p += 2; + put16(p, 0x0f); // cw_max + p += 2; + *p++ = 2; // aifsn + *p++ = (1<ival * (int)bss->dtimcount * 1024; + + /* is assoc */ + put32(p, bss->aid != 0); + p += 4; + + /* dtim time (system time) */ + put32(p, bss->rs + dtimoff); + p += 4; + + /* dtim tsf */ + put64(p, bss->ts + dtimoff); + p += 8; + + /* beacon interval */ + put32(p, bss->ival); + p += 4; + put32(p, reciprocal(bss->ival)); + p += 4; + + /* dtim interval */ + put32(p, bss->ival * bss->dtimperiod); + p += 4; + put32(p, reciprocal(bss->ival * bss->dtimperiod)); + p += 4; + + /* listen interval */ + put32(p, 10); + p += 4; + + /* assoc id */ + put32(p, bss->aid & 0x3fff); + p += 4; + + /* assoc beacon arrive time */ + put32(p, bss->rs); + p += 4; + } + USED(p); + + if((err = cmd(ctlr, 40, c, sizeof(c))) != nil) + return err; + + if(amr == CmdRemove) + macid = -1; + ctlr->macid = macid; + + return nil; +} + +static char* +setbindingcontext(Ctlr *ctlr, int amr) +{ + uchar c[Tcmdsize], *p; + int bindid; + char *err; + int i; + + bindid = ctlr->bindid; + if(bindid < 0){ + if(amr == CmdRemove) + return nil; + amr = CmdAdd; + bindid = 0; + } else if(amr == CmdAdd) + amr = CmdModify; + + if(ctlr->phyid < 0) + return "setbindingcontext: no phyid"; + if(ctlr->macid < 0) + return "setbindingcontext: no macid"; + + p = c; + put32(p, bindid); + p += 4; + put32(p, amr); + p += 4; + + i = 0; + if(amr != CmdRemove){ + put32(p, ctlr->macid); + p += 4; + i++; + } + for(; i < 3; i++){ + put32(p, -1); + p += 4; + } + put32(p, ctlr->phyid); + p += 4; + + if((err = cmd(ctlr, 43, c, p - c)) != nil) + return err; + + if(amr == CmdRemove) + bindid = -1; + ctlr->bindid = bindid; + return nil; +} + +static int +timeeventdone(void *arg) +{ + Ctlr *ctlr = arg; + return ctlr->te.id == -1 || ctlr->te.active != 0; +} + +static char* +settimeevent(Ctlr *ctlr, int amr, int ival) +{ + int duration, delay, timeid; + uchar c[9*4], *p; + char *err; + + switch(amr){ + case CmdAdd: + timeid = ctlr->te.id; + if(timeid == -1) + timeid = 0; + else { + if(ctlr->te.active) + return nil; + amr = CmdModify; + } + break; + default: + timeid = ctlr->te.id; + if(timeid == -1) + return nil; + break; + } + + if(ival){ + duration = ival*2; + delay = ival/2; + } else { + duration = 1024; + delay = 0; + } + + memset(p = c, 0, sizeof(c)); + put32(p, ctlr->macid); + p += 4; + put32(p, amr); + p += 4; + put32(p, timeid); + p += 4; + + put32(p, 0); // apply time + p += 4; + put32(p, delay); + p += 4; + put32(p, 0); // depends on + p += 4; + put32(p, 1); // interval + p += 4; + put32(p, duration); + p += 4; + *p++ = 1; // repeat + *p++ = 0; // max frags + put16(p, 1<<0 | 1<<1 | 1<<11); // policy + p += 2; + + ctlr->te.active = 0; + if((err = cmd(ctlr, 41, c, p - c)) != nil) + return err; + + if(amr == CmdRemove){ + ctlr->te.active = 0; + ctlr->te.id = -1; + return nil; + } + tsleep(&ctlr->te, timeeventdone, ctlr, 100); + return ctlr->te.active? nil: "timeevent did not start"; +} + + +static char* +setbindingquotas(Ctlr *ctlr, int bindid) +{ + uchar c[4*(3*4)], *p; + int i; + + i = 0; + p = c; + + if(bindid != -1){ + put32(p, bindid); + p += 4; + put32(p, 128); + p += 4; + put32(p, 0); + p += 4; + i++; + } + for(; i < 4; i++){ + put32(p, -1); + p += 4; + put32(p, 0); + p += 4; + put32(p, 0); + p += 4; + } + + return cmd(ctlr, 44, c, p - c); +} + +static char* +setmcastfilter(Ctlr *ctlr) +{ + uchar *p; + char *err; + Block *b; + + b = allocb(4+6+2); + p = b->rp; + + *p++ = 1; // filter own + *p++ = 0; // port id + *p++ = 0; // count + *p++ = 1; // pass all + + memmove(p, ctlr->bssid, 6); + p += 6; + *p++ = 0; + *p++ = 0; + + b->wp = p; + if((err = qcmd(ctlr, 4, 208, nil, 0, b)) != nil){ + freeb(b); + return err; + } + return flushq(ctlr, 4); +} + +static char* +setmacpowermode(Ctlr *ctlr) +{ + uchar c[4 + 2+2 + 4+4+4+4 + 1+1 + 2+2 + 1+1+1+1 + 1+1+1+1 + 1+1], *p; + + p = c; + put32(p, ctlr->macid); + p += 4; + + put16(p, 0); // flags + p += 2; + put16(p, 5); // keep alive seconds + p += 2; + + put32(p, 0); // rx data timeout + p += 4; + put32(p, 0); // tx data timeout + p += 4; + put32(p, 0); // rx data timeout uapsd + p += 4; + put32(p, 0); // tx data timeout uapsd + p += 4; + + *p++ = 0; // lprx rssi threshold + *p++ = 0; // skip dtim periods + + put16(p, 0); // snooze interval + p += 2; + put16(p, 0); // snooze window + p += 2; + + *p++ = 0; // snooze step + *p++ = 0; // qndp tid + *p++ = 0; // uapsd ac flags + *p++ = 0; // uapsd max sp + + *p++ = 0; // heavy tx thld packets + *p++ = 0; // heavy rx thld packets + + *p++ = 0; // heavy tx thld percentage + *p++ = 0; // heavy rx thld percentage + + *p++ = 0; // limited ps threshold + *p++ = 0; // reserved + + return cmd(ctlr, 169, c, p - c); +} + +static char* +disablebeaconfilter(Ctlr *ctlr) +{ + uchar c[11*4]; + + memset(c, 0, sizeof(c)); + return cmd(ctlr, 210, c, 11*4); +} + +static void +tttxbackoff(Ctlr *ctlr) +{ + uchar c[4]; + + put32(c, 0); + cmd(ctlr, 126, c, sizeof(c)); +} + +static char* +updatedevicepower(Ctlr *ctlr) +{ + uchar c[4]; + + memset(c, 0, sizeof(c)); + put16(c, 0<<13 | 1<<0); // cont active off, pm enable + + return cmd(ctlr, 119, c, 4); +} + +static char* +postboot7000(Ctlr *ctlr) +{ + char *err; + + if(ctlr->calib.done == 0){ + if((err = readnvmconfig(ctlr)) != nil) + return err; + } + + if((err = sendtxantconfig(ctlr, ctlr->rfcfg.txantmask)) != nil) + return err; + + if(ctlr->calib.done == 0){ + if((err = sendphyconfig(ctlr, + ctlr->fw->physku, + ctlr->fw->init.defcalib.flowmask, + ctlr->fw->init.defcalib.eventmask)) != 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 { + Block *b; + int i; + + for(i = 0; i < nelem(ctlr->calib.cmd); i++){ + if((b = ctlr->calib.cmd[i]) == nil) + continue; + b = copyblock(b, BLEN(b)); + if((qcmd(ctlr, 4, 108, nil, 0, b)) != nil){ + freeb(b); + return err; + } + if((err = flushq(ctlr, 4)) != nil) + return err; + } - 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); + if((err = sendphyconfig(ctlr, + ctlr->fw->physku, + ctlr->fw->main.defcalib.flowmask, + ctlr->fw->main.defcalib.eventmask)) != nil) + return err; + + if((err = sendbtcoexadv(ctlr)) != nil) + return err; + + /* Initialize tx backoffs to the minimum. */ + if(ctlr->family == 7000) + tttxbackoff(ctlr); + + if((err = updatedevicepower(ctlr)) != nil){ + print("can't update device power: %s\n", err); + return err; + } + if((err = sendmccupdate(ctlr, "ZZ")) != nil){ + print("can't disable beacon filter: %s\n", err); + return err; + } + if((err = disablebeaconfilter(ctlr)) != nil){ + print("can't disable beacon filter: %s\n", err); + return err; + } + } return nil; } static char* -reset(Ctlr *ctlr) +postboot6000(Ctlr *ctlr) { + uchar c[Tcmdsize]; char *err; - int i, q; - if(ctlr->power) - poweroff(ctlr); - if((err = initring(ctlr)) != nil) - return err; - if((err = poweron(ctlr)) != nil) + /* disable wimax coexistance */ + memset(c, 0, sizeof(c)); + if((err = cmd(ctlr, 90, c, 4+4*16)) != nil) return err; - if((err = niclock(ctlr)) != nil) - return err; - prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain); - nicunlock(ctlr); + /* 6235 times out if we calibrate the crystal immediately */ + tsleep(&up->sleep, return0, nil, 10); + 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; + /* for some reason 8086:4238 needs a second try */ + if(cmd(ctlr, 176, c, 8) != nil && (err = cmd(ctlr, 176, c, 8)) != nil) + return err; + } - csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi); + 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; - 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)); + /* 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}; + int q; + + /* send calibration records (runtime firmware) */ + for(q=0; qtype != Type5150 && ctlr->type != Type2030 && + ctlr->type != Type2000) + continue; + if(i == 17 && (ctlr->type >= Type6000 || ctlr->type == Type5150) && + ctlr->type != Type2030 && ctlr->type != Type2000) + continue; + + if((b = ctlr->calib.cmd[i]) == nil) + continue; + b = copyblock(b, BLEN(b)); + if((err = qcmd(ctlr, 4, 176, nil, 0, b)) != nil){ + freeb(b); + return err; + } + if((err = flushq(ctlr, 4)) != nil) + return err; + } + + /* temperature sensor offset */ + switch (ctlr->type){ + case Type6005: + 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; + break; + + case Type2030: + case Type2000: + memset(c, 0, sizeof(c)); + c[0] = 18; + c[1] = 0; + c[2] = 1; + c[3] = 1; + if(ctlr->eeprom.rawtemp != 0){ + put16(c + 4, ctlr->eeprom.temp); + put16(c + 6, ctlr->eeprom.rawtemp); + } else{ + put16(c + 4, 2700); + put16(c + 6, 2700); + } + put16(c + 8, ctlr->eeprom.volt); + if((err = cmd(ctlr, 176, c, 4+2+2+2+2)) != nil) + return err; + break; + } + + 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; + } + + if((err = sendtxantconfig(ctlr, ctlr->rfcfg.txantmask & 7)) != nil) + return err; + + if(ctlr->type == Type2030){ + if((err = sendbtcoexadv(ctlr)) != nil) + return err; + } } - nicunlock(ctlr); + return nil; +} - 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); +static void +initqueue(Ctlr *ctlr, int qid, int fifo, int chainmode, int window) +{ + csr32w(ctlr, HbusTargWptr, (qid << 8) | 0); - 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(ctlr->family >= 7000 || ctlr->type != Type4965){ + if(ctlr->family >= 7000) + prphwrite(ctlr, SchedQueueStatus + qid*4, 1 << 19); - 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); + if(chainmode) + prphwrite(ctlr, SchedQChainSel, prphread(ctlr, SchedQChainSel) | (1<type != Type4965) ? 7 : 6; i >= 0; i--) - csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna); + prphwrite(ctlr, SchedAggrSel, prphread(ctlr, SchedAggrSel) & ~(1<broken = 0; - ctlr->wait.r = 0; - ctlr->wait.w = 0; + /* Set scheduler window size and frame limit. */ + memwrite(ctlr, ctlr->sched.base + SchedCtxOff + qid*8, 0); + memwrite(ctlr, ctlr->sched.base + SchedCtxOff + qid*8 + 4, window<<16 | window); - ctlr->ie = Idefmask; - csr32w(ctlr, Imr, ctlr->ie); - csr32w(ctlr, Isr, ~0); + if(ctlr->family >= 7000){ + prphwrite(ctlr, SchedQueueStatus + qid*4, 0x017f0018 | fifo); + } else { + prphwrite(ctlr, SchedQueueStatus + qid*4, 0x00ff0018 | fifo); + } + } else { + if(chainmode) + prphwrite(ctlr, SchedQChainSel4965, prphread(ctlr, SchedQChainSel4965) | (1<type >= Type6000) - csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff); + prphwrite(ctlr, SchedQueueRdptr4965 + qid*4, 0); - return nil; + /* Set scheduler window size and frame limit. */ + memwrite(ctlr, ctlr->sched.base + SchedCtxOff4965 + qid*8, window); + memwrite(ctlr, ctlr->sched.base + SchedCtxOff4965 + qid*8 + 4, window<<16); + + prphwrite(ctlr, SchedQueueStatus4965 + qid*4, 0x0007fc01 | fifo<<1); + } } static char* @@ -1224,15 +3090,15 @@ postboot(Ctlr *ctlr) { uint ctxoff, ctxlen, dramaddr; char *err; - int i, q; + int i, f; if((err = niclock(ctlr)) != nil) return err; - if(ctlr->type != Type4965){ - dramaddr = SchedDramAddr5000; - ctxoff = SchedCtxOff5000; - ctxlen = SchedCtxLen5000; + if(ctlr->family >= 7000 || ctlr->type != Type4965){ + dramaddr = SchedDramAddr; + ctxoff = SchedCtxOff; + ctxlen = (SchedTransTblOff + 2*ctlr->ntxq) - ctxoff; } else { dramaddr = SchedDramAddr4965; ctxoff = SchedCtxOff4965; @@ -1245,166 +3111,92 @@ postboot(Ctlr *ctlr) 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); + if(ctlr->family >= 7000) { + prphwrite(ctlr, SchedEnCtrl, 0); + prphwrite(ctlr, SchedChainExtEn, 0); + } - 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); + for(i = 0; i < nelem(ctlr->tx); i++){ + if(i == 4 && ctlr->family < 7000 && ctlr->type == Type4965) + f = 4; + else { + static char qid2fifo[] = { + 3, 2, 1, 0, 7, 5, 6, + }; + f = qid2fifo[i]; } - /* Enable interrupts for all our 20 queues. */ - prphwrite(ctlr, SchedIntrMask5000, 0xfffff); + initqueue(ctlr, i, f, i != 4 && ctlr->type != Type4965, 64); + } - /* Identify TX FIFO rings (0-7). */ - prphwrite(ctlr, SchedTxFact5000, 0xff); + /* Enable interrupts for all queues. */ + if(ctlr->family >= 7000){ + prphwrite(ctlr, SchedEnCtrl, 1 << 4); + } else if(ctlr->type != Type4965) { + prphwrite(ctlr, SchedIntrMask, (1<ntxq)-1); } 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); + prphwrite(ctlr, SchedIntrMask4965, (1<ntxq)-1); } - /* 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); - } + /* Identify TX FIFO rings (0-7). */ + if(ctlr->family >= 7000 || ctlr->type != Type4965){ + prphwrite(ctlr, SchedTxFact, 0xff); + } else { + prphwrite(ctlr, SchedTxFact4965, 0xff); } - 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; - } + /* Enable DMA channels */ + for(i = 0; i < ctlr->ndma; i++) + csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna); - 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; - } + /* Auto Retry Enable */ + csr32w(ctlr, FhTxChicken, csr32r(ctlr, FhTxChicken) | 2); - 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; - } + nicunlock(ctlr); - /* set tx antenna config */ - put32(c, ctlr->rfcfg.txantmask & 7); - if((err = cmd(ctlr, 152, c, 4)) != nil) - return err; - } + if((err = enablepaging(ctlr)) != nil){ + ctlr->calib.done = 0; + return err; } + if(ctlr->family >= 7000) + return postboot7000(ctlr); + else if(ctlr->type != Type4965) + return postboot6000(ctlr); + return nil; } static char* loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size) { + enum { Maxchunk = 0x20000 }; uchar *dma; char *err; + while(size > Maxchunk){ + if((err = loadfirmware1(ctlr, dst, data, Maxchunk)) != nil) + return err; + size -= Maxchunk; + data += Maxchunk; + dst += Maxchunk; + } + dma = mallocalign(size, 16, 0, 0); if(dma == nil) return "no memory for dma"; memmove(dma, data, size); coherence(); - if((err = niclock(ctlr)) != 0){ + + if((err = niclock(ctlr)) != nil){ free(dma); return err; } + + if(ctlr->family >= 7000 && dst >= 0x40000 && dst < 0x57fff) + prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) | ExtAddr); + else + prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) & ~ExtAddr); + csr32w(ctlr, FhTxConfig + 9*32, 0); csr32w(ctlr, FhSramAddr + 9*4, dst); csr32w(ctlr, FhTfbdCtrl0 + 9*8, PCIWADDR(dma)); @@ -1415,12 +3207,74 @@ loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size) FhTxBufStatusTfbdValid); csr32w(ctlr, FhTxConfig + 9*32, FhTxConfigDmaEna | FhTxConfigCirqHostEndTfd); nicunlock(ctlr); - if(irqwait(ctlr, Ifhtx|Ierr, 5000) != Ifhtx){ - free(dma); - return "dma error / timeout"; + + err = nil; + if(irqwait(ctlr, Ifhtx|Ierr, 5000) != Ifhtx) + err = "dma error / timeout"; + + if(niclock(ctlr) == nil){ + prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) & ~ExtAddr); + nicunlock(ctlr); } + free(dma); - return 0; + + return err; +} + +static char* +setloadstatus(Ctlr *ctlr, u32int val) +{ + char *err; + + if((err = niclock(ctlr)) != nil) + return err; + csr32w(ctlr, UcodeLoadStatus, val); + nicunlock(ctlr); + return nil; +} + +static char* +loadsections(Ctlr *ctlr, FWSect *sect, int nsect) +{ + int i, num; + char *err; + + if(ctlr->family >= 8000){ + if((err = niclock(ctlr)) != nil) + return err; + prphwrite(ctlr, ReleaseCpuReset, CpuResetBit); + nicunlock(ctlr); + } + + num = 0; + for(i = 0; i < nsect; i++){ + if(sect[i].addr == 0xAAAABBBB) + break; + if(sect[i].addr == 0xFFFFCCCC) + num = 16; + else { + if(sect[i].data == nil || sect[i].size == 0) + return "bad load section"; + if((err = loadfirmware1(ctlr, sect[i].addr, sect[i].data, sect[i].size)) != nil) + return err; + num++; + } + if(ctlr->family >= 8000 + && (err = setloadstatus(ctlr, (1ULL << num)-1)) != nil) + return err; + } + return nil; +} + +static char* +ucodestart(Ctlr *ctlr) +{ + memset(&ctlr->fwinfo, 0, sizeof(ctlr->fwinfo)); + if(ctlr->family >= 8000) + return setloadstatus(ctlr, -1); + csr32w(ctlr, Reset, 0); + return nil; } static char* @@ -1435,28 +3289,38 @@ boot(Ctlr *ctlr) 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) + if((err = loadsections(ctlr, fw->init.sect, fw->init.nsect)) != nil) return err; - if((err = loadfirmware1(ctlr, 0x00800000, fw->init.data.data, fw->init.data.size)) != nil) + if((err = ucodestart(ctlr)) != nil) return err; - csr32w(ctlr, Reset, 0); - if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive) + + tsleep(&up->sleep, return0, 0, 100); + + if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive|| !ctlr->fwinfo.valid){ 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) + + if((err = loadsections(ctlr, fw->main.sect, fw->main.nsect)) != nil) return err; - if((err = loadfirmware1(ctlr, 0x00800000, fw->main.data.data, fw->main.data.size)) != nil) + if((err= ucodestart(ctlr)) != nil) return err; - csr32w(ctlr, Reset, 0); - if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive) + + tsleep(&up->sleep, return0, 0, 100); + + if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive || !ctlr->fwinfo.valid) return "main firmware boot failed"; + return postboot(ctlr); } + if(ctlr->family >= 7000) + return "wrong firmware image"; + size = ROUND(fw->init.data.size, 16) + ROUND(fw->init.text.size, 16); dma = mallocalign(size, 16, 0, 0); if(dma == nil) @@ -1512,7 +3376,7 @@ boot(Ctlr *ctlr) csr32w(ctlr, Reset, 0); if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){ free(dma); - return "init firmware boot failed"; + return "boot firmware boot failed"; } free(dma); @@ -1548,25 +3412,36 @@ static int txqready(void *arg) { TXQ *q = arg; - return q->n < Ntx; + return q->n < Ntxqmax; } static char* qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block) { + int hdrlen; + Block *bcmd; uchar *d, *c; TXQ *q; - assert(qid < nelem(ctlr->tx)); - assert(size <= Tcmdsize-4); + assert(qid < ctlr->ntxq); + + if((code & 0xFF00) != 0) + hdrlen = 8; + else + hdrlen = 4; + + if(hdrlen+size > Tcmdsize) + bcmd = allocb(hdrlen + size); + else + bcmd = nil; ilock(ctlr); q = &ctlr->tx[qid]; - while(q->n >= Ntx && !ctlr->broken){ + while(q->n >= Ntxqmax && !ctlr->broken){ iunlock(ctlr); qlock(q); if(!waserror()){ - tsleep(q, txqready, q, 10); + tsleep(q, txqready, q, 5); poperror(); } qunlock(q); @@ -1576,25 +3451,47 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block) iunlock(ctlr); return "qcmd: broken"; } + /* wake up the nic (just needed for 7k) */ + if(ctlr->family == 7000 && q->n == 0) + if(niclock(ctlr) != nil){ + iunlock(ctlr); + return "qcmd: busy"; + } q->n++; - q->lastcmd = code; + q->b[q->i] = block; - c = q->c + q->i * Tcmdsize; - d = q->d + q->i * Tdscsize; + if(bcmd != nil){ + bcmd->next = q->b[q->i]; + q->b[q->i] = bcmd; - /* build command */ - c[0] = code; - c[1] = 0; /* flags */ - c[2] = q->i; - c[3] = qid; + c = bcmd->rp; + bcmd->wp = c + hdrlen + size; + } else { + c = q->c + q->i * Tcmdsize; + } + /* build command */ + if(hdrlen == 8){ + c[0] = code; + c[1] = code>>8; /* group id */ + c[2] = q->i; + c[3] = qid; + put16(c+4, size); + c[6] = 0; + c[7] = code>>16; + } else { + c[0] = code; + c[1] = 0; /* flags */ + c[2] = q->i; + c[3] = qid; + } if(size > 0) - memmove(c+4, data, size); - - size += 4; + memmove(c+hdrlen, data, size); + size += hdrlen; /* build descriptor */ + d = q->d + q->i * Tdscsize; *d++ = 0; *d++ = 0; *d++ = 0; @@ -1604,8 +3501,9 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block) if(block != nil){ size = BLEN(block); put32(d, PCIWADDR(block->rp)); d += 4; - put16(d, size << 4); + put16(d, size << 4); d += 2; } + USED(d); coherence(); @@ -1645,6 +3543,7 @@ flushq(Ctlr *ctlr, uint qid) qunlock(q); if(ctlr->broken) return "flushq: broken"; + ctlr->broken = 1; return "flushq: timeout"; } @@ -1654,9 +3553,13 @@ 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) + + if((err = qcmd(ctlr, 4, code, data, size, nil)) != nil + || (err = flushq(ctlr, 4)) != nil){ + print("#l%d: cmd %ud: %s\n", ctlr->edev->ctlrno, code, err); return err; - return flushq(ctlr, 4); + } + return nil; } static void @@ -1664,98 +3567,93 @@ setled(Ctlr *ctlr, int which, int on, int off) { uchar c[8]; - csr32w(ctlr, Led, csr32r(ctlr, Led) & ~LedBsmCtrl); + if(ctlr->family >= 7000) + return; // TODO - memset(c, 0, sizeof(c)); + csr32w(ctlr, Led, csr32r(ctlr, Led) & ~LedBsmCtrl); put32(c, 10000); c[4] = which; c[5] = on; c[6] = off; + c[7] = 0; cmd(ctlr, 72, c, sizeof(c)); } -static void -addnode(Ctlr *ctlr, uchar id, uchar *addr) +static char* +rxoff7000(Ether *edev, Ctlr *ctlr) +{ + char *err; + int i; + + for(i = 0; i < nelem(ctlr->tx); i++) + flushq(ctlr, i); + settimeevent(ctlr, CmdRemove, 0); + + if((err = setbindingquotas(ctlr, -1)) != nil){ + print("can't disable quotas: %s\n", err); + return err; + } + if((err = delstation(ctlr, &ctlr->bss)) != nil){ + print("can't remove bss station: %s\n", err); + return err; + } + if((err = delstation(ctlr, &ctlr->bcast)) != nil){ + print("can't remove bcast station: %s\n", err); + return err; + } + if((err = setbindingcontext(ctlr, CmdRemove)) != nil){ + print("removing bindingcontext: %s\n", err); + return err; + } + if((err = setmaccontext(edev, ctlr, CmdRemove, nil)) != nil){ + print("removing maccontext: %s\n", err); + return err; + } + if((err = setphycontext(ctlr, CmdRemove)) != nil){ + print("setphycontext: %s\n", err); + return err; + } + return nil; +} + +static char* +rxon7000(Ether *edev, Ctlr *ctlr) { - uchar c[Tcmdsize], *p; + char *err; - 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 */ + if((err = setphycontext(ctlr, CmdAdd)) != nil){ + print("setphycontext: %s\n", err); + return err; } - 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); + if((err = setmaccontext(edev, ctlr, CmdAdd, nil)) != nil){ + print("setmaccontext: %s\n", err); + return err; + } + if((err = setbindingcontext(ctlr, CmdAdd)) != nil){ + print("removing bindingcontext: %s\n", err); + return err; + } + if((err = setmcastfilter(ctlr)) != nil){ + print("can't set mcast filter: %s\n", err); + return err; + } + if((err = setmacpowermode(ctlr)) != nil){ + print("can't set mac power: %s\n", err); + return err; + } + if((err = setbindingquotas(ctlr, ctlr->aid != 0 ? ctlr->bindid : -1)) != nil){ + print("can't set binding quotas: %s\n", err); + return err; + } + return nil; } -void -rxon(Ether *edev, Wnode *bss) +static char* +rxon6000(Ether *edev, Ctlr *ctlr) { 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 */ @@ -1769,9 +3667,9 @@ rxon(Ether *edev, Wnode *bss) *p++ = 0x0f; /* cck mask (not yet negotiated) */ put16(p, ctlr->aid & 0x3fff); p += 2; /* aid */ - put32(p, flags); + put32(p, ctlr->rxflags); p += 4; - put32(p, filter); + put32(p, ctlr->rxfilter); p += 4; *p++ = ctlr->channel; p++; /* reserved */ @@ -1784,61 +3682,100 @@ rxon(Ether *edev, Wnode *bss) p += 2; /* reserved */ } if((err = cmd(ctlr, 16, c, p - c)) != nil){ - print("rxon: %s\n", err); - return; + print("rxon6000: %s\n", err); + return err; } + return nil; +} - 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 char* +rxon(Ether *edev, Wnode *bss) +{ + Ctlr *ctlr = edev->ctlr; + char *err; + + if(ctlr->family >= 7000) + if((err = rxoff7000(edev, ctlr)) != nil) + goto Out; + + ctlr->rxfilter = FilterNoDecrypt | FilterMulticast | FilterBeacon; + if(ctlr->family >= 7000) + ctlr->rxfilter |= FilterNoDecryptMcast; + if(ctlr->prom) + ctlr->rxfilter |= FilterPromisc; + + ctlr->rxflags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto; + if(bss != nil){ + ctlr->aid = bss->aid; + ctlr->channel = bss->channel; + memmove(ctlr->bssid, bss->bssid, sizeof(ctlr->bssid)); + if(bss->cap & (1<<5)) + ctlr->rxflags |= RFlagShPreamble; + if(bss->cap & (1<<10)) + ctlr->rxflags |= RFlagShSlot; + if(ctlr->aid != 0){ + ctlr->rxfilter |= FilterBSS; + ctlr->rxfilter &= ~FilterBeacon; + ctlr->bss.id = -1; + } else { + ctlr->bcast.id = -1; + } + } else { + ctlr->aid = 0; + memmove(ctlr->bssid, edev->bcast, sizeof(ctlr->bssid)); + ctlr->bcast.id = -1; + ctlr->bss.id = -1; } -} -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 } -}; + if(ctlr->aid != 0) + setled(ctlr, 2, 0, 1); /* on when associated */ + else if(memcmp(ctlr->bssid, edev->bcast, sizeof(ctlr->bssid)) != 0) + setled(ctlr, 2, 10, 10); /* slow blink when connecting */ + else + setled(ctlr, 2, 5, 5); /* fast blink when scanning */ -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, -}; + if(ctlr->wifi->debug) + print("#l%d: rxon: bssid %E, aid %x, channel %d, rxfilter %ux, rxflags %ux\n", + edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, ctlr->rxfilter, ctlr->rxflags); + + if(ctlr->family >= 7000) + err = rxon7000(edev, ctlr); + else + err = rxon6000(edev, ctlr); + if(err != nil) + goto Out; + + if(ctlr->bcast.id == -1){ + if((err = setstation(ctlr, + (ctlr->type != Type4965)? 15: 31, + StaTypeGeneralPurpose, + edev->bcast, + &ctlr->bcast)) != nil) + goto Out; + } + if(ctlr->bss.id == -1 && bss != nil && ctlr->aid != 0){ + if((err = setstation(ctlr, + 0, + StaTypeLink, + bss->bssid, + &ctlr->bss)) != nil) + goto Out; + + if(ctlr->family >= 7000) + if((err = setmaccontext(edev, ctlr, CmdModify, bss)) != nil) + goto Out; + } +Out: + return err; +} static void transmit(Wifi *wifi, Wnode *wn, Block *b) { - int flags, nodeid, rate, ant; + int flags, rate, ant; uchar c[Tcmdsize], *p; Ether *edev; + Station *sta; Ctlr *ctlr; Wifipkt *w; char *err; @@ -1848,24 +3785,35 @@ transmit(Wifi *wifi, Wnode *wn, Block *b) qlock(ctlr); if(ctlr->attached == 0 || ctlr->broken){ +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); + || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0))){ + if(rxon(edev, wn) != nil) + goto Broken; + } + + /* + * unless authenticated, the firmware will hop + * channels unless we force it onto a channel using + * a timeevent. + */ + if(ctlr->aid == 0 && ctlr->family >= 7000) + if(settimeevent(ctlr, CmdAdd, wn->ival) != nil) + goto Broken; if(b == nil){ /* association note has no data to transmit */ qunlock(ctlr); return; } - - rate = 0; flags = 0; - nodeid = ctlr->bcastnodeid; + sta = &ctlr->bcast; + p = wn->minrate; w = (Wifipkt*)b->rp; if((w->a1[0] & 1) == 0){ flags |= TFlagNeedACK; @@ -1873,20 +3821,27 @@ transmit(Wifi *wifi, Wnode *wn, Block *b) if(BLEN(b) > 512-4) flags |= TFlagNeedRTS; - if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){ - nodeid = ctlr->bssnodeid; - rate = 2; /* BUG: hardcode 11Mbit */ + if((w->fc[0] & 0x0c) == 0x08 && ctlr->bss.id != -1){ + sta = &ctlr->bss; + p = wn->actrate; } if(flags & (TFlagNeedRTS|TFlagNeedCTS)){ - if(ctlr->type != Type4965){ + if(ctlr->family >= 7000 || ctlr->type != Type4965){ flags &= ~(TFlagNeedRTS|TFlagNeedCTS); flags |= TFlagNeedProtection; } else flags |= TFlagFullTxOp; } } - qunlock(ctlr); + + if(sta->id == -1) + goto Broken; + + if(p >= wifi->rates) + rate = p - wifi->rates; + else + rate = 0; /* select first available antenna */ ant = ctlr->rfcfg.txantmask & 7; @@ -1906,7 +3861,7 @@ transmit(Wifi *wifi, Wnode *wn, Block *b) *p++ = ratetab[rate].flags | (ant<<6); p += 2; /* xflags */ - *p++ = nodeid; + *p++ = sta->id; /* station id */ *p++ = 0; /* security */ *p++ = 0; /* linkq */ p++; /* reserved */ @@ -1926,8 +3881,10 @@ transmit(Wifi *wifi, Wnode *wn, Block *b) put16(p, 0); /* timeout */ p += 2; p += 2; /* txop */ + qunlock(ctlr); + if((err = qcmd(ctlr, 0, 28, c, p - c, b)) != nil){ - print("transmit: %s\n", err); + print("#l%d: transmit %s\n", edev->ctlrno, err); freeb(b); } } @@ -1938,6 +3895,10 @@ iwlctl(Ether *edev, void *buf, long n) Ctlr *ctlr; ctlr = edev->ctlr; + if(n >= 5 && memcmp(buf, "reset", 5) == 0){ + ctlr->broken = 1; + return n; + } if(ctlr->wifi) return wifictl(ctlr->wifi, buf, n); return 0; @@ -1957,27 +3918,12 @@ iwlifstat(Ether *edev, void *buf, long n, ulong off) 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(); - } - } - } + for(i = 0; i < edev->nopt; i++) + wificfg(ctlr->wifi, edev->opt[i]); } static void @@ -1994,6 +3940,47 @@ iwlpromiscuous(void *arg, int on) qunlock(ctlr); } +static void +iwlmulticast(void *, uchar*, int) +{ +} + +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; + + rxon(edev, ctlr->wifi->bss); + break; + } + qunlock(ctlr); + } +} + static void iwlattach(Ether *edev) { @@ -2014,44 +4001,79 @@ iwlattach(Ether *edev) if((csr32r(ctlr, Gpc) & RfKill) == 0) error("wifi disabled by switch"); - if(ctlr->wifi == nil) - ctlr->wifi = wifiattach(edev, transmit); - 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], + char *fn; + + fn = ctlr->fwname; + if(fn == nil){ + fn = fwname[ctlr->type]; + if(ctlr->type == Type6005){ + switch(ctlr->pdev->did){ + case 0x0082: /* Centrino Advanced-N 6205 */ + case 0x0085: /* Centrino Advanced-N 6205 */ + break; + default: /* Centrino Advanced-N 6030, 6235 */ + fn = "iwn-6030"; + } + } + } + fw = readfirmware(fn); + print("#l%d: firmware: %s, rev %ux, build %ud, size [%d] %ux+%ux + [%d] %ux+%ux + %ux\n", + edev->ctlrno, fn, fw->rev, fw->build, - fw->main.text.size, fw->main.data.size, - fw->init.text.size, fw->init.data.size, + fw->main.nsect, fw->main.text.size, fw->main.data.size, + fw->init.nsect, fw->init.text.size, fw->init.data.size, fw->boot.text.size); ctlr->fw = fw; } + if(ctlr->family >= 7000){ + u32int u = ctlr->fw->physku; + + ctlr->rfcfg.type = u & 3; u >>= 2; + ctlr->rfcfg.step = u & 3; u >>= 2; + ctlr->rfcfg.dash = u & 3; u >>= 12; + + ctlr->rfcfg.txantmask = u & 15; u >>= 4; + ctlr->rfcfg.rxantmask = u & 15; + } + 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; + if(ctlr->wifi == nil){ + qsetlimit(edev->oq, MaxQueue); + + ctlr->wifi = wifiattach(edev, transmit); + /* tested with 2230, it has transmit issues using higher bit rates */ + if(ctlr->family >= 7000 || ctlr->type != Type2030) + ctlr->wifi->rates = iwlrates; + } setoptions(edev); ctlr->attached = 1; + + kproc("iwlrecover", iwlrecover, edev); } qunlock(ctlr); poperror(); } +static void +updatesystime(Ctlr *ctlr, u32int ts) +{ + u32int dt = ts - (u32int)ctlr->systime; + ctlr->systime += dt; +} + static void receive(Ctlr *ctlr) { Block *b, *bb; - uchar *d, *dd, *cc; + uchar *d; RXQ *rx; TXQ *tx; uint hw; @@ -2059,9 +4081,9 @@ receive(Ctlr *ctlr) 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; + int type, flags, idx, qid, len; b = rx->b[rx->i]; if(b == nil) @@ -2075,43 +4097,70 @@ receive(Ctlr *ctlr) idx = *d++; qid = *d++; - if((qid & 0x80) == 0 && qid < nelem(ctlr->tx)){ + tx = nil; + bb = nil; + if((qid & 0x80) == 0 && qid < ctlr->ntxq){ 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); - } + bb = tx->b[idx]; + tx->b[idx] = nil; } len &= 0x3fff; - if(len < 4 || type == 0) - continue; - len -= 4; - switch(type){ + if(len >= 0) switch(type){ case 1: /* microcontroller ready */ setfwinfo(ctlr, d, len); break; case 24: /* add node done */ + if(len < 4) + break; break; case 28: /* tx done */ + if(ctlr->family >= 7000){ + if(len <= 36 || d[36] == 1 || d[36] == 2) + break; + } else if(ctlr->type == Type4965){ + if(len <= 20 || d[20] == 1 || d[20] == 2) + break; + } else { + if(len <= 32 || d[32] == 1 || d[32] == 2) + break; + } + if(ctlr->wifi != nil) + wifitxfail(ctlr->wifi, bb); + break; + case 41: + if(len < 2*4) + break; + if(get32(d) != 0) + break; + if(ctlr->te.id == -1) + ctlr->te.id = get32(d+8); + break; + case 42: + if(len < 6*4) + break; + if(ctlr->te.id == -1 || get32(d+8) != ctlr->te.id) + break; + switch(get32(d+16)){ + case 1: + ctlr->te.active = 1; + wakeup(&ctlr->te); + break; + case 2: + ctlr->te.active = 0; + ctlr->te.id = -1; + wakeup(&ctlr->te); + } break; case 102: /* calibration result (Type5000 only) */ + if(ctlr->family >= 7000) + break; if(len < 4) break; idx = d[0]; - if(idx >= nelem(ctlr->calib.cmd)) + Calib: + if(idx < 0 || idx >= nelem(ctlr->calib.cmd)) break; if(rbplant(ctlr, rx->i) < 0) break; @@ -2120,52 +4169,130 @@ receive(Ctlr *ctlr) b->rp = d; b->wp = d + len; ctlr->calib.cmd[idx] = b; - continue; + break; + case 4: /* init complete (>= 7000 family) */ + if(ctlr->family < 7000) + break; + /* wet floor */ case 103: /* calibration done (Type5000 only) */ ctlr->calib.done = 1; break; + case 107: /* calibration result (>= 7000 family) */ + if(ctlr->family < 7000) + break; + len -= 4; + if(len < 0) + break; + idx = get16(d+2); + if(idx < len) + len = idx; + idx = -1; + switch(get16(d)){ + case 1: + idx = &ctlr->calib.cfg - &ctlr->calib.cmd[0]; + break; + case 2: + idx = &ctlr->calib.nch - &ctlr->calib.cmd[0]; + break; + case 4: + if(len < 2) + break; + idx = &ctlr->calib.papd[get16(d+4) % nelem(ctlr->calib.papd)] - &ctlr->calib.cmd[0]; + break; + case 5: + if(len < 2) + break; + idx = &ctlr->calib.txp[get16(d+4) % nelem(ctlr->calib.txp)] - &ctlr->calib.cmd[0]; + break; + } + len += 4; + goto Calib; case 130: /* start scan */ - break; case 132: /* stop scan */ break; - case 156: /* rx statistics */ + case 136: /* NVM access (>= 7000 family) */ + if(ctlr->family < 7000) + break; + len -= 8; + if(len < 0) + break; + if(ctlr->nvm.len < len) + len = ctlr->nvm.len; + ctlr->nvm.off = get16(d + 0); + ctlr->nvm.ret = get16(d + 2); + ctlr->nvm.type= get16(d + 4); + ctlr->nvm.sts = get16(d + 6); + d += 8; + if(ctlr->nvm.ret < len) + len = ctlr->nvm.ret; + if(ctlr->nvm.buf != nil && len > 0) + memmove(ctlr->nvm.buf, d, len); + ctlr->nvm.buf = nil; + ctlr->nvm.len = 0; break; + case 156: /* rx statistics */ case 157: /* beacon statistics */ - break; case 161: /* state changed */ - break; case 162: /* beacon missed */ + case 177: /* mduart load notification */ break; case 192: /* rx phy */ + if(len >= 8) + updatesystime(ctlr, get32(d+4)); break; case 195: /* rx done */ if(d + 2 > b->lim) break; d += d[1]; d += 56; + /* wet floor */ 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; + len = get16(d); + if(ctlr->mqrx){ + if(d + 48 + len > b->lim) + break; + updatesystime(ctlr, get32(d+36)); + if((d[12] & 3) != 3) + break; + d += 48; + } else { + d += 4; + if(d + len + 4 > b->lim) + break; + if((d[len] & 3) != 3) + break; + } if(ctlr->wifi == nil) break; if(rbplant(ctlr, rx->i) < 0) break; b->rp = d; b->wp = d + len; + + put64(d - 8, ctlr->systime); + b->flag |= Btimestamp; + wifiiq(ctlr->wifi, b); - continue; + break; case 197: /* rx compressed ba */ break; } - /* paranoia: clear the descriptor */ - memset(b->rp, 0, Rdscsize); + freeblist(bb); + if(tx != nil && tx->n > 0){ + tx->n--; + wakeup(tx); + /* unlock 7k family nics as all commands are done */ + if(ctlr->family == 7000 && tx->n == 0) + nicunlock(ctlr); + } } - csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7); + + if(ctlr->mqrx){ + csr32w(ctlr, FhRxQ0Wptr, ((hw+Nrx-1) % Nrx) & ~7); + }else + csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7); } static void @@ -2189,30 +4316,42 @@ iwlinterrupt(Ureg*, void *arg) goto done; csr32w(ctlr, Isr, isr); csr32w(ctlr, FhIsr, fhisr); - if((isr & (Iswrx | Ifhrx | Irxperiodic)) || (fhisr & Ifhrx)) + + if((isr & (Iswrx | Ifhrx | Irxperiodic | Ialive)) || (fhisr & Ifhrx)) receive(ctlr); if(isr & Ierr){ ctlr->broken = 1; - iprint("#l%d: fatal firmware error\n", edev->ctlrno); + print("#l%d: fatal firmware error\n", edev->ctlrno); dumpctlr(ctlr); } ctlr->wait.m |= isr; - if(ctlr->wait.m & ctlr->wait.w){ - ctlr->wait.r = ctlr->wait.m & ctlr->wait.w; - ctlr->wait.m &= ~ctlr->wait.r; + 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; + pcidisable(ctlr->pdev); +} + static Ctlr *iwlhead, *iwltail; static void iwlpci(void) { Pcidev *pdev; + char *fwname; + int family; pdev = nil; while(pdev = pcimatch(pdev, 0, 0)) { @@ -2223,56 +4362,75 @@ iwlpci(void) continue; if(pdev->vid != 0x8086) continue; + if(pdev->mem[0].bar & 1) + continue; switch(pdev->did){ default: continue; - case 0x0084: /* WiFi Link 1000 */ + case 0x0084: /* WiFi Link 1000 */ case 0x4229: /* WiFi Link 4965 */ case 0x4230: /* WiFi Link 4965 */ + case 0x4232: /* Wifi Link 5100 */ + case 0x4235: /* Intel Corporation Ultimate N WiFi Link 5300 */ case 0x4236: /* WiFi Link 5300 AGN */ case 0x4237: /* Wifi Link 5100 AGN */ + case 0x4239: /* Centrino Advanced-N 6200 */ + case 0x423d: /* Wifi Link 5150 */ + case 0x423b: /* PRO/Wireless 5350 AGN */ + case 0x0082: /* Centrino Advanced-N 6205 */ case 0x0085: /* Centrino Advanced-N 6205 */ - case 0x422b: /* Centrino Ultimate-N 6300 */ + case 0x422b: /* Centrino Ultimate-N 6300 variant 1 */ + case 0x4238: /* Centrino Ultimate-N 6300 variant 2 */ case 0x08ae: /* Centrino Wireless-N 100 */ + case 0x0083: /* Centrino Wireless-N 1000 */ + case 0x008a: /* Centrino Wireless-N 1030 */ + case 0x0891: /* Centrino Wireless-N 2200 */ + case 0x0887: /* Centrino Wireless-N 2230 */ + case 0x0888: /* Centrino Wireless-N 2230 */ + case 0x0090: /* Centrino Advanced-N 6030 */ + case 0x0091: /* Centrino Advanced-N 6030 */ + case 0x088e: /* Centrino Advanced-N 6235 */ + case 0x088f: /* Centrino Advanced-N 6235 */ + family = 0; + fwname = nil; + break; + case 0x08b1: /* Wireless AC 7260 */ + case 0x08b2: /* Wireless AC 7260 */ + family = 7000; + fwname = nil; + break; + case 0x24f3: /* Wireless AC 8260 */ + family = 8000; + fwname = "iwm-8000C-34"; + break; + case 0x24fd: /* Wireless AC 8265 */ + family = 8000; + fwname = "iwm-8265-34"; + break; + case 0x2526: /* Wireless AC 9260 */ + family = 9000; + fwname = "iwm-9260-34"; 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); + ctlr->port = pdev->mem[0].bar & ~0xF; + mem = vmap(ctlr->port, pdev->mem[0].size); if(mem == nil) { - print("iwl: can't map %8.8luX\n", pdev->mem[0].bar); + print("iwl: can't map %llux\n", ctlr->port); 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; - } + ctlr->fwname = fwname; + ctlr->family = family; + ctlr->mqrx = family >= 9000; if(iwlhead != nil) iwltail->link = ctlr; @@ -2291,10 +4449,10 @@ iwlpnp(Ether* edev) iwlpci(); again: for(ctlr = iwlhead; ctlr != nil; ctlr = ctlr->link){ - if(ctlr->active) + if(ctlr->edev != nil) continue; if(edev->port == 0 || edev->port == ctlr->port){ - ctlr->active = 1; + ctlr->edev = edev; break; } } @@ -2307,18 +4465,24 @@ again: 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 = 10; + edev->multicast = iwlmulticast; + edev->mbps = 54; + pcienable(ctlr->pdev); if(iwlinit(edev) < 0){ + pcidisable(ctlr->pdev); + ctlr->edev = (void*)-1; edev->ctlr = nil; goto again; } + + pcisetbme(ctlr->pdev); + intrenable(edev->irq, iwlinterrupt, edev, edev->tbdf, edev->name); return 0; }