]> 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 72a5771665e1d7a70cd93a31c5dec8fca2bd6b7a..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,
 
@@ -90,6 +93,7 @@ enum {
        GpDrv   = 0x050,
                GpDrvCalV6      = 1<<2,
                GpDrv1X2        = 1<<3,
+               GpDrvRadioIqInvert      = 1<<7, 
 
        Led             = 0x094,
                LedBsmCtrl      = 1<<5,
@@ -366,6 +370,8 @@ struct Ctlr {
                uchar   version;
                uchar   type;
                u16int  volt;
+               u16int  temp;
+               u16int  rawtemp;
 
                char    regdom[4+1];
 
@@ -396,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",
@@ -408,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);
@@ -813,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)
@@ -855,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;
@@ -1166,6 +1205,8 @@ reset(Ctlr *ctlr)
                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)
@@ -1214,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)
 {
@@ -1309,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;
                }
 
@@ -1339,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;
@@ -1354,8 +1475,9 @@ postboot(Ctlr *ctlr)
                                        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;
@@ -1364,6 +1486,26 @@ postboot(Ctlr *ctlr)
                                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){
@@ -1379,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;
+                       }
                }
        }
 
@@ -1543,7 +1690,7 @@ static int
 txqready(void *arg)
 {
        TXQ *q = arg;
-       return q->n < Ntx;
+       return q->n < Ntxqmax;
 }
 
 static char*
@@ -1557,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);
@@ -1706,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;
@@ -1722,7 +1869,12 @@ rxon(Ether *edev, Wnode *bss)
                        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;
@@ -1738,7 +1890,6 @@ rxon(Ether *edev, Wnode *bss)
                ctlr->bcastnodeid = -1;
                ctlr->bssnodeid = -1;
        }
-       flags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto;
 
        if(ctlr->aid != 0)
                setled(ctlr, 2, 0, 1);          /* on when associated */
@@ -1802,6 +1953,7 @@ static struct ratetab {
        {   4,  20, RFlagCCK },
        {  11,  55, RFlagCCK },
        {  22, 110, RFlagCCK },
+
        {  12, 0xd, 0 },
        {  18, 0xf, 0 },
        {  24, 0x5, 0 },
@@ -1813,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,
@@ -1858,9 +2029,9 @@ transmit(Wifi *wifi, Wnode *wn, Block *b)
                return;
        }
 
-       rate = 0;
        flags = 0;
        nodeid = ctlr->bcastnodeid;
+       p = wn->minrate;
        w = (Wifipkt*)b->rp;
        if((w->a1[0] & 1) == 0){
                flags |= TFlagNeedACK;
@@ -1870,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)){
@@ -1881,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 */
@@ -1933,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;
@@ -1952,27 +2131,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
@@ -1989,6 +2153,50 @@ 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;
+
+                       ctlr->bcastnodeid = -1;
+                       ctlr->bssnodeid = -1;
+                       ctlr->aid = 0;
+                       rxon(edev, ctlr->wifi->bss);
+                       break;
+               }
+               qunlock(ctlr);
+       }
+}
+
 static void
 iwlattach(Ether *edev)
 {
@@ -2009,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,
@@ -2037,6 +2260,8 @@ iwlattach(Ether *edev)
                setoptions(edev);
 
                ctlr->attached = 1;
+
+               kproc("iwlrecover", iwlrecover, edev);
        }
        qunlock(ctlr);
        poperror();
@@ -2046,7 +2271,7 @@ static void
 receive(Ctlr *ctlr)
 {
        Block *b, *bb;
-       uchar *d, *dd, *cc;
+       uchar *d;
        RXQ *rx;
        TXQ *tx;
        uint hw;
@@ -2054,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;
@@ -2070,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);
@@ -2101,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)
@@ -2157,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
@@ -2188,7 +2419,7 @@ 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;
@@ -2199,6 +2430,17 @@ done:
        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
@@ -2219,30 +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");
@@ -2257,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;
@@ -2299,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;
 }