]> 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 cb266fc7369ec3dedc8f19f96feb37c3aaaceb9c..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,
 
@@ -87,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,
@@ -345,7 +353,6 @@ struct Ctlr {
                Rendez;
                u32int  m;
                u32int  w;
-               u32int  r;
        } wait;
 
        struct {
@@ -363,6 +370,8 @@ struct Ctlr {
                uchar   version;
                uchar   type;
                u16int  volt;
+               u16int  temp;
+               u16int  rawtemp;
 
                char    regdom[4+1];
 
@@ -393,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",
@@ -405,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);
@@ -810,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)
@@ -852,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;
@@ -1010,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
@@ -1161,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);
@@ -1194,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;
@@ -1207,6 +1255,82 @@ 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)
 {
@@ -1302,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;
                }
 
@@ -1332,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;
@@ -1347,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));
@@ -1360,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;
+                       }
                }
        }
 
@@ -1524,7 +1690,7 @@ static int
 txqready(void *arg)
 {
        TXQ *q = arg;
-       return q->n < Ntx;
+       return q->n < Ntxqmax;
 }
 
 static char*
@@ -1538,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);
@@ -1578,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();
@@ -1686,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;
@@ -1695,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;
@@ -1716,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 */
@@ -1772,6 +1953,7 @@ static struct ratetab {
        {   4,  20, RFlagCCK },
        {  11,  55, RFlagCCK },
        {  22, 110, RFlagCCK },
+
        {  12, 0xd, 0 },
        {  18, 0xf, 0 },
        {  24, 0x5, 0 },
@@ -1783,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,
@@ -1808,7 +2009,6 @@ transmit(Wifi *wifi, Wnode *wn, Block *b)
        Wifipkt *w;
        char *err;
 
-       w = (Wifipkt*)b->rp;
        edev = wifi->ether;
        ctlr = edev->ctlr;
 
@@ -1819,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;
 
@@ -1836,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)){
@@ -1847,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 */
@@ -1899,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;
@@ -1919,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
@@ -1949,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);
+
+                       if((csr32r(ctlr, Gpc) & RfKill) == 0)
+                               break;
 
-               /* wait for disassociation */
-               edev->link = 1;
-               setled(ctlr, 2, 0, 1);
-               while((bss = wifi->bss) != nil){
-                       if(bss->aid == 0)
+                       if(reset(ctlr) != nil)
                                break;
-                       tsleep(&up->sleep, return0, 0, 1000);
+                       if(boot(ctlr) != nil)
+                               break;
+
+                       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;
@@ -2020,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,
@@ -2047,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();
@@ -2060,7 +2271,7 @@ static void
 receive(Ctlr *ctlr)
 {
        Block *b, *bb;
-       uchar *d, *dd, *cc;
+       uchar *d;
        RXQ *rx;
        TXQ *tx;
        uint hw;
@@ -2068,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;
@@ -2084,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);
@@ -2115,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)
@@ -2171,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
@@ -2202,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
@@ -2236,29 +2461,33 @@ iwlpci(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");
@@ -2273,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;
@@ -2315,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;
 }