]> 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 f5b793c869dbc01dcbaf0880a0f757df0091abdf..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,
 
@@ -65,11 +67,12 @@ enum {
        FhIsr           = 0x010,        /* second interrupt status */
 
        Reset           = 0x020,
-
+               
        Rev             = 0x028,        /* hardware revision */
 
        EepromIo        = 0x02c,        /* EEPROM i/o register */
        EepromGp        = 0x030,
+               
        OtpromGp        = 0x034,
                DevSelOtp       = 1<<16,
                RelativeAccess  = 1<<17,
@@ -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,
@@ -106,6 +114,7 @@ enum {
        AnaPll          = 0x20c,
 
        Dbghpetmem      = 0x240,
+       Dbglinkpwrmgmt  = 0x250,
 
        MemRaddr        = 0x40c,
        MemWaddr        = 0x410,
@@ -197,16 +206,18 @@ enum {
 enum {
        SchedBase               = 0xa02c00,
        SchedSramAddr           = SchedBase,
-       SchedDramAddr5000       = SchedBase+0x008,
+
        SchedDramAddr4965       = SchedBase+0x010,
-       SchedTxFact5000         = SchedBase+0x010,
        SchedTxFact4965         = SchedBase+0x01c,
        SchedQueueRdptr4965     = SchedBase+0x064,      // +q*4
-       SchedQueueRdptr5000     = SchedBase+0x068,      // +q*4
        SchedQChainSel4965      = SchedBase+0x0d0,
        SchedIntrMask4965       = SchedBase+0x0e4,
-       SchedQChainSel5000      = SchedBase+0x0e8,
        SchedQueueStatus4965    = SchedBase+0x104,      // +q*4
+
+       SchedDramAddr5000       = SchedBase+0x008,
+       SchedTxFact5000         = SchedBase+0x010,
+       SchedQueueRdptr5000     = SchedBase+0x068,      // +q*4
+       SchedQChainSel5000      = SchedBase+0x0e8,
        SchedIntrMask5000       = SchedBase+0x108,
        SchedQueueStatus5000    = SchedBase+0x10c,      // +q*4
        SchedAggrSel5000        = SchedBase+0x248,
@@ -215,24 +226,31 @@ enum {
 enum {
        SchedCtxOff4965         = 0x380,
        SchedCtxLen4965         = 416,
-       SchedTransTblOff4965    = 0x500,
 
        SchedCtxOff5000         = 0x600,
        SchedCtxLen5000         = 512,
-       SchedTransTblOff5000    = 0x7e0,
 };
 
-/* controller types */
 enum {
-       Type4965        = 0,
-       Type5300        = 2,
-       Type5350        = 3,
-       Type5150        = 4,
-       Type5100        = 5,
-       Type1000        = 6,
-       Type6000        = 7,
-       Type6050        = 8,
-       Type6005        = 11,
+       FilterPromisc           = 1<<0,
+       FilterCtl               = 1<<1,
+       FilterMulticast         = 1<<2,
+       FilterNoDecrypt         = 1<<3,
+       FilterBSS               = 1<<5,
+       FilterBeacon            = 1<<6,
+};
+
+enum {
+       RFlag24Ghz              = 1<<0,
+       RFlagCCK                = 1<<1,
+       RFlagAuto               = 1<<2,
+       RFlagShSlot             = 1<<4,
+       RFlagShPreamble         = 1<<5,
+       RFlagNoDiversity        = 1<<7,
+       RFlagAntennaA           = 1<<8,
+       RFlagAntennaB           = 1<<9,
+       RFlagTSF                = 1<<15,
+       RFlagCTSToSelf          = 1<<30,
 };
 
 typedef struct FWInfo FWInfo;
@@ -284,6 +302,8 @@ struct TXQ
        uchar   *d;
        uchar   *c;
 
+       uint    lastcmd;
+
        Rendez;
        QLock;
 };
@@ -306,7 +326,9 @@ struct Ctlr {
 
        int type;
        int port;
+       int power;
        int active;
+       int broken;
        int attached;
 
        u32int ie;
@@ -314,7 +336,15 @@ struct Ctlr {
        u32int *nic;
        uchar *kwpage;
 
+       /* assigned node ids in hardware node table or -1 if unassigned */
+       int bcastnodeid;
+       int bssnodeid;
+
+       /* current receiver settings */
+       uchar bssid[Eaddrlen];
        int channel;
+       int prom;
+       int aid;
 
        RXQ rx;
        TXQ tx[20];
@@ -323,7 +353,6 @@ struct Ctlr {
                Rendez;
                u32int  m;
                u32int  w;
-               u32int  r;
        } wait;
 
        struct {
@@ -335,9 +364,25 @@ struct Ctlr {
        } rfcfg;
 
        struct {
+               int     otp;
+               uint    off;
+
+               uchar   version;
+               uchar   type;
+               u16int  volt;
+               u16int  temp;
+               u16int  rawtemp;
+
+               char    regdom[4+1];
+
                u32int  crystal;
        } eeprom;
 
+       struct {
+               Block   *cmd[21];
+               int     done;
+       } calib;
+
        struct {
                u32int  base;
                uchar   *s;
@@ -347,6 +392,39 @@ struct Ctlr {
        FWImage *fw;
 };
 
+/* controller types */
+enum {
+       Type4965        = 0,
+       Type5300        = 2,
+       Type5350        = 3,
+       Type5150        = 4,
+       Type5100        = 5,
+       Type1000        = 6,
+       Type6000        = 7,
+       Type6050        = 8,
+       Type6005        = 11,   /* also Centrino Advanced-N 6030, 6235 */
+       Type2030        = 12,
+       Type2000        = 16,
+};
+
+static char *fwname[32] = {
+       [Type4965] "iwn-4965",
+       [Type5300] "iwn-5000",
+       [Type5350] "iwn-5000",
+       [Type5150] "iwn-5150",
+       [Type5100] "iwn-5000",
+       [Type1000] "iwn-1000",
+       [Type6000] "iwn-6000",
+       [Type6050] "iwn-6050",
+       [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);
+static char *flushq(Ctlr *ctlr, uint qid);
+static char *cmd(Ctlr *ctlr, uint code, uchar *data, int size);
+
 #define csr32r(c, r)   (*((c)->nic+((r)/4)))
 #define csr32w(c, r, v)        (*((c)->nic+((r)/4)) = (v))
 
@@ -443,6 +521,7 @@ dumpctlr(Ctlr *ctlr)
        u32int dump[13];
        int i;
 
+       print("lastcmd: %ud (0x%ux)\n", ctlr->tx[4].lastcmd,  ctlr->tx[4].lastcmd);
        if(ctlr->fwinfo.errptr == 0){
                print("no error pointer\n");
                return;
@@ -481,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++){
@@ -495,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;
@@ -593,18 +680,166 @@ poweron(Ctlr *ctlr)
        prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11));
 
        nicunlock(ctlr);
+
+       ctlr->power = 1;
+
        return 0;
 }
 
+static void
+poweroff(Ctlr *ctlr)
+{
+       int i, j;
+
+       csr32w(ctlr, Reset, 1);
+
+       /* Disable interrupts */
+       ctlr->ie = 0;
+       csr32w(ctlr, Imr, 0);
+       csr32w(ctlr, Isr, ~0);
+       csr32w(ctlr, FhIsr, ~0);
+
+       /* Stop scheduler */
+       if(ctlr->type != Type4965)
+               prphwrite(ctlr, SchedTxFact5000, 0);
+       else
+               prphwrite(ctlr, SchedTxFact4965, 0);
+
+       /* Stop TX ring */
+       if(niclock(ctlr) == nil){
+               for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--){
+                       csr32w(ctlr, FhTxConfig + i*32, 0);
+                       for(j = 0; j < 200; j++){
+                               if(csr32r(ctlr, FhTxStatus) & (0x10000<<i))
+                                       break;
+                               delay(10);
+                       }
+               }
+               nicunlock(ctlr);
+       }
+
+       /* Stop RX ring */
+       if(niclock(ctlr) == nil){
+               csr32w(ctlr, FhRxConfig, 0);
+               for(j = 0; j < 200; j++){
+                       if(csr32r(ctlr, FhRxStatus) & 0x1000000)
+                               break;
+                       delay(10);
+               }
+               nicunlock(ctlr);
+       }
+
+       /* Disable DMA */
+       if(niclock(ctlr) == nil){
+               prphwrite(ctlr, ApmgClkDis, DmaClkRqt);
+               nicunlock(ctlr);
+       }
+       delay(5);
+
+       /* Stop busmaster DMA activity. */
+       csr32w(ctlr, Reset, csr32r(ctlr, Reset) | (1<<9));
+       for(j = 0; j < 100; j++){
+               if(csr32r(ctlr, Reset) & (1<<8))
+                       break;
+               delay(10);
+       }
+
+       /* Reset the entire device. */
+       csr32w(ctlr, Reset, csr32r(ctlr, Reset) | (1<<7));
+       delay(10);
+
+       /* Clear "initialization complete" bit. */
+       csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) & ~InitDone);
+
+       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)
 {
        Ctlr *ctlr;
        char *err;
-       uchar b[2];
-       uint u;
+       uchar b[4];
+       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)
@@ -615,11 +850,14 @@ 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;
        }
        if((err = eepromread(ctlr, b, 2, 0x048)) != nil){
+       Err2:
                eepromunlock(ctlr);
                goto Err;
        }
@@ -629,20 +867,61 @@ iwlinit(Ether *edev)
        ctlr->rfcfg.dash = u & 3;       u >>= 4;
        ctlr->rfcfg.txantmask = u & 15; u >>= 4;
        ctlr->rfcfg.rxantmask = u & 15;
-       if((err = eepromread(ctlr, b, 4, 0x128)) != nil){
-               eepromunlock(ctlr);
-               goto Err;
+       if((err = eepromread(ctlr, b, 2, 0x66)) != nil)
+               goto Err2;
+       regoff = get16(b);
+       if((err = eepromread(ctlr, b, 4, regoff+1)) != nil)
+               goto Err2;
+       strncpy(ctlr->eeprom.regdom, (char*)b, 4);
+       ctlr->eeprom.regdom[4] = 0;
+       if((err = eepromread(ctlr, b, 2, 0x67)) != nil)
+               goto Err2;
+       caloff = get16(b);
+       if((err = eepromread(ctlr, b, 4, caloff)) != nil)
+               goto Err2;
+       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);
        }
-       ctlr->eeprom.crystal = get32(b);
-       eepromunlock(ctlr);
 
-       ctlr->ie = 0;
-       csr32w(ctlr, Isr, ~0);  /* clear pending interrupts */
-       csr32w(ctlr, Imr, 0);   /* no interrupts for now */
+       if(ctlr->type != Type4965 && ctlr->type != Type5150){
+               if((err = eepromread(ctlr, b, 4, caloff + 0x128)) != nil)
+                       goto Err2;
+               ctlr->eeprom.crystal = get32(b);
+       }
+       eepromunlock(ctlr);
 
+       switch(ctlr->type){
+       case Type4965:
+               ctlr->rfcfg.txantmask = 3;
+               ctlr->rfcfg.rxantmask = 7;
+               break;
+       case Type5100:
+               ctlr->rfcfg.txantmask = 2;
+               ctlr->rfcfg.rxantmask = 3;
+               break;
+       case Type6000:
+               if(ctlr->pdev->did == 0x422c || ctlr->pdev->did == 0x4230){
+                       ctlr->rfcfg.txantmask = 6;
+                       ctlr->rfcfg.rxantmask = 6;
+               }
+               break;
+       }
+       poweroff(ctlr);
        return 0;
 Err:
        print("iwlinit: %s\n", err);
+       poweroff(ctlr);
        return -1;
 }
 
@@ -669,7 +948,7 @@ Tooshort:
                        return "bad firmware signature";
                p += 4;
                strncpy(i->descr, (char*)p, 64);
-               i->descr[sizeof(i->descr)-1] = 0;
+               i->descr[64] = 0;
                p += 64;
                i->rev = get32(p); p += 4;
                i->build = get32(p); p += 4;
@@ -691,7 +970,7 @@ Tooshort:
                        default:s = &dummy;
                        }
                        p += 2;
-                       if(get16(p) != alt)
+                       if(get16(p) != 0 && get16(p) != alt)
                                s = &dummy;
                        p += 2;
                        s->size = get32(p); p += 4;
@@ -773,222 +1052,768 @@ 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;
-
-       w.ctlr = ctlr;
-       w.mask = mask;
-       tsleep(&ctlr->wait, gotirq, &w, timeout);
-       ctlr->wait.w = 0;
-       return ctlr->wait.r & mask;
-}
-
-static char*
-loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size)
-{
-       uchar *dma;
-       char *err;
+       u32int r;
 
-       dma = mallocalign(size, 16, 0, 0);
-       if(dma == nil)
-               return "no memory for dma";
-       memmove(dma, data, size);
-       coherence();
-       if((err = niclock(ctlr)) != 0){
-               free(dma);
-               return err;
-       }
-       csr32w(ctlr, FhTxConfig + 9*32, 0);
-       csr32w(ctlr, FhSramAddr + 9*4, dst);
-       csr32w(ctlr, FhTfbdCtrl0 + 9*8, PCIWADDR(dma));
-       csr32w(ctlr, FhTfbdCtrl1 + 9*8, size);
-       csr32w(ctlr, FhTxBufStatus + 9*32,
-               (1<<FhTxBufStatusTbNumShift) |
-               (1<<FhTxBufStatusTbIdxShift) |
-               FhTxBufStatusTfbdValid);
-       csr32w(ctlr, FhTxConfig + 9*32, FhTxConfigDmaEna | FhTxConfigCirqHostEndTfd);
-       nicunlock(ctlr);
-       if(irqwait(ctlr, Ifhtx|Ierr, 5000) != Ifhtx){
-               free(dma);
-               return "dma error / timeout";
+       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;
        }
-       free(dma);
-       return 0;
+       ctlr->wait.m &= ~r;
+       iunlock(ctlr);
+       return r;
 }
 
 static int
-txqready(void *arg)
+rbplant(Ctlr *ctlr, int i)
 {
-       TXQ *q = arg;
-       return q->n < Ntx;
+       Block *b;
+
+       b = iallocb(Rbufsize + 256);
+       if(b == nil)
+               return -1;
+       b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256);
+       memset(b->rp, 0, Rdscsize);
+       ctlr->rx.b[i] = b;
+       ctlr->rx.p[i] = PCIWADDR(b->rp) >> 8;
+       return 0;
 }
 
-static void
-qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
+static char*
+initring(Ctlr *ctlr)
 {
-       uchar *d, *c;
-       TXQ *q;
-
-       assert(qid < nelem(ctlr->tx));
-       assert(size <= Tcmdsize-4);
+       RXQ *rx;
+       TXQ *tx;
+       int i, q;
 
-       ilock(ctlr);
-       q = &ctlr->tx[qid];
-       while(q->n >= Ntx){
-               iunlock(ctlr);
-               eqlock(q);
-               if(waserror()){
-                       qunlock(q);
-                       nexterror();
+       rx = &ctlr->rx;
+       if(rx->b == nil)
+               rx->b = malloc(sizeof(Block*) * Nrx);
+       if(rx->p == nil)
+               rx->p = mallocalign(sizeof(u32int) * Nrx, 256, 0, 0);
+       if(rx->s == nil)
+               rx->s = mallocalign(Rstatsize, 16, 0, 0);
+       if(rx->b == nil || rx->p == nil || rx->s == nil)
+               return "no memory for rx ring";
+       memset(ctlr->rx.s, 0, Rstatsize);
+       for(i=0; i<Nrx; i++){
+               rx->p[i] = 0;
+               if(rx->b[i] != nil){
+                       freeb(rx->b[i]);
+                       rx->b[i] = nil;
                }
-               tsleep(q, txqready, q, 10);
-               qunlock(q);
-               ilock(ctlr);
+               if(rbplant(ctlr, i) < 0)
+                       return "no memory for rx descriptors";
        }
-       q->n++;
-
-       q->b[q->i] = block;
-       c = q->c + q->i * Tcmdsize;
-       d = q->d + q->i * Tdscsize;
-
-       /* build command */
-       c[0] = code;
-       c[1] = 0;       /* flags */
-       c[2] = q->i;
-       c[3] = qid;
-
-       memmove(c+4, data, size);
+       rx->i = 0;
 
-       size += 4;
+       if(ctlr->sched.s == nil)
+               ctlr->sched.s = mallocalign(512 * nelem(ctlr->tx) * 2, 1024, 0, 0);
+       if(ctlr->sched.s == nil)
+               return "no memory for sched buffer";
+       memset(ctlr->sched.s, 0, 512 * nelem(ctlr->tx));
 
-       /* build descriptor */
-       *d++ = 0;
-       *d++ = 0;
-       *d++ = 0;
-       *d++ = 1 + (block != nil); /* nsegs */
-       put32(d, PCIWADDR(c));  d += 4;
-       put16(d, size << 4); d += 2;
-       if(block != nil){
-               size = BLEN(block);
-               if(size > Tbufsize)
-                       size = Tbufsize;
-               put32(d, PCIWADDR(block->rp)); d += 4;
-               put16(d, size << 4);
+       for(q=0; q<nelem(ctlr->tx); q++){
+               tx = &ctlr->tx[q];
+               if(tx->b == nil)
+                       tx->b = malloc(sizeof(Block*) * Ntx);
+               if(tx->d == nil)
+                       tx->d = mallocalign(Tdscsize * Ntx, 256, 0, 0);
+               if(tx->c == nil)
+                       tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0);
+               if(tx->b == nil || tx->d == nil || tx->c == nil)
+                       return "no memory for tx ring";
+               memset(tx->d, 0, Tdscsize * Ntx);
+               memset(tx->c, 0, Tcmdsize * Ntx);
+               for(i=0; i<Ntx; i++){
+                       if(tx->b[i] != nil){
+                               freeb(tx->b[i]);
+                               tx->b[i] = nil;
+                       }
+               }
+               tx->i = 0;
+               tx->n = 0;
+               tx->lastcmd = 0;
        }
 
-       coherence();
-
-       q->i = (q->i+1) % Ntx;
-       csr32w(ctlr, HbusTargWptr, (qid<<8) | q->i);
+       if(ctlr->kwpage == nil)
+               ctlr->kwpage = mallocalign(4096, 4096, 0, 0);
+       if(ctlr->kwpage == nil)
+               return "no memory for kwpage";          
+       memset(ctlr->kwpage, 0, 4096);
 
-       iunlock(ctlr);
+       return nil;
 }
 
-static void
-cmd(Ctlr *ctlr, uint code, uchar *data, int size)
+static char*
+reset(Ctlr *ctlr)
 {
-       qcmd(ctlr, 4, code, data, size, nil);
-}
+       char *err;
+       int i, q;
 
-static void
-setled(Ctlr *ctlr, int which, int on, int off)
-{
-       uchar c[8];
+       if(ctlr->power)
+               poweroff(ctlr);
+       if((err = initring(ctlr)) != nil)
+               return err;
+       if((err = poweron(ctlr)) != nil)
+               return err;
 
-       csr32w(ctlr, Led, csr32r(ctlr, Led) & ~LedBsmCtrl);
+       if((err = niclock(ctlr)) != nil)
+               return err;
+       prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain);
+       nicunlock(ctlr);
 
-       memset(c, 0, sizeof(c));
-       put32(c, 10000);
-       c[4] = which;
-       c[5] = on;
-       c[6] = off;
-       cmd(ctlr, 72, c, sizeof(c));
-}
+       csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi);
 
-/*
- * initialization which runs after the firmware has been booted up
- */
-static void
+       if((err = niclock(ctlr)) != nil)
+               return err;
+       if(ctlr->type != Type4965)
+               prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | EarlyPwroffDis);
+       if(ctlr->type == Type1000){
+               /*
+                * Select first Switching Voltage Regulator (1.32V) to
+                * solve a stability issue related to noisy DC2DC line
+                * in the silicon of 1000 Series.
+                */
+               prphwrite(ctlr, ApmgDigitalSvr, 
+                       (prphread(ctlr, ApmgDigitalSvr) & ~(0xf<<5)) | (3<<5));
+       }
+       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);
+       csr32w(ctlr, FhRxWptr, 0);
+       csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p) >> 8);
+       csr32w(ctlr, FhStatusWptr, PCIWADDR(ctlr->rx.s) >> 4);
+       csr32w(ctlr, FhRxConfig,
+               FhRxConfigEna | 
+               FhRxConfigIgnRxfEmpty |
+               FhRxConfigIrqDstHost | 
+               FhRxConfigSingleFrame |
+               (Nrxlog << FhRxConfigNrbdShift));
+       csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7);
+       nicunlock(ctlr);
+
+       if((err = niclock(ctlr)) != nil)
+               return err;
+       if(ctlr->type != Type4965)
+               prphwrite(ctlr, SchedTxFact5000, 0);
+       else
+               prphwrite(ctlr, SchedTxFact4965, 0);
+       csr32w(ctlr, FhKwAddr, PCIWADDR(ctlr->kwpage) >> 4);
+       for(q = (ctlr->type != Type4965) ? 19 : 15; q >= 0; q--)
+               csr32w(ctlr, FhCbbcQueue + q*4, PCIWADDR(ctlr->tx[q].d) >> 8);
+       nicunlock(ctlr);
+
+       for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--)
+               csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna);
+
+       csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
+       csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
+
+       ctlr->broken = 0;
+       ctlr->wait.m = 0;
+       ctlr->wait.w = 0;
+
+       ctlr->ie = Idefmask;
+       csr32w(ctlr, Imr, ctlr->ie);
+       csr32w(ctlr, Isr, ~0);
+
+       if(ctlr->type >= Type6000)
+               csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff);
+
+       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)
 {
-       uchar c[8];
+       uint ctxoff, ctxlen, dramaddr;
        char *err;
        int i, q;
 
-       /* main led turn on! (verify that firmware processes commands) */
-       setled(ctlr, 2, 0, 1);
-
        if((err = niclock(ctlr)) != nil)
-               error(err);
+               return err;
+
+       if(ctlr->type != Type4965){
+               dramaddr = SchedDramAddr5000;
+               ctxoff = SchedCtxOff5000;
+               ctxlen = SchedCtxLen5000;
+       } else {
+               dramaddr = SchedDramAddr4965;
+               ctxoff = SchedCtxOff4965;
+               ctxlen = SchedCtxLen4965;
+       }
+
        ctlr->sched.base = prphread(ctlr, SchedSramAddr);
-       for(i=0; i < SchedCtxLen5000/4; i++)
-               memwrite(ctlr, ctlr->sched.base + SchedCtxOff5000 + i*4, 0);
+       for(i=0; i < ctxlen; i += 4)
+               memwrite(ctlr, ctlr->sched.base + ctxoff + i, 0);
+
+       prphwrite(ctlr, dramaddr, PCIWADDR(ctlr->sched.s)>>10);
 
-       prphwrite(ctlr, SchedDramAddr5000, PCIWADDR(ctlr->sched.s)>>10);
        csr32w(ctlr, FhTxChicken, csr32r(ctlr, FhTxChicken) | 2);
 
-       /* Enable chain mode for all queues, except command queue. */
-       prphwrite(ctlr, SchedQChainSel5000, 0xfffef);
-       prphwrite(ctlr, SchedAggrSel5000, 0);
+       if(ctlr->type != Type4965){
+               /* Enable chain mode for all queues, except command queue 4. */
+               prphwrite(ctlr, SchedQChainSel5000, 0xfffef);
+               prphwrite(ctlr, SchedAggrSel5000, 0);
 
-       for(q=0; q<nelem(ctlr->tx); q++){
-               prphwrite(ctlr, SchedQueueRdptr5000 + q*4, 0);
-               csr32w(ctlr, HbusTargWptr, q << 8);
-               memwrite(ctlr, ctlr->sched.base + SchedCtxOff5000 + q*8, 0);
-               /* Set scheduler window size and frame limit. */
-               memwrite(ctlr, ctlr->sched.base + SchedCtxOff5000 + q*8 + 4, 64<<16 | 64);
+               for(q=0; q<20; q++){
+                       prphwrite(ctlr, SchedQueueRdptr5000 + q*4, 0);
+                       csr32w(ctlr, HbusTargWptr, q << 8);
+
+                       memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 0);
+                       /* Set scheduler window size and frame limit. */
+                       memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16 | 64);
+               }
+               /* 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);
+
+               for(q=0; q<16; q++) {
+                       prphwrite(ctlr, SchedQueueRdptr4965 + q*4, 0);
+                       csr32w(ctlr, HbusTargWptr, q << 8);
+
+                       /* Set scheduler window size. */
+                       memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 64);
+                       /* Set scheduler window size and frame limit. */
+                       memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16);
+               }
+               /* Enable interrupts for all our 16 queues. */
+               prphwrite(ctlr, SchedIntrMask4965, 0xffff);
+
+               /* Identify TX FIFO rings (0-7). */
+               prphwrite(ctlr, SchedTxFact4965, 0xff);
        }
 
-       /* Enable interrupts for all our 20 queues. */
-       prphwrite(ctlr, SchedIntrMask5000, 0xfffff);
-       /* Identify TX FIFO rings (0-7). */
-       prphwrite(ctlr, SchedTxFact5000, 0xff);
        /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
        for(q=0; q<7; q++){
-               static uchar qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
-               prphwrite(ctlr, SchedQueueStatus5000 + q*4, 0x00ff0018 | qid2fifo[q]);
+               if(ctlr->type != Type4965){
+                       static uchar qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
+                       prphwrite(ctlr, SchedQueueStatus5000 + q*4, 0x00ff0018 | qid2fifo[q]);
+               } else {
+                       static uchar qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
+                       prphwrite(ctlr, SchedQueueStatus4965 + q*4, 0x0007fc01 | qid2fifo[q]<<1);
+               }
        }
        nicunlock(ctlr);
 
-       if(ctlr->type != Type5150){
+       if(ctlr->type != Type4965){
+               uchar c[Tcmdsize];
+
+               /* disable wimax coexistance */
                memset(c, 0, sizeof(c));
-               c[0] = 15;      /* code */
-               c[1] = 0;       /* grup */
-               c[2] = 1;       /* ngroup */
-               c[3] = 1;       /* isvalid */
-               put16(c+4, ctlr->eeprom.crystal);
-               cmd(ctlr, 176, c, 8);
+               if((err = cmd(ctlr, 90, c, 4+4*16)) != nil)
+                       return err;
+
+               if(ctlr->type != Type5150){
+                       /* calibrate crystal */
+                       memset(c, 0, sizeof(c));
+                       c[0] = 15;      /* code */
+                       c[1] = 0;       /* group */
+                       c[2] = 1;       /* ngroup */
+                       c[3] = 1;       /* isvalid */
+                       c[4] = ctlr->eeprom.crystal;
+                       c[5] = ctlr->eeprom.crystal>>16;
+                       /* 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;
+               }
+
+               if(ctlr->calib.done == 0){
+                       /* query calibration (init firmware) */
+                       memset(c, 0, sizeof(c));
+                       put32(c + 0*(5*4) + 0, 0xffffffff);
+                       put32(c + 0*(5*4) + 4, 0xffffffff);
+                       put32(c + 0*(5*4) + 8, 0xffffffff);
+                       put32(c + 2*(5*4) + 0, 0xffffffff);
+                       if((err = cmd(ctlr, 101, c, (((2*(5*4))+4)*2)+4)) != nil)
+                               return err;
+
+                       /* wait to collect calibration records */
+                       if(irqwait(ctlr, Ierr, 2000))
+                               return "calibration failed";
+
+                       if(ctlr->calib.done == 0){
+                               print("iwl: no calibration results\n");
+                               ctlr->calib.done = 1;
+                       }
+               } else {
+                       static uchar cmds[] = {8, 9, 11, 17, 16};
+
+                       /* send calibration records (runtime firmware) */
+                       for(q=0; q<nelem(cmds); q++){
+                               Block *b;
+
+                               i = cmds[q];
+                               if(i == 8 && ctlr->type != Type5150 && ctlr->type != Type2030 &&
+                                       ctlr->type != Type2000)
+                                       continue;
+                               if(i == 17 && (ctlr->type >= Type6000 || ctlr->type == Type5150) &&
+                                       ctlr->type != Type2030 && ctlr->type != Type2000)
+                                       continue;
+
+                               if((b = ctlr->calib.cmd[i]) == nil)
+                                       continue;
+                               b = copyblock(b, BLEN(b));
+                               if((err = qcmd(ctlr, 4, 176, nil, 0, b)) != nil){
+                                       freeb(b);
+                                       return err;
+                               }
+                               if((err = flushq(ctlr, 4)) != nil)
+                                       return err;
+                       }
+
+                       /* temperature sensor offset */
+                       switch (ctlr->type){
+                       case Type6005:
+                               memset(c, 0, sizeof(c));
+                               c[0] = 18;
+                               c[1] = 0;
+                               c[2] = 1;
+                               c[3] = 1;
+                               put16(c + 4, 2700);
+                               if((err = cmd(ctlr, 176, c, 4+2+2)) != nil)
+                                       return err;
+                               break;
+
+                       case Type2030:
+                       case Type2000:
+                               memset(c, 0, sizeof(c));
+                               c[0] = 18;
+                               c[1] = 0;
+                               c[2] = 1;
+                               c[3] = 1;
+                               if(ctlr->eeprom.rawtemp != 0){
+                                       put16(c + 4, ctlr->eeprom.temp);
+                                       put16(c + 6, ctlr->eeprom.rawtemp);
+                               } else{
+                                       put16(c + 4, 2700);
+                                       put16(c + 6, 2700);
+                               }
+                               put16(c + 8, ctlr->eeprom.volt);
+                               if((err = cmd(ctlr, 176, c, 4+2+2+2+2)) != nil)
+                                       return err;
+                               break;
+                       }
+
+                       if(ctlr->type == Type6005 || ctlr->type == Type6050){
+                               /* runtime DC calibration */
+                               memset(c, 0, sizeof(c));
+                               put32(c + 0*(5*4) + 0, 0xffffffff);
+                               put32(c + 0*(5*4) + 4, 1<<1);
+                               if((err = cmd(ctlr, 101, c, (((2*(5*4))+4)*2)+4)) != nil)
+                                       return err;
+                       }
+
+                       /* set tx antenna config */
+                       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;
+                       }
+               }
        }
 
-       if(ctlr->type != Type4965){
-               put32(c, ctlr->rfcfg.txantmask & 7);
-               cmd(ctlr, 152, c, 4);
+       return nil;
+}
+
+static char*
+loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size)
+{
+       uchar *dma;
+       char *err;
+
+       dma = mallocalign(size, 16, 0, 0);
+       if(dma == nil)
+               return "no memory for dma";
+       memmove(dma, data, size);
+       coherence();
+       if((err = niclock(ctlr)) != 0){
+               free(dma);
+               return err;
+       }
+       csr32w(ctlr, FhTxConfig + 9*32, 0);
+       csr32w(ctlr, FhSramAddr + 9*4, dst);
+       csr32w(ctlr, FhTfbdCtrl0 + 9*8, PCIWADDR(dma));
+       csr32w(ctlr, FhTfbdCtrl1 + 9*8, size);
+       csr32w(ctlr, FhTxBufStatus + 9*32,
+               (1<<FhTxBufStatusTbNumShift) |
+               (1<<FhTxBufStatusTbIdxShift) |
+               FhTxBufStatusTfbdValid);
+       csr32w(ctlr, FhTxConfig + 9*32, FhTxConfigDmaEna | FhTxConfigCirqHostEndTfd);
+       nicunlock(ctlr);
+       if(irqwait(ctlr, Ifhtx|Ierr, 5000) != Ifhtx){
+               free(dma);
+               return "dma error / timeout";
        }
+       free(dma);
+       return 0;
+}
+
+static char*
+boot(Ctlr *ctlr)
+{
+       int i, n, size;
+       uchar *p, *dma;
+       FWImage *fw;
+       char *err;
+
+       fw = ctlr->fw;
+
+       if(fw->boot.text.size == 0){
+               if(ctlr->calib.done == 0){
+                       if((err = loadfirmware1(ctlr, 0x00000000, fw->init.text.data, fw->init.text.size)) != nil)
+                               return err;
+                       if((err = loadfirmware1(ctlr, 0x00800000, fw->init.data.data, fw->init.data.size)) != nil)
+                               return err;
+                       csr32w(ctlr, Reset, 0);
+                       if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
+                               return "init firmware boot failed";
+                       if((err = postboot(ctlr)) != nil)
+                               return err;
+                       if((err = reset(ctlr)) != nil)
+                               return err;
+               }
+               if((err = loadfirmware1(ctlr, 0x00000000, fw->main.text.data, fw->main.text.size)) != nil)
+                       return err;
+               if((err = loadfirmware1(ctlr, 0x00800000, fw->main.data.data, fw->main.data.size)) != nil)
+                       return err;
+               csr32w(ctlr, Reset, 0);
+               if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
+                       return "main firmware boot failed";
+               return postboot(ctlr);
+       }
+
+       size = ROUND(fw->init.data.size, 16) + ROUND(fw->init.text.size, 16);
+       dma = mallocalign(size, 16, 0, 0);
+       if(dma == nil)
+               return "no memory for dma";
+
+       if((err = niclock(ctlr)) != nil){
+               free(dma);
+               return err;
+       }
+
+       p = dma;
+       memmove(p, fw->init.data.data, fw->init.data.size);
+       coherence();
+       prphwrite(ctlr, BsmDramDataAddr, PCIWADDR(p) >> 4);
+       prphwrite(ctlr, BsmDramDataSize, fw->init.data.size);
+       p += ROUND(fw->init.data.size, 16);
+       memmove(p, fw->init.text.data, fw->init.text.size);
+       coherence();
+       prphwrite(ctlr, BsmDramTextAddr, PCIWADDR(p) >> 4);
+       prphwrite(ctlr, BsmDramTextSize, fw->init.text.size);
+
+       nicunlock(ctlr);
+       if((err = niclock(ctlr)) != nil){
+               free(dma);
+               return err;
+       }
+
+       p = fw->boot.text.data;
+       n = fw->boot.text.size/4;
+       for(i=0; i<n; i++, p += 4)
+               prphwrite(ctlr, BsmSramBase+i*4, get32(p));
+
+       prphwrite(ctlr, BsmWrMemSrc, 0);
+       prphwrite(ctlr, BsmWrMemDst, 0);
+       prphwrite(ctlr, BsmWrDwCount, n);
+
+       prphwrite(ctlr, BsmWrCtrl, 1<<31);
+
+       for(i=0; i<1000; i++){
+               if((prphread(ctlr, BsmWrCtrl) & (1<<31)) == 0)
+                       break;
+               delay(10);
+       }
+       if(i == 1000){
+               nicunlock(ctlr);
+               free(dma);
+               return "bootcode timeout";
+       }
+
+       prphwrite(ctlr, BsmWrCtrl, 1<<30);
+       nicunlock(ctlr);
+
+       csr32w(ctlr, Reset, 0);
+       if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){
+               free(dma);
+               return "init firmware boot failed";
+       }
+       free(dma);
+
+       size = ROUND(fw->main.data.size, 16) + ROUND(fw->main.text.size, 16);
+       dma = mallocalign(size, 16, 0, 0);
+       if(dma == nil)
+               return "no memory for dma";
+       if((err = niclock(ctlr)) != nil){
+               free(dma);
+               return err;
+       }
+       p = dma;
+       memmove(p, fw->main.data.data, fw->main.data.size);
+       coherence();
+       prphwrite(ctlr, BsmDramDataAddr, PCIWADDR(p) >> 4);
+       prphwrite(ctlr, BsmDramDataSize, fw->main.data.size);
+       p += ROUND(fw->main.data.size, 16);
+       memmove(p, fw->main.text.data, fw->main.text.size);
+       coherence();
+       prphwrite(ctlr, BsmDramTextAddr, PCIWADDR(p) >> 4);
+       prphwrite(ctlr, BsmDramTextSize, fw->main.text.size | (1<<31));
+       nicunlock(ctlr);
+
+       if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){
+               free(dma);
+               return "main firmware boot failed";
+       }
+       free(dma);
+       return postboot(ctlr);
+}
+
+static int
+txqready(void *arg)
+{
+       TXQ *q = arg;
+       return q->n < Ntxqmax;
+}
+
+static char*
+qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
+{
+       uchar *d, *c;
+       TXQ *q;
+
+       assert(qid < nelem(ctlr->tx));
+       assert(size <= Tcmdsize-4);
+
+       ilock(ctlr);
+       q = &ctlr->tx[qid];
+       while(q->n >= Ntxqmax && !ctlr->broken){
+               iunlock(ctlr);
+               qlock(q);
+               if(!waserror()){
+                       tsleep(q, txqready, q, 5);
+                       poperror();
+               }
+               qunlock(q);
+               ilock(ctlr);
+       }
+       if(ctlr->broken){
+               iunlock(ctlr);
+               return "qcmd: broken";
+       }
+       q->n++;
+
+       q->lastcmd = code;
+       q->b[q->i] = block;
+       c = q->c + q->i * Tcmdsize;
+       d = q->d + q->i * Tdscsize;
+
+       /* build command */
+       c[0] = code;
+       c[1] = 0;       /* flags */
+       c[2] = q->i;
+       c[3] = qid;
+
+       if(size > 0)
+               memmove(c+4, data, size);
+
+       size += 4;
+
+       /* build descriptor */
+       *d++ = 0;
+       *d++ = 0;
+       *d++ = 0;
+       *d++ = 1 + (block != nil); /* nsegs */
+       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, size << 4);
+       }
+
+       coherence();
+
+       q->i = (q->i+1) % Ntx;
+       csr32w(ctlr, HbusTargWptr, (qid<<8) | q->i);
+
+       iunlock(ctlr);
+
+       return nil;
+}
+
+static int
+txqempty(void *arg)
+{
+       TXQ *q = arg;
+       return q->n == 0;
+}
+
+static char*
+flushq(Ctlr *ctlr, uint qid)
+{
+       TXQ *q;
+       int i;
+
+       q = &ctlr->tx[qid];
+       qlock(q);
+       for(i = 0; i < 200 && !ctlr->broken; i++){
+               if(txqempty(q)){
+                       qunlock(q);
+                       return nil;
+               }
+               if(!waserror()){
+                       tsleep(q, txqempty, q, 10);
+                       poperror();
+               }
+       }
+       qunlock(q);
+       if(ctlr->broken)
+               return "flushq: broken";
+       return "flushq: timeout";
+}
+
+static char*
+cmd(Ctlr *ctlr, uint code, uchar *data, int size)
+{
+       char *err;
+
+       if(0) print("cmd %ud\n", code);
+       if((err = qcmd(ctlr, 4, code, data, size, nil)) != nil)
+               return err;
+       return flushq(ctlr, 4);
+}
+
+static void
+setled(Ctlr *ctlr, int which, int on, int off)
+{
+       uchar c[8];
+
+       csr32w(ctlr, Led, csr32r(ctlr, Led) & ~LedBsmCtrl);
+
+       memset(c, 0, sizeof(c));
+       put32(c, 10000);
+       c[4] = which;
+       c[5] = on;
+       c[6] = off;
+       cmd(ctlr, 72, c, sizeof(c));
 }
 
 static void
@@ -1016,42 +1841,85 @@ addnode(Ctlr *ctlr, uchar id, uchar *addr)
                p += 8;         /* tcs */
                p += 8;         /* rxmic */
                p += 8;         /* txmic */
-               p += 4;         /* htflags */
-               p += 4;         /* mask */
-               p += 2;         /* disable tid */
-               p += 2;         /* reserved */
-               p++;            /* add ba tid */
-               p++;            /* del ba tid */
-               p += 2;         /* add ba ssn */
-               p += 4;         /* reserved */
        }
+       p += 4;         /* htflags */
+       p += 4;         /* mask */
+       p += 2;         /* disable tid */
+       p += 2;         /* reserved */
+       p++;            /* add ba tid */
+       p++;            /* del ba tid */
+       p += 2;         /* add ba ssn */
+       p += 4;         /* reserved */
        cmd(ctlr, 24, c, p - c);
 }
 
-void
-rxon(Ether *edev)
+static void
+rxon(Ether *edev, Wnode *bss)
 {
        uchar c[Tcmdsize], *p;
+       int filter, flags;
        Ctlr *ctlr;
+       char *err;
 
        ctlr = edev->ctlr;
+       filter = FilterNoDecrypt | FilterMulticast | FilterBeacon;
+       if(ctlr->prom){
+               filter |= FilterPromisc;
+               if(bss != nil)
+                       ctlr->channel = bss->channel;
+               bss = nil;
+       }
+       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;
+               if(ctlr->aid != 0){
+                       filter |= FilterBSS;
+                       filter &= ~FilterBeacon;
+                       ctlr->bssnodeid = -1;
+               } else
+                       ctlr->bcastnodeid = -1;
+       } else {
+               memmove(ctlr->bssid, edev->bcast, Eaddrlen);
+               ctlr->aid = 0;
+               ctlr->bcastnodeid = -1;
+               ctlr->bssnodeid = -1;
+       }
+
+       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 */
-       p += 8;                                 /* bssid */
+       memmove(p, ctlr->bssid, 6); p += 8;     /* bssid */
        memmove(p, edev->ea, 6); p += 8;        /* wlap */
-       *p++ = 3;                               /* mode */
+       *p++ = 3;                               /* mode (STA) */
        *p++ = 0;                               /* air (?) */
        /* rxchain */
        put16(p, ((ctlr->rfcfg.rxantmask & 7)<<1) | (2<<10) | (2<<12));
        p += 2;
        *p++ = 0xff;                            /* ofdm mask (not yet negotiated) */
        *p++ = 0x0f;                            /* cck mask (not yet negotiated) */
-       p += 2;                                 /* associd (?) */
-       put32(p, (1<<15)|(1<<30)|(1<<0));       /* flags (TSF | CTS_TO_SELF | 24GHZ) */
+       put16(p, ctlr->aid & 0x3fff);
+       p += 2;                                 /* aid */
+       put32(p, flags);
        p += 4;
-       put32(p, 4|1);                          /* filter (MULTICAST|PROMISC) */
+       put32(p, filter);
        p += 4;
-       *p++ = ctlr->channel;                   /* chan */
+       *p++ = ctlr->channel;
        p++;                                    /* reserved */
        *p++ = 0xff;                            /* ht single mask */
        *p++ = 0xff;                            /* ht dual mask */
@@ -1061,7 +1929,19 @@ rxon(Ether *edev)
                put16(p, 0); p += 2;            /* acquisition */
                p += 2;                         /* reserved */
        }
-       cmd(ctlr, 16, c, p - c);
+       if((err = cmd(ctlr, 16, c, p - c)) != nil){
+               print("rxon: %s\n", err);
+               return;
+       }
+
+       if(ctlr->bcastnodeid == -1){
+               ctlr->bcastnodeid = (ctlr->type != Type4965) ? 15 : 31;
+               addnode(ctlr, ctlr->bcastnodeid, edev->bcast);
+       }
+       if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){
+               ctlr->bssnodeid = 0;
+               addnode(ctlr, ctlr->bssnodeid, bss->bssid);
+       }
 }
 
 static struct ratetab {
@@ -1069,10 +1949,11 @@ static struct ratetab {
        uchar   plcp;
        uchar   flags;
 } ratetab[] = {
-       {   2,  10, 1<<1 },
-       {   4,  20, 1<<1 },
-       {  11,  55, 1<<1 },
-       {  22, 110, 1<<1 },
+       {   2,  10, RFlagCCK },
+       {   4,  20, RFlagCCK },
+       {  11,  55, RFlagCCK },
+       {  22, 110, RFlagCCK },
+
        {  12, 0xd, 0 },
        {  18, 0xf, 0 },
        {  24, 0x5, 0 },
@@ -1084,26 +1965,118 @@ 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,
+       TFlagNeedCTS            = 1<<2,
+       TFlagNeedACK            = 1<<3,
+       TFlagLinkq              = 1<<4,
+       TFlagImmBa              = 1<<6,
+       TFlagFullTxOp           = 1<<7,
+       TFlagBtDis              = 1<<12,
+       TFlagAutoSeq            = 1<<13,
+       TFlagMoreFrag           = 1<<14,
+       TFlagInsertTs           = 1<<16,
+       TFlagNeedPadding        = 1<<20,
+};
+
 static void
-transmit(Wifi *wifi, Wnode *, Block *b)
+transmit(Wifi *wifi, Wnode *wn, Block *b)
 {
+       int flags, nodeid, rate, ant;
        uchar c[Tcmdsize], *p;
+       Ether *edev;
        Ctlr *ctlr;
+       Wifipkt *w;
+       char *err;
+
+       edev = wifi->ether;
+       ctlr = edev->ctlr;
+
+       qlock(ctlr);
+       if(ctlr->attached == 0 || ctlr->broken){
+               qunlock(ctlr);
+               freeb(b);
+               return;
+       }
+
+       if((wn->channel != ctlr->channel)
+       || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)))
+               rxon(edev, wn);
+
+       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;
 
-       ctlr = wifi->ether->ctlr;
+               if(BLEN(b) > 512-4)
+                       flags |= TFlagNeedRTS;
+
+               if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){
+                       nodeid = ctlr->bssnodeid;
+                       p = wn->actrate;
+               }
+
+               if(flags & (TFlagNeedRTS|TFlagNeedCTS)){
+                       if(ctlr->type != Type4965){
+                               flags &= ~(TFlagNeedRTS|TFlagNeedCTS);
+                               flags |= TFlagNeedProtection;
+                       } else
+                               flags |= TFlagFullTxOp;
+               }
+       }
+       if(p >= wifi->rates)
+               rate = p - wifi->rates;
+       else
+               rate = 0;
+       qunlock(ctlr);
+
+       /* select first available antenna */
+       ant = ctlr->rfcfg.txantmask & 7;
+       ant |= (ant == 0);
+       ant = ((ant - 1) & ant) ^ ant;
 
        memset(p = c, 0, sizeof(c));
        put16(p, BLEN(b));
        p += 2;
        p += 2;         /* lnext */
-       put32(p, 0);    /* flags */
+       put32(p, flags);
        p += 4;
        put32(p, 0);
        p += 4;         /* scratch */
-       *p++ = ratetab[2].plcp;                 /* plcp */
-       *p++ = ratetab[2].flags | (1<<6);       /* rflags */
+
+       *p++ = ratetab[rate].plcp;
+       *p++ = ratetab[rate].flags | (ant<<6);
+
        p += 2;         /* xflags */
-       *p++ = 15;      /* id (5000 only) */
+       *p++ = nodeid;
        *p++ = 0;       /* security */
        *p++ = 0;       /* linkq */
        p++;            /* reserved */
@@ -1112,31 +2085,21 @@ transmit(Wifi *wifi, Wnode *, Block *b)
        p += 2;         /* reserved */
        put32(p, ~0);   /* lifetime */
        p += 4;
-       /* scratch ptr? not clear what this is for */
+
+       /* BUG: scratch ptr? not clear what this is for */
        put32(p, PCIWADDR(ctlr->kwpage));
        p += 5;
+
        *p++ = 60;      /* rts ntries */
        *p++ = 15;      /* data ntries */
        *p++ = 0;       /* tid */
        put16(p, 0);    /* timeout */
        p += 2;
        p += 2;         /* txop */
-       qcmd(ctlr, 0, 28, c, p - c, b);
-}
-
-static int
-rbplant(Ctlr *ctlr, int i)
-{
-       Block *b;
-
-       b = iallocb(Rbufsize + 256);
-       if(b == nil)
-               return -1;
-       b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256);
-       memset(b->rp, 0, Rdscsize);
-       ctlr->rx.b[i] = b;
-       ctlr->rx.p[i] = PCIWADDR(b->rp) >> 8;
-       return 0;
+       if((err = qcmd(ctlr, 0, 28, c, p - c, b)) != nil){
+               print("transmit: %s\n", err);
+               freeb(b);
+       }
 }
 
 static long
@@ -1145,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;
@@ -1165,22 +2132,68 @@ static void
 setoptions(Ether *edev)
 {
        Ctlr *ctlr;
-       char buf[64];
        int i;
 
        ctlr = edev->ctlr;
-       ctlr->channel = 3;
-       for(i = 0; i < edev->nopt; i++){
-               if(strncmp(edev->opt[i], "channel=", 8) == 0)
-                       ctlr->channel = atoi(edev->opt[i]+8);
-               else
-               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
+iwlpromiscuous(void *arg, int on)
+{
+       Ether *edev;
+       Ctlr *ctlr;
+
+       edev = arg;
+       ctlr = edev->ctlr;
+       qlock(ctlr);
+       ctlr->prom = on;
+       rxon(edev, ctlr->wifi->bss);
+       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);
        }
 }
 
@@ -1190,24 +2203,43 @@ iwlattach(Ether *edev)
        FWImage *fw;
        Ctlr *ctlr;
        char *err;
-       RXQ *rx;
-       TXQ *tx;
-       int i, q;
 
        ctlr = edev->ctlr;
        eqlock(ctlr);
        if(waserror()){
+               print("#l%d: %s\n", edev->ctlrno, up->errstr);
+               if(ctlr->power)
+                       poweroff(ctlr);
                qunlock(ctlr);
                nexterror();
        }
        if(ctlr->attached == 0){
-               if(ctlr->wifi == nil)
+               if((csr32r(ctlr, Gpc) & RfKill) == 0)
+                       error("wifi disabled by switch");
+
+               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("iwn-5000");
-                       print("#l%d: firmware: rev %ux, build %ud, size %ux+%ux+%ux+%ux+%ux\n",
-                               edev->ctlrno,
+                       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, fn,
                                fw->rev, fw->build,
                                fw->main.text.size, fw->main.data.size,
                                fw->init.text.size, fw->init.data.size,
@@ -1215,112 +2247,21 @@ iwlattach(Ether *edev)
                        ctlr->fw = fw;
                }
 
-               rx = &ctlr->rx;
-               rx->i = 0;
-               if(rx->b == nil)
-                       rx->b = malloc(sizeof(Block*) * Nrx);
-               if(rx->p == nil)
-                       rx->p = mallocalign(sizeof(u32int) * Nrx, 256, 0, 0);
-               if(rx->s == nil)
-                       rx->s = mallocalign(Rstatsize, 16, 0, 0);
-               if(rx->b == nil || rx->p == nil || rx->s == nil)
-                       error("no memory for rx ring");
-               memset(rx->s, 0, Rstatsize);
-               for(i=0; i<Nrx; i++){
-                       rx->p[i] = 0;
-                       if(rx->b[i] != nil){
-                               freeb(rx->b[i]);
-                               rx->b[i] = nil;
-                       }
-                       if(rbplant(ctlr, i) < 0)
-                               error("no memory for rx descriptors");
-               }
-
-               for(q=0; q<nelem(ctlr->tx); q++){
-                       tx = &ctlr->tx[q];
-                       tx->i = 0;
-                       tx->n = 0;
-                       if(tx->b == nil)
-                               tx->b = malloc(sizeof(Block*) * Ntx);
-                       if(tx->d == nil)
-                               tx->d = mallocalign(Tdscsize * Ntx, 256, 0, 0);
-                       if(tx->c == nil)
-                               tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0);
-                       if(tx->b == nil || tx->d == nil || tx->c == nil)
-                               error("no memory for tx ring");
-                       memset(tx->d, 0, Tdscsize * Ntx);
-               }
-
-               if(ctlr->sched.s == nil)
-                       ctlr->sched.s = mallocalign(512 * nelem(ctlr->tx) * 2, 1024, 0, 0);
-               if(ctlr->kwpage == nil)
-                       ctlr->kwpage = mallocalign(4096, 4096, 0, 0);
-
-               if((err = niclock(ctlr)) != nil)
-                       error(err);
-               prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain);
-               nicunlock(ctlr);
-
-               csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi);
-
-               if((err = niclock(ctlr)) != nil)
-                       error(err);
-               prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | EarlyPwroffDis);
-               nicunlock(ctlr);
-
-               if((err = niclock(ctlr)) != nil)
-                       error(err);
-               csr32w(ctlr, FhRxConfig, 0);
-               csr32w(ctlr, FhRxWptr, 0);
-               csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p) >> 8);
-               csr32w(ctlr, FhStatusWptr, PCIWADDR(ctlr->rx.s) >> 4);
-               csr32w(ctlr, FhRxConfig,
-                       FhRxConfigEna | 
-                       FhRxConfigIgnRxfEmpty |
-                       FhRxConfigIrqDstHost | 
-                       FhRxConfigSingleFrame |
-                       (Nrxlog << FhRxConfigNrbdShift));
-               csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7);
-               nicunlock(ctlr);
-
-               if((err = niclock(ctlr)) != nil)
-                       error(err);
-               prphwrite(ctlr, SchedTxFact5000, 0);
-               csr32w(ctlr, FhKwAddr, PCIWADDR(ctlr->kwpage) >> 4);
-               for(q=0; q<nelem(ctlr->tx); q++)
-                       csr32w(ctlr, FhCbbcQueue + q*4, PCIWADDR(ctlr->tx[q].d) >> 8);
-               nicunlock(ctlr);
-               for(i=0; i<8; i++)
-                       csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna);
-               csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
-               csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
-
-               ctlr->ie = Idefmask;
-               csr32w(ctlr, Imr, ctlr->ie);
-               csr32w(ctlr, Isr, ~0);
-
-               if(ctlr->type >= Type6000)
-                       csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff);
-
-               if((err = loadfirmware1(ctlr, 0x00000000, ctlr->fw->main.text.data, ctlr->fw->main.text.size)) != nil)
+               if((err = reset(ctlr)) != nil)
                        error(err);
-               if((err = loadfirmware1(ctlr, 0x00800000, ctlr->fw->main.data.data, ctlr->fw->main.data.size)) != nil)
+               if((err = boot(ctlr)) != nil)
                        error(err);
 
-               csr32w(ctlr, Reset, 0);
-               if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
-                       error("firmware boot failed");
-
-               postboot(ctlr);
+               ctlr->bcastnodeid = -1;
+               ctlr->bssnodeid = -1;
+               ctlr->channel = 1;
+               ctlr->aid = 0;
 
                setoptions(edev);
 
-               rxon(edev);
-               addnode(ctlr, 15, edev->bcast);
-
-               edev->prom = 1;
-               edev->link = 1;
                ctlr->attached = 1;
+
+               kproc("iwlrecover", iwlrecover, edev);
        }
        qunlock(ctlr);
        poperror();
@@ -1330,14 +2271,16 @@ static void
 receive(Ctlr *ctlr)
 {
        Block *b, *bb;
-       uchar *d, *dd, *cc;
+       uchar *d;
        RXQ *rx;
        TXQ *tx;
        uint hw;
 
        rx = &ctlr->rx;
-       if(rx->s == nil || rx->b == nil)
+       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;
@@ -1354,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);
@@ -1385,10 +2324,31 @@ 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)
-                       break;
-               case 103:       /* calibration done (Type5000 only)
+               case 102:       /* calibration result (Type5000 only) */
+                       if(len < 4)
+                               break;
+                       idx = d[0];
+                       if(idx >= nelem(ctlr->calib.cmd))
+                               break;
+                       if(rbplant(ctlr, rx->i) < 0)
+                               break;
+                       if(ctlr->calib.cmd[idx] != nil)
+                               freeb(ctlr->calib.cmd[idx]);
+                       b->rp = d;
+                       b->wp = d + len;
+                       ctlr->calib.cmd[idx] = b;
+                       continue;
+               case 103:       /* calibration done (Type5000 only) */
+                       ctlr->calib.done = 1;
                        break;
                case 130:       /* start scan */
                        break;
@@ -1405,9 +2365,10 @@ receive(Ctlr *ctlr)
                case 192:       /* rx phy */
                        break;
                case 195:       /* rx done */
-                       if(d + 60 > b->lim)
+                       if(d + 2 > b->lim)
                                break;
-                       d += 60;
+                       d += d[1];
+                       d += 56;
                case 193:       /* mpdu rx done */
                        if(d + 4 > b->lim)
                                break;
@@ -1427,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
@@ -1457,20 +2418,29 @@ iwlinterrupt(Ureg*, void *arg)
        if((isr & (Iswrx | Ifhrx | Irxperiodic)) || (fhisr & Ifhrx))
                receive(ctlr);
        if(isr & Ierr){
-               iprint("#l%d: fatal firmware error\n", edev->ctlrno);
+               ctlr->broken = 1;
+               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
@@ -1491,23 +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 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");
@@ -1522,7 +2502,6 @@ iwlpci(void)
                }
                ctlr->nic = mem;
                ctlr->pdev = pdev;
-               ctlr->type = (csr32r(ctlr, Rev) >> 4) & 0xF;
 
                if(iwlhead != nil)
                        iwltail->link = ctlr;
@@ -1557,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->promiscuous = nil;
-       edev->multicast = nil;
-       edev->mbps = 10;
+       edev->shutdown = iwlshutdown;
+       edev->promiscuous = iwlpromiscuous;
+       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;
 }