]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/pc/etheriwl.c
kernel: cleanup the software mouse cursor mess
[plan9front.git] / sys / src / 9 / pc / etheriwl.c
index 4d49450c6c1ec81af26c6282cc30404228b865d9..22e92884bbda48457c733c1a8d220d2e8f11e325 100644 (file)
 #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,
 
@@ -69,6 +72,7 @@ enum {
 
        EepromIo        = 0x02c,        /* EEPROM i/o register */
        EepromGp        = 0x030,
+               
        OtpromGp        = 0x034,
                DevSelOtp       = 1<<16,
                RelativeAccess  = 1<<17,
@@ -86,6 +90,11 @@ enum {
        Gio             = 0x03c,
                EnaL0S          = 1<<1,
 
+       GpDrv   = 0x050,
+               GpDrvCalV6      = 1<<2,
+               GpDrv1X2        = 1<<3,
+               GpDrvRadioIqInvert      = 1<<7, 
+
        Led             = 0x094,
                LedBsmCtrl      = 1<<5,
                LedOn           = 0x38,
@@ -105,6 +114,7 @@ enum {
        AnaPll          = 0x20c,
 
        Dbghpetmem      = 0x240,
+       Dbglinkpwrmgmt  = 0x250,
 
        MemRaddr        = 0x40c,
        MemWaddr        = 0x410,
@@ -343,7 +353,6 @@ struct Ctlr {
                Rendez;
                u32int  m;
                u32int  w;
-               u32int  r;
        } wait;
 
        struct {
@@ -355,9 +364,14 @@ struct Ctlr {
        } rfcfg;
 
        struct {
+               int     otp;
+               uint    off;
+
                uchar   version;
                uchar   type;
                u16int  volt;
+               u16int  temp;
+               u16int  rawtemp;
 
                char    regdom[4+1];
 
@@ -388,10 +402,12 @@ enum {
        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",
@@ -400,7 +416,9 @@ static char *fwname[16] = {
        [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);
@@ -542,10 +560,11 @@ static char*
 eepromread(Ctlr *ctlr, void *data, int count, uint off)
 {
        uchar *out = data;
-       u32int w;
+       u32int w, s;
        int i;
 
        w = 0;
+       off += ctlr->eeprom.off;
        for(; count > 0; count -= 2, off++){
                csr32w(ctlr, EepromIo, off << 2);
                for(i=0; i<10; i++){
@@ -556,6 +575,13 @@ eepromread(Ctlr *ctlr, void *data, int count, uint off)
                }
                if(i == 10)
                        return "eepromread: timeout";
+               if(ctlr->eeprom.otp){
+                       s = csr32r(ctlr, OtpromGp);
+                       if(s & EccUncorrStts)
+                               return "eepromread: otprom ecc error";
+                       if(s & EccCorrStts)
+                               csr32w(ctlr, OtpromGp, s);
+               }
                *out++ = w >> 16;
                if(count > 1)
                        *out++ = w >> 24;
@@ -728,6 +754,66 @@ poweroff(Ctlr *ctlr)
        ctlr->power = 0;
 }
 
+static char*
+rominit(Ctlr *ctlr)
+{
+       uint prev, last;
+       uchar buf[2];
+       char *err;
+       int i;
+
+       ctlr->eeprom.otp = 0;
+       ctlr->eeprom.off = 0;
+       if(ctlr->type < Type1000 || (csr32r(ctlr, OtpromGp) & DevSelOtp) == 0)
+               return nil;
+
+       /* Wait for clock stabilization before accessing prph. */
+       if((err = clockwait(ctlr)) != nil)
+               return err;
+
+       if((err = niclock(ctlr)) != nil)
+               return err;
+       prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | ResetReq);
+       delay(5);
+       prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) & ~ResetReq);
+       nicunlock(ctlr);
+
+       /* Set auto clock gate disable bit for HW with OTP shadow RAM. */
+       if(ctlr->type != Type1000)
+               csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) | (1<<31));
+
+       csr32w(ctlr, EepromGp, csr32r(ctlr, EepromGp) & ~0x00000180);
+
+       /* Clear ECC status. */
+       csr32w(ctlr, OtpromGp, csr32r(ctlr, OtpromGp) | (EccCorrStts | EccUncorrStts));
+
+       ctlr->eeprom.otp = 1;
+       if(ctlr->type != Type1000)
+               return nil;
+
+       /* Switch to absolute addressing mode. */
+       csr32w(ctlr, OtpromGp, csr32r(ctlr, OtpromGp) & ~RelativeAccess);
+
+       /*
+        * Find the block before last block (contains the EEPROM image)
+        * for HW without OTP shadow RAM.
+        */
+       prev = last = 0;
+       for(i=0; i<3; i++){
+               if((err = eepromread(ctlr, buf, 2, last)) != nil)
+                       return err;
+               if(get16(buf) == 0)
+                       break;
+               prev = last;
+               last = get16(buf);
+       }
+       if(i == 0 || i >= 3)
+               return "rominit: missing eeprom image";
+
+       ctlr->eeprom.off = prev+1;
+       return nil;
+}
+
 static int
 iwlinit(Ether *edev)
 {
@@ -737,6 +823,23 @@ 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) >> 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)
@@ -747,6 +850,8 @@ iwlinit(Ether *edev)
        }
        if((err = eepromlock(ctlr)) != nil)
                goto Err;
+       if((err = rominit(ctlr)) != nil)
+               goto Err2;
        if((err = eepromread(ctlr, edev->ea, sizeof(edev->ea), 0x15)) != nil){
                eepromunlock(ctlr);
                goto Err;
@@ -777,6 +882,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;
@@ -935,39 +1052,35 @@ 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
@@ -1086,6 +1199,16 @@ reset(Ctlr *ctlr)
        }
        nicunlock(ctlr);
 
+       if((err = niclock(ctlr)) != nil)
+               return err;
+       if((ctlr->type == Type6005 || ctlr->type == Type6050) && ctlr->eeprom.version == 6)
+               csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrvCalV6);
+       if(ctlr->type == Type6005)
+               csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrv1X2);
+       if(ctlr->type == Type2030 || ctlr->type == Type2000)
+               csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrvRadioIqInvert);
+       nicunlock(ctlr);
+
        if((err = niclock(ctlr)) != nil)
                return err;
        csr32w(ctlr, FhRxConfig, 0);
@@ -1119,7 +1242,7 @@ reset(Ctlr *ctlr)
        csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
 
        ctlr->broken = 0;
-       ctlr->wait.r = 0;
+       ctlr->wait.m = 0;
        ctlr->wait.w = 0;
 
        ctlr->ie = Idefmask;
@@ -1132,10 +1255,86 @@ reset(Ctlr *ctlr)
        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)
 {
-       uint ctxoff, ctxlen, dramaddr, txfact;
+       uint ctxoff, ctxlen, dramaddr;
        char *err;
        int i, q;
 
@@ -1146,12 +1345,10 @@ postboot(Ctlr *ctlr)
                dramaddr = SchedDramAddr5000;
                ctxoff = SchedCtxOff5000;
                ctxlen = SchedCtxLen5000;
-               txfact = SchedTxFact5000;
        } else {
                dramaddr = SchedDramAddr4965;
                ctxoff = SchedCtxOff4965;
                ctxlen = SchedCtxLen4965;
-               txfact = SchedTxFact4965;
        }
 
        ctlr->sched.base = prphread(ctlr, SchedSramAddr);
@@ -1177,6 +1374,9 @@ postboot(Ctlr *ctlr)
                }
                /* Enable interrupts for all our 20 queues. */
                prphwrite(ctlr, SchedIntrMask5000, 0xfffff);
+
+               /* Identify TX FIFO rings (0-7). */
+               prphwrite(ctlr, SchedTxFact5000, 0xff);
        } else {
                /* Disable chain mode for all our 16 queues. */
                prphwrite(ctlr, SchedQChainSel4965, 0);
@@ -1192,10 +1392,10 @@ postboot(Ctlr *ctlr)
                }
                /* Enable interrupts for all our 16 queues. */
                prphwrite(ctlr, SchedIntrMask4965, 0xffff);
-       }
 
-       /* Identify TX FIFO rings (0-7). */
-       prphwrite(ctlr, txfact, 0xff);
+               /* Identify TX FIFO rings (0-7). */
+               prphwrite(ctlr, SchedTxFact4965, 0xff);
+       }
 
        /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
        for(q=0; q<7; q++){
@@ -1226,7 +1426,8 @@ 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;
                }
 
@@ -1256,13 +1457,16 @@ postboot(Ctlr *ctlr)
                                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;
@@ -1271,6 +1475,39 @@ postboot(Ctlr *ctlr)
                                        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));
@@ -1284,6 +1521,11 @@ postboot(Ctlr *ctlr)
                        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;
+                       }
                }
        }
 
@@ -1448,7 +1690,7 @@ static int
 txqready(void *arg)
 {
        TXQ *q = arg;
-       return q->n < Ntx;
+       return q->n < Ntxqmax;
 }
 
 static char*
@@ -1462,11 +1704,11 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
 
        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);
@@ -1502,8 +1744,9 @@ qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
        put32(d, PCIWADDR(c));  d += 4;
        put16(d, size << 4); d += 2;
        if(block != nil){
+               size = BLEN(block);
                put32(d, PCIWADDR(block->rp)); d += 4;
-               put16(d, BLEN(block) << 4);
+               put16(d, size << 4);
        }
 
        coherence();
@@ -1610,7 +1853,7 @@ addnode(Ctlr *ctlr, uchar id, uchar *addr)
        cmd(ctlr, 24, c, p - c);
 }
 
-void
+static void
 rxon(Ether *edev, Wnode *bss)
 {
        uchar c[Tcmdsize], *p;
@@ -1619,12 +1862,19 @@ rxon(Ether *edev, Wnode *bss)
        char *err;
 
        ctlr = edev->ctlr;
-       filter = FilterMulticast | FilterBeacon;
+       filter = FilterNoDecrypt | FilterMulticast | FilterBeacon;
        if(ctlr->prom){
                filter |= FilterPromisc;
+               if(bss != nil)
+                       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;
@@ -1640,10 +1890,17 @@ rxon(Ether *edev, Wnode *bss)
                ctlr->bcastnodeid = -1;
                ctlr->bssnodeid = -1;
        }
-       flags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto;
 
-       if(0) print("rxon: bssid %E, aid %x, channel %d, filter %x, flags %x\n",
-               ctlr->bssid, ctlr->aid, ctlr->channel, filter, flags);
+       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 */
@@ -1696,6 +1953,7 @@ static struct ratetab {
        {   4,  20, RFlagCCK },
        {  11,  55, RFlagCCK },
        {  22, 110, RFlagCCK },
+
        {  12, 0xd, 0 },
        {  18, 0xf, 0 },
        {  24, 0x5, 0 },
@@ -1707,6 +1965,25 @@ static struct ratetab {
        { 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,
@@ -1732,7 +2009,6 @@ transmit(Wifi *wifi, Wnode *wn, Block *b)
        Wifipkt *w;
        char *err;
 
-       w = (Wifipkt*)b->rp;
        edev = wifi->ether;
        ctlr = edev->ctlr;
 
@@ -1743,15 +2019,20 @@ transmit(Wifi *wifi, Wnode *wn, Block *b)
                return;
        }
 
-       if(ctlr->prom == 0)
-       if(wn->aid != ctlr->aid
-       || wn->channel != ctlr->channel
-       || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)
+       if((wn->channel != ctlr->channel)
+       || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)))
                rxon(edev, wn);
 
-       rate = 0;
+       if(b == nil){
+               /* association note has no data to transmit */
+               qunlock(ctlr);
+               return;
+       }
+
        flags = 0;
        nodeid = ctlr->bcastnodeid;
+       p = wn->minrate;
+       w = (Wifipkt*)b->rp;
        if((w->a1[0] & 1) == 0){
                flags |= TFlagNeedACK;
 
@@ -1760,7 +2041,7 @@ transmit(Wifi *wifi, Wnode *wn, Block *b)
 
                if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){
                        nodeid = ctlr->bssnodeid;
-                       rate = 2; /* BUG: hardcode 11Mbit */
+                       p = wn->actrate;
                }
 
                if(flags & (TFlagNeedRTS|TFlagNeedCTS)){
@@ -1771,6 +2052,10 @@ transmit(Wifi *wifi, Wnode *wn, Block *b)
                                flags |= TFlagFullTxOp;
                }
        }
+       if(p >= wifi->rates)
+               rate = p - wifi->rates;
+       else
+               rate = 0;
        qunlock(ctlr);
 
        /* select first available antenna */
@@ -1823,6 +2108,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;
@@ -1843,19 +2132,11 @@ static void
 setoptions(Ether *edev)
 {
        Ctlr *ctlr;
-       char buf[64];
        int i;
 
        ctlr = edev->ctlr;
-       for(i = 0; i < edev->nopt; i++){
-               if(strncmp(edev->opt[i], "essid=", 6) == 0){
-                       snprint(buf, sizeof(buf), "essid %s", edev->opt[i]+6);
-                       if(!waserror()){
-                               wifictl(ctlr->wifi, buf, strlen(buf));
-                               poperror();
-                       }
-               }
-       }
+       for(i = 0; i < edev->nopt; i++)
+               wificfg(ctlr->wifi, edev->opt[i]);
 }
 
 static void
@@ -1873,60 +2154,52 @@ iwlpromiscuous(void *arg, int on)
 }
 
 static void
-iwlproc(void *arg)
+iwlmulticast(void *, uchar*, int)
+{
+}
+
+static void
+iwlrecover(void *arg)
 {
        Ether *edev;
        Ctlr *ctlr;
-       Wifi *wifi;
-       Wnode *bss;
 
        edev = arg;
        ctlr = edev->ctlr;
-       wifi = ctlr->wifi;
-
+       while(waserror())
+               ;
        for(;;){
-               /* hop channels for catching beacons */
-               setled(ctlr, 2, 5, 5);
-               while(wifi->bss == nil){
-                       qlock(ctlr);
-                       if(wifi->bss != nil){
-                               qunlock(ctlr);
-                               break;
-                       }
-                       ctlr->channel = 1 + ctlr->channel % 11;
-                       ctlr->aid = 0;
-                       rxon(edev, nil);
-                       qunlock(ctlr);
-                       tsleep(&up->sleep, return0, 0, 1000);
-               }
+               tsleep(&up->sleep, return0, 0, 4000);
 
-               /* wait for association */
-               setled(ctlr, 2, 10, 10);
-               while((bss = wifi->bss) != nil){
-                       if(bss->aid != 0)
+               qlock(ctlr);
+               for(;;){
+                       if(ctlr->broken == 0)
                                break;
-                       tsleep(&up->sleep, return0, 0, 1000);
-               }
 
-               if(bss == nil)
-                       continue;
+                       if(ctlr->power)
+                               poweroff(ctlr);
 
-               /* wait for disassociation */
-               edev->link = 1;
-               setled(ctlr, 2, 0, 1);
-               while((bss = wifi->bss) != nil){
-                       if(bss->aid == 0)
+                       if((csr32r(ctlr, Gpc) & RfKill) == 0)
+                               break;
+
+                       if(reset(ctlr) != nil)
+                               break;
+                       if(boot(ctlr) != nil)
                                break;
-                       tsleep(&up->sleep, return0, 0, 1000);
+
+                       ctlr->bcastnodeid = -1;
+                       ctlr->bssnodeid = -1;
+                       ctlr->aid = 0;
+                       rxon(edev, ctlr->wifi->bss);
+                       break;
                }
-               edev->link = 0;
+               qunlock(ctlr);
        }
 }
 
 static void
 iwlattach(Ether *edev)
 {
-       char name[32];
        FWImage *fw;
        Ctlr *ctlr;
        char *err;
@@ -1944,14 +2217,29 @@ 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,
@@ -1971,10 +2259,9 @@ iwlattach(Ether *edev)
 
                setoptions(edev);
 
-               snprint(name, sizeof(name), "#l%diwl", edev->ctlrno);
-               kproc(name, iwlproc, edev);
-
                ctlr->attached = 1;
+
+               kproc("iwlrecover", iwlrecover, edev);
        }
        qunlock(ctlr);
        poperror();
@@ -1984,7 +2271,7 @@ static void
 receive(Ctlr *ctlr)
 {
        Block *b, *bb;
-       uchar *d, *dd, *cc;
+       uchar *d;
        RXQ *rx;
        TXQ *tx;
        uint hw;
@@ -1992,6 +2279,8 @@ receive(Ctlr *ctlr)
        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;
@@ -2008,19 +2297,15 @@ receive(Ctlr *ctlr)
                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);
@@ -2039,6 +2324,14 @@ receive(Ctlr *ctlr)
                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)
@@ -2095,10 +2388,10 @@ receive(Ctlr *ctlr)
                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
@@ -2126,20 +2419,28 @@ iwlinterrupt(Ureg*, void *arg)
                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
@@ -2160,28 +2461,33 @@ iwlpci(void)
                switch(pdev->did){
                default:
                        continue;
+               case 0x0084:    /* WiFi Link 1000 */
                case 0x4229:    /* WiFi Link 4965 */
                case 0x4230:    /* WiFi Link 4965 */
+               case 0x4232:    /* Wifi Link 5100 */
+               case 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");
@@ -2196,14 +2502,6 @@ iwlpci(void)
                }
                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;
@@ -2238,18 +2536,23 @@ 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);
                edev->ctlr = nil;
                goto again;
        }
+
+       pcisetbme(ctlr->pdev);
+       intrenable(edev->irq, iwlinterrupt, edev, edev->tbdf, edev->name);
        
        return 0;
 }