#include "io.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<<Ntxlog,
+ Ntxqmax = MaxQueue/1500,
+
Nrxlog = 8,
Nrx = 1<<Nrxlog,
GpDrv = 0x050,
GpDrvCalV6 = 1<<2,
GpDrv1X2 = 1<<3,
+ GpDrvRadioIqInvert = 1<<7,
Led = 0x094,
LedBsmCtrl = 1<<5,
Rendez;
u32int m;
u32int w;
- u32int r;
} wait;
struct {
uchar version;
uchar type;
u16int volt;
+ u16int temp;
+ u16int rawtemp;
char regdom[4+1];
Type1000 = 6,
Type6000 = 7,
Type6050 = 8,
- Type6005 = 11,
+ Type6005 = 11, /* also Centrino Advanced-N 6030, 6235 */
+ Type2030 = 12,
+ Type2000 = 16,
};
-static char *fwname[16] = {
+static char *fwname[32] = {
[Type4965] "iwn-4965",
[Type5300] "iwn-5000",
[Type5350] "iwn-5000",
[Type1000] "iwn-1000",
[Type6000] "iwn-6000",
[Type6050] "iwn-6050",
- [Type6005] "iwn-6005",
+ [Type6005] "iwn-6005", /* see in iwlattach() below */
+ [Type2030] "iwn-2030",
+ [Type2000] "iwn-2000",
};
static char *qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block);
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) >> 4) & 0x1F;
+ if(fwname[ctlr->type] == nil){
+ print("iwl: unsupported controller type %d\n", ctlr->type);
+ return -1;
+ }
+
if((err = handover(ctlr)) != nil)
goto Err;
if((err = poweron(ctlr)) != nil)
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;
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
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)
csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
ctlr->broken = 0;
- ctlr->wait.r = 0;
+ ctlr->wait.m = 0;
ctlr->wait.w = 0;
ctlr->ie = Idefmask;
return nil;
}
+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->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;
+
+ /* 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*
postboot(Ctlr *ctlr)
{
c[3] = 1; /* isvalid */
c[4] = ctlr->eeprom.crystal;
c[5] = ctlr->eeprom.crystal>>16;
- if((err = cmd(ctlr, 176, c, 8)) != nil)
+ /* 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;
}
Block *b;
i = cmds[q];
- if(i == 8 && ctlr->type != Type5150)
+ if(i == 8 && ctlr->type != Type5150 && ctlr->type != Type2030 &&
+ ctlr->type != Type2000)
continue;
- if(i == 17 && (ctlr->type >= Type6000 || ctlr->type == Type5150))
+ 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->ref++; /* dont free on command completion */
+ b = copyblock(b, BLEN(b));
if((err = qcmd(ctlr, 4, 176, nil, 0, b)) != nil){
freeb(b);
return err;
return err;
}
- if(ctlr->type == Type6005){
- /* temperature sensor offset */
+ /* temperature sensor offset */
+ switch (ctlr->type){
+ case Type6005:
memset(c, 0, sizeof(c));
c[0] = 18;
c[1] = 0;
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){
put32(c, ctlr->rfcfg.txantmask & 7);
if((err = cmd(ctlr, 152, c, 4)) != nil)
return err;
+
+ if(ctlr->type == Type2030){
+ if((err = sendbtcoexadv(ctlr)) != nil)
+ return err;
+ }
}
}
txqready(void *arg)
{
TXQ *q = arg;
- return q->n < Ntx;
+ return q->n < Ntxqmax;
}
static char*
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);
cmd(ctlr, 24, c, p - c);
}
-void
+static void
rxon(Ether *edev, Wnode *bss)
{
uchar c[Tcmdsize], *p;
ctlr->channel = bss->channel;
bss = nil;
}
+ flags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto;
if(bss != nil){
+ if(bss->cap & (1<<5))
+ flags |= RFlagShPreamble;
+ if(bss->cap & (1<<10))
+ flags |= RFlagShSlot;
ctlr->channel = bss->channel;
memmove(ctlr->bssid, bss->bssid, Eaddrlen);
ctlr->aid = bss->aid;
ctlr->bcastnodeid = -1;
ctlr->bssnodeid = -1;
}
- flags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto;
if(ctlr->aid != 0)
setled(ctlr, 2, 0, 1); /* on when associated */
{ 4, 20, RFlagCCK },
{ 11, 55, RFlagCCK },
{ 22, 110, RFlagCCK },
+
{ 12, 0xd, 0 },
{ 18, 0xf, 0 },
{ 24, 0x5, 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,
return;
}
- rate = 0;
flags = 0;
nodeid = ctlr->bcastnodeid;
+ p = wn->minrate;
w = (Wifipkt*)b->rp;
if((w->a1[0] & 1) == 0){
flags |= TFlagNeedACK;
if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){
nodeid = ctlr->bssnodeid;
- rate = 2; /* BUG: hardcode 11Mbit */
+ p = wn->actrate;
}
if(flags & (TFlagNeedRTS|TFlagNeedCTS)){
flags |= TFlagFullTxOp;
}
}
+ if(p >= wifi->rates)
+ rate = p - wifi->rates;
+ else
+ rate = 0;
qunlock(ctlr);
/* select first available antenna */
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;
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
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;
+
+ ctlr->bcastnodeid = -1;
+ ctlr->bssnodeid = -1;
+ ctlr->aid = 0;
+ rxon(edev, ctlr->wifi->bss);
+ break;
+ }
+ qunlock(ctlr);
+ }
+}
+
static void
iwlattach(Ether *edev)
{
if((csr32r(ctlr, Gpc) & RfKill) == 0)
error("wifi disabled by switch");
- if(ctlr->wifi == nil)
+ 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->type != Type2030)
+ ctlr->wifi->rates = iwlrates;
+ }
if(ctlr->fw == nil){
- fw = readfirmware(fwname[ctlr->type]);
+ char *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 %ux+%ux+%ux+%ux+%ux\n",
- edev->ctlrno,
- fwname[ctlr->type],
+ edev->ctlrno, fn,
fw->rev, fw->build,
fw->main.text.size, fw->main.data.size,
fw->init.text.size, fw->init.data.size,
setoptions(edev);
ctlr->attached = 1;
+
+ kproc("iwlrecover", iwlrecover, edev);
}
qunlock(ctlr);
poperror();
receive(Ctlr *ctlr)
{
Block *b, *bb;
- uchar *d, *dd, *cc;
+ uchar *d;
RXQ *rx;
TXQ *tx;
uint hw;
rx = &ctlr->rx;
if(ctlr->broken || rx->s == nil || rx->b == nil)
return;
+
+ bb = nil;
for(hw = get16(rx->s) % Nrx; rx->i != hw; rx->i = (rx->i + 1) % Nrx){
uchar type, flags, idx, qid;
u32int len;
idx = *d++;
qid = *d++;
+ if(bb != nil){
+ freeb(bb);
+ bb = nil;
+ }
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->b[idx] = nil;
tx->n--;
wakeup(tx);
case 24: /* add node done */
break;
case 28: /* tx done */
+ 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;
+ }
+ wifitxfail(ctlr->wifi, bb);
break;
case 102: /* calibration result (Type5000 only) */
if(len < 4)
case 197: /* rx compressed ba */
break;
}
- /* paranoia: clear the descriptor */
- memset(b->rp, 0, Rdscsize);
}
csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7);
+ if(bb != nil)
+ freeb(bb);
}
static void
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;
+}
+
static Ctlr *iwlhead, *iwltail;
static void
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 */
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");
}
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;
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);
edev->ctlr = nil;
goto again;
}
+
+ pcisetbme(ctlr->pdev);
+ intrenable(edev->irq, iwlinterrupt, edev, edev->tbdf, edev->name);
return 0;
}