ref: 693485e9c4eab63cf46f38783c2e5f8097ae2bc9
parent: a87b6909bc209e04a9660b9b2bacea84151fb15d
author: cinap_lenrek <[email protected]>
date: Sun Sep 6 12:53:39 EDT 2020
etheriwl: add support for Intel Wireless AC 8265
--- a/sys/src/9/pc/etheriwl.c
+++ b/sys/src/9/pc/etheriwl.c
@@ -2,8 +2,8 @@
* Intel WiFi Link driver.
*
* Written without any documentation but Damien Bergaminis
- * OpenBSD iwn(4) driver sources. Requires intel firmware
- * to be present in /lib/firmware/iwn-* on attach.
+ * OpenBSD iwn(4) and iwm(4) driver sources. Requires intel
+ * firmware to be present in /lib/firmware/iwn-* on attach.
*/
#include "u.h"
@@ -34,18 +34,38 @@
Tbufsize = 4*1024,
Tdscsize = 128,
Tcmdsize = 140,
+
+ FWPageshift = 12,
+ FWPagesize = 1<<FWPageshift,
+ FWBlockshift = 3,
+ FWBlockpages = 1<<FWBlockshift,
+ FWBlocksize = 1<<(FWBlockshift + FWPageshift),
};
/* registers */
enum {
Cfg = 0x000, /* config register */
+ CfgMacDashShift = 0,
+ CfgMacDashMask = 3<<CfgMacDashShift,
+ CfgMacStepShift = 2,
+ CfgMacStepMask = 3<<CfgMacStepShift,
+
MacSi = 1<<8,
RadioSi = 1<<9,
+
+ CfgPhyTypeShift = 10,
+ CfgPhyTypeMask = 3<<CfgPhyTypeShift,
+ CfgPhyDashShift = 12,
+ CfgPhyDashMask = 3<<CfgPhyDashShift,
+ CfgPhyStepShift = 14,
+ CfgPhyStepMask = 3<<CfgPhyStepShift,
+
EepromLocked = 1<<21,
NicReady = 1<<22,
HapwakeL1A = 1<<23,
PrepareDone = 1<<25,
Prepare = 1<<27,
+ EnablePme = 1<<28,
Isr = 0x008, /* interrupt status */
Imr = 0x00c, /* interrupt mask */
@@ -107,6 +127,9 @@
ShadowRegCtrl = 0x0a8,
+ MboxSet = 0x088,
+ MboxSetOsAlive = 1<<5,
+
Giochicken = 0x100,
L1AnoL0Srx = 1<<23,
DisL0Stimer = 1<<29,
@@ -127,6 +150,8 @@
PrphRdata = 0x450,
HbusTargWptr = 0x460,
+
+ UcodeLoadStatus = 0x1af0,
};
/*
@@ -139,7 +164,11 @@
FhKwAddr = 0x197c,
FhSramAddr = 0x19a4, // +q*4
- FhCbbcQueue = 0x19d0, // +q*4
+
+ FhCbbcQueue0 = 0x19d0, // +q*4
+ FhCbbcQueue16 = 0x1bf0, // +q*4
+ FhCbbcQueue20 = 0x1b20, // +q*4
+
FhStatusWptr = 0x1bc0,
FhRxBase = 0x1bc4,
FhRxWptr = 0x1bc8,
@@ -166,7 +195,9 @@
FhTxBufStatusTfbdValid = 3,
FhTxChicken = 0x1e98,
+
FhTxStatus = 0x1eb0,
+ FhTxErrors = 0x1eb8,
};
/*
@@ -198,6 +229,13 @@
BsmDramDataAddr = 0x3498,
BsmDramDataSize = 0x349c,
BsmSramBase = 0x3800,
+
+ /* 8000 family */
+ ReleaseCpuReset = 0x300c,
+ CpuResetBit = 0x1000000,
+
+ LmpmChick = 0xa01ff8,
+ ExtAddr = 1,
};
/*
@@ -214,13 +252,25 @@
SchedIntrMask4965 = SchedBase+0x0e4,
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,
+ SchedDramAddr = SchedBase+0x008,
+ SchedTxFact = SchedBase+0x010,
+ SchedQueueWrptr = SchedBase+0x018, // +q*4
+ SchedQueueRdptr = SchedBase+0x068, // +q*4
+ SchedQChainSel = SchedBase+0x0e8,
+ SchedIntrMask = SchedBase+0x108,
+
+ SchedQueueStatus = SchedBase+0x10c, // +q*4
+
+ SchedGpCtrl = SchedBase+0x1a8,
+ Enable31Queues = 1<<0,
+ AutoActiveMode = 1<<18,
+
+ SchedChainExtEn = SchedBase+0x244,
+ SchedAggrSel = SchedBase+0x248,
+ SchedEnCtrl = SchedBase+0x254,
+
+ SchedQueueRdptr20 = SchedBase+0x2b4, // +q*4
+ SchedQueueStatus20 = SchedBase+0x334, // +q*4
};
enum {
@@ -227,8 +277,11 @@
SchedCtxOff4965 = 0x380,
SchedCtxLen4965 = 416,
- SchedCtxOff5000 = 0x600,
- SchedCtxLen5000 = 512,
+ SchedCtxOff = 0x600, // +q*8
+
+ SchedSttsOff = 0x6A0, // +q*16
+
+ SchedTransTblOff = 0x7E0, // +q*2
};
enum {
@@ -236,6 +289,7 @@
FilterCtl = 1<<1,
FilterMulticast = 1<<2,
FilterNoDecrypt = 1<<3,
+ FilterNoDecryptMcast = 1<<4,
FilterBSS = 1<<5,
FilterBeacon = 1<<6,
};
@@ -253,18 +307,44 @@
RFlagCTSToSelf = 1<<30,
};
+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,
+};
+
+enum {
+ CmdAdd = 1,
+ CmdModify,
+ CmdRemove,
+};
+
typedef struct FWInfo FWInfo;
typedef struct FWImage FWImage;
typedef struct FWSect FWSect;
+typedef struct FWBlock FWBlock;
+typedef struct FWMem FWMem;
typedef struct TXQ TXQ;
typedef struct RXQ RXQ;
+typedef struct Station Station;
+
typedef struct Ctlr Ctlr;
struct FWSect
{
uchar *data;
+ uint addr;
uint size;
};
@@ -271,29 +351,75 @@
struct FWImage
{
struct {
+ int nsect;
+ union {
+ struct {
+ FWSect text;
+ FWSect data;
+ };
+ FWSect sect[16];
+ };
+ struct {
+ u32int flowmask;
+ u32int eventmask;
+ } defcalib;
+ } init, main;
+
+ struct {
FWSect text;
- FWSect data;
- } init, main, boot;
+ } boot;
uint rev;
uint build;
char descr[64+1];
+
+ u32int capa[4];
+ u32int api[4];
+
+ u32int physku;
+
+ u32int pagedmemsize;
+
uchar data[];
};
struct FWInfo
{
- uchar major;
- uchar minjor;
+ u32int major;
+ u32int minor;
uchar type;
uchar subtype;
+ u32int scdptr;
+ u32int regptr;
u32int logptr;
u32int errptr;
u32int tstamp;
- u32int valid;
+
+ struct {
+ u32int major;
+ u32int minor;
+ u32int errptr;
+ u32int logptr;
+ } umac;
};
+struct FWBlock
+{
+ uint size;
+ uchar *p;
+};
+
+struct FWMem
+{
+ uchar *css;
+
+ uint npage;
+ uint nblock;
+
+ FWBlock block[32];
+};
+
struct TXQ
{
uint n;
@@ -316,6 +442,11 @@
uchar *s;
};
+struct Station
+{
+ int id;
+};
+
struct Ctlr {
Lock;
QLock;
@@ -323,11 +454,16 @@
Ctlr *link;
uvlong port;
Pcidev *pdev;
+ Ether *edev;
Wifi *wifi;
+ char *fwname;
+ int family;
int type;
+ uint step;
+ uint dash;
+
int power;
- int active;
int broken;
int attached;
@@ -336,10 +472,18 @@
u32int *nic;
uchar *kwpage;
- /* assigned node ids in hardware node table or -1 if unassigned */
- int bcastnodeid;
- int bssnodeid;
+ /* assigned sta ids in hardware sta table or -1 if unassigned */
+ Station bcast;
+ Station bss;
+ u32int rxflags;
+ u32int rxfilter;
+
+ int phyid;
+ int macid;
+ int bindid;
+ int timeid;
+
/* current receiver settings */
uchar bssid[Eaddrlen];
int channel;
@@ -346,9 +490,14 @@
int prom;
int aid;
+ uvlong systime;
+
RXQ rx;
- TXQ tx[20];
+ TXQ tx[7];
+ int ndma;
+ int ntxq;
+
struct {
Rendez;
u32int m;
@@ -359,6 +508,7 @@
uchar type;
uchar step;
uchar dash;
+ uchar pnum;
uchar txantmask;
uchar rxantmask;
} rfcfg;
@@ -379,7 +529,27 @@
} eeprom;
struct {
- Block *cmd[21];
+ u32int version;
+
+ void *buf;
+ int len;
+
+ int off;
+ int ret;
+ int type;
+ int sts;
+ } nvm;
+
+ struct {
+ union {
+ Block *cmd[21];
+ struct {
+ Block *cfg;
+ Block *nch;
+ Block *papd[9];
+ Block *txp[9];
+ };
+ };
int done;
} calib;
@@ -390,6 +560,8 @@
FWInfo fwinfo;
FWImage *fw;
+
+ FWMem fwmem;
};
/* controller types */
@@ -405,8 +577,49 @@
Type6005 = 11, /* also Centrino Advanced-N 6030, 6235 */
Type2030 = 12,
Type2000 = 16,
+
+ Type8265 = 35,
};
+static struct ratetab {
+ uchar rate;
+ uchar plcp;
+ uchar flags;
+} ratetab[] = {
+ { 2, 10, RFlagCCK },
+ { 4, 20, RFlagCCK },
+ { 11, 55, RFlagCCK },
+ { 22, 110, RFlagCCK },
+
+ { 12, 0xd, 0 },
+ { 18, 0xf, 0 },
+ { 24, 0x5, 0 },
+ { 36, 0x7, 0 },
+ { 48, 0x9, 0 },
+ { 72, 0xb, 0 },
+ { 96, 0x1, 0 },
+ { 108, 0x3, 0 },
+ { 120, 0x3, 0 }
+};
+
+static uchar iwlrates[] = {
+ 0x80 | 2,
+ 0x80 | 4,
+ 0x80 | 11,
+ 0x80 | 22,
+
+ 0x80 | 12,
+ 0x80 | 18,
+ 0x80 | 24,
+ 0x80 | 36,
+ 0x80 | 48,
+ 0x80 | 72,
+ 0x80 | 96,
+ 0x80 | 108,
+ 0x80 | 120,
+ 0
+};
+
static char *fwname[32] = {
[Type4965] "iwn-4965",
[Type5300] "iwn-5000",
@@ -441,6 +654,11 @@
*((u32int*)p) = v;
}
static void
+put64(uchar *p, uvlong v)
+{
+ *((u64int*)p) = v;
+}
+static void
put16(uchar *p, uint v){
*((u16int*)p) = v;
};
@@ -500,21 +718,105 @@
{
FWInfo *i;
- if(len < 32)
- return;
i = &ctlr->fwinfo;
- i->minjor = *d++;
- i->major = *d++;
- d += 2+8;
- i->type = *d++;
- i->subtype = *d++;
- d += 2;
- i->logptr = get32(d); d += 4;
- i->errptr = get32(d); d += 4;
- i->tstamp = get32(d); d += 4;
- i->valid = get32(d);
-};
+ switch(len){
+ case 1+1+2+1+1 + 1+1 + 1+1 + 2 + 4+4+4+4+4+4 + 4:
+ case 1+1+2+1+1 + 1+1 + 1+1 + 2 + 4+4+4+4+4+4 + 4 + 4+4 + 1+1 + 2 + 4+4:
+ i->minor = *d++;
+ i->major = *d++;
+ d += 2; // id
+ d++; // api minor
+ d++; // api major
+ i->subtype = *d++;
+ i->type = *d++;
+ d++; // mac
+ d++; // opt
+ d += 2; // reserved2
+
+ i->tstamp = get32(d); d += 4;
+ i->errptr = get32(d); d += 4;
+ i->logptr = get32(d); d += 4;
+ i->regptr = get32(d); d += 4;
+ d += 4; // dbgm_config_ptr
+ d += 4; // alive counter ptr
+
+ i->scdptr = get32(d); d += 4;
+
+ if(len < 1+1+2+1+1+1+1+1+1+2+4+4+4+4+4 + 4+4+4+1+1+2+4+4)
+ break;
+
+ d += 4; // fwrd addr
+ d += 4; // fwrd size
+
+ i->umac.minor = *d++;
+ i->umac.major = *d++;
+ d++; // id
+ d += 2;
+ i->umac.errptr = get32(d); d += 4;
+ i->umac.logptr = get32(d); d += 4;
+ break;
+
+ case 4+4 + 1+1 + 1+1 + 4+4+4+4+4+4 + 4 + 4+4 + 4+4+4+4:
+ i->minor = get32(d);
+ d += 4;
+ i->major = get32(d);
+ d += 4;
+ i->subtype = *d++;
+ i->type = *d++;
+ d++; // mac
+ d++; // opt
+
+ i->tstamp = get32(d); d += 4;
+ i->errptr = get32(d); d += 4;
+ i->logptr = get32(d); d += 4;
+ i->regptr = get32(d); d += 4;
+ d += 4; // dbgm_config_ptr
+ d += 4; // alive counter ptr
+
+ i->scdptr = get32(d);
+ d += 4;
+
+ d += 4; // fwrd addr
+ d += 4; // fwrd size
+
+ i->umac.minor = get32(d); d += 4;
+ i->umac.major = get32(d); d += 4;
+ i->umac.errptr = get32(d); d += 4;
+ i->umac.logptr = get32(d); d += 4;
+ break;
+
+ default:
+ if(len < 32)
+ break;
+ i->minor = *d++;
+ i->major = *d++;
+ d += 2+8;
+ i->type = *d++;
+ i->subtype = *d++;
+ d += 2;
+ i->logptr = get32(d); d += 4;
+ i->errptr = get32(d); d += 4;
+ i->tstamp = get32(d); d += 4;
+ }
+ USED(d);
+
+ if(0){
+ iprint("fwinfo: ver %ud.%ud type %ud.%ud\n",
+ i->major, i->minor, i->type, i->subtype);
+ iprint("fwinfo: scdptr=%.8ux\n", i->scdptr);
+ iprint("fwinfo: regptr=%.8ux\n", i->regptr);
+ iprint("fwinfo: logptr=%.8ux\n", i->logptr);
+ iprint("fwinfo: errptr=%.8ux\n", i->errptr);
+
+ iprint("fwinfo: ts=%.8ux\n", i->tstamp);
+
+ iprint("fwinfo: umac ver %ud.%ud\n", i->umac.major, i->umac.minor);
+ iprint("fwinfo: umac errptr %.8ux\n", i->umac.errptr);
+ iprint("fwinfo: umac logptr %.8ux\n", i->umac.logptr);
+ }
+}
+
static void
dumpctlr(Ctlr *ctlr)
{
@@ -522,6 +824,7 @@
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;
@@ -528,12 +831,22 @@
}
for(i=0; i<nelem(dump); i++)
dump[i] = memread(ctlr, ctlr->fwinfo.errptr + i*4);
- print( "error:\tid %ux, pc %ux,\n"
- "\tbranchlink %.8ux %.8ux, interruptlink %.8ux %.8ux,\n"
- "\terrordata %.8ux %.8ux, srcline %ud, tsf %ux, time %ux\n",
- dump[1], dump[2],
- dump[4], dump[3], dump[6], dump[5],
- dump[7], dump[8], dump[9], dump[10], dump[11]);
+
+ if(ctlr->family >= 7000){
+ print( "error:\tid %ux, trm_hw_status %.8ux %.8ux,\n"
+ "\tbranchlink2 %.8ux, interruptlink %.8ux %.8ux,\n"
+ "\terrordata %.8ux %.8ux %.8ux\n",
+ dump[1], dump[2], dump[3],
+ dump[4], dump[5], dump[6],
+ dump[7], dump[8], dump[9]);
+ } else {
+ print( "error:\tid %ux, pc %ux,\n"
+ "\tbranchlink %.8ux %.8ux, interruptlink %.8ux %.8ux,\n"
+ "\terrordata %.8ux %.8ux, srcline %ud, tsf %ux, time %ux\n",
+ dump[1], dump[2],
+ dump[4], dump[3], dump[6], dump[5],
+ dump[7], dump[8], dump[9], dump[10], dump[11]);
+ }
}
static char*
@@ -597,9 +910,15 @@
csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | NicReady);
for(i=0; i<5; i++){
if(csr32r(ctlr, Cfg) & NicReady)
- return 0;
+ goto Ready;
delay(10);
}
+
+ if(ctlr->family >= 7000){
+ csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) | (1<<31));
+ delay(1);
+ }
+
csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | Prepare);
for(i=0; i<15000; i++){
if((csr32r(ctlr, Cfg) & PrepareDone) == 0)
@@ -611,10 +930,14 @@
csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | NicReady);
for(i=0; i<5; i++){
if(csr32r(ctlr, Cfg) & NicReady)
- return 0;
+ goto Ready;
delay(10);
}
return "handover: timeout";
+Ready:
+ if(ctlr->family >= 7000)
+ csr32w(ctlr, MboxSet, csr32r(ctlr, MboxSet) | MboxSetOsAlive);
+ return nil;
}
static char*
@@ -638,9 +961,18 @@
int capoff;
char *err;
- /* Disable L0s exit timer (NMI bug workaround). */
- csr32w(ctlr, Giochicken, csr32r(ctlr, Giochicken) | DisL0Stimer);
+ if(ctlr->family >= 7000){
+ /* Reset entire device */
+ csr32w(ctlr, Reset, (1<<7));
+ delay(5);
+ }
+
+ if(ctlr->family < 8000){
+ /* Disable L0s exit timer (NMI bug workaround). */
+ csr32w(ctlr, Giochicken, csr32r(ctlr, Giochicken) | DisL0Stimer);
+ }
+
/* Don't wait for ICH L0s (ICH bug workaround). */
csr32w(ctlr, Giochicken, csr32r(ctlr, Giochicken) | L1AnoL0Srx);
@@ -659,27 +991,31 @@
csr32w(ctlr, Gio, csr32r(ctlr, Gio) & ~EnaL0S);
}
- if(ctlr->type != Type4965 && ctlr->type <= Type1000)
- csr32w(ctlr, AnaPll, csr32r(ctlr, AnaPll) | 0x00880300);
+ if(ctlr->family < 7000){
+ if(ctlr->type != Type4965 && ctlr->type <= Type1000)
+ csr32w(ctlr, AnaPll, csr32r(ctlr, AnaPll) | 0x00880300);
+ }
/* Wait for clock stabilization before accessing prph. */
if((err = clockwait(ctlr)) != nil)
return err;
- if((err = niclock(ctlr)) != nil)
- return err;
+ if(ctlr->family < 8000){
+ if((err = niclock(ctlr)) != nil)
+ return err;
- /* Enable DMA and BSM (Bootstrap State Machine). */
- if(ctlr->type == Type4965)
- prphwrite(ctlr, ApmgClkEna, DmaClkRqt | BsmClkRqt);
- else
- prphwrite(ctlr, ApmgClkEna, DmaClkRqt);
- delay(20);
+ /* Enable DMA and BSM (Bootstrap State Machine). */
+ if(ctlr->type == Type4965)
+ prphwrite(ctlr, ApmgClkEna, DmaClkRqt | BsmClkRqt);
+ else
+ prphwrite(ctlr, ApmgClkEna, DmaClkRqt);
+ delay(20);
- /* Disable L1-Active. */
- prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11));
+ /* Disable L1-Active. */
+ prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11));
- nicunlock(ctlr);
+ nicunlock(ctlr);
+ }
ctlr->power = 1;
@@ -700,14 +1036,14 @@
csr32w(ctlr, FhIsr, ~0);
/* Stop scheduler */
- if(ctlr->type != Type4965)
- prphwrite(ctlr, SchedTxFact5000, 0);
+ if(ctlr->family >= 7000 || ctlr->type != Type4965)
+ prphwrite(ctlr, SchedTxFact, 0);
else
prphwrite(ctlr, SchedTxFact4965, 0);
/* Stop TX ring */
if(niclock(ctlr) == nil){
- for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--){
+ for(i = 0; i < ctlr->ndma; i++){
csr32w(ctlr, FhTxConfig + i*32, 0);
for(j = 0; j < 200; j++){
if(csr32r(ctlr, FhTxStatus) & (0x10000<<i))
@@ -729,13 +1065,23 @@
nicunlock(ctlr);
}
- /* Disable DMA */
- if(niclock(ctlr) == nil){
- prphwrite(ctlr, ApmgClkDis, DmaClkRqt);
- nicunlock(ctlr);
+ if(ctlr->family <= 7000){
+ /* Disable DMA */
+ if(niclock(ctlr) == nil){
+ prphwrite(ctlr, ApmgClkDis, DmaClkRqt);
+ nicunlock(ctlr);
+ }
+ delay(5);
}
- delay(5);
+ if(ctlr->family >= 7000){
+ csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) | (1<<31));
+ csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | Prepare|EnablePme);
+ delay(1);
+ csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) & ~(1<<31));
+ delay(5);
+ }
+
/* Stop busmaster DMA activity. */
csr32w(ctlr, Reset, csr32r(ctlr, Reset) | (1<<9));
for(j = 0; j < 100; j++){
@@ -834,16 +1180,31 @@
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;
+ ctlr->type = csr32r(ctlr, Rev);
+ if(ctlr->family >= 8000){
+ ctlr->type &= 0xFFFF;
+ ctlr->dash = 0;
+ ctlr->step = ctlr->type & 15, ctlr->type >>= 4;
+ } else {
+ ctlr->type &= 0x1FF;
+ ctlr->dash = ctlr->type & 3, ctlr->type >>= 2;
+ ctlr->step = ctlr->type & 3, ctlr->type >>= 2;
+ if(fwname[ctlr->type] == nil){
+ print("iwl: unsupported controller type %d\n", ctlr->type);
+ return -1;
+ }
}
if((err = handover(ctlr)) != nil)
goto Err;
+
+ /* >= 7000 family needs firmware loaded to access NVM */
+ if(ctlr->family >= 7000)
+ return 0;
+
if((err = poweron(ctlr)) != nil)
goto Err;
+
if((csr32r(ctlr, EepromGp) & 0x7) == 0){
err = "bad rom signature";
goto Err;
@@ -930,6 +1291,7 @@
{
uchar *p, *e;
FWSect *s;
+ uint t, l;
memset(i, 0, sizeof(*i));
if(size < 4){
@@ -956,28 +1318,112 @@
altmask |= (uvlong)get32(p) << 32; p += 4;
while(alt > 0 && (altmask & (1ULL<<alt)) == 0)
alt--;
- while(p < e){
- FWSect dummy;
+ for(;p < e; p += (l + 3) & ~3){
+ if(p + 8 > e)
+ goto Tooshort;
- if((p + 2+2+4) > e)
+ t = get32(p), p += 4;
+ l = get32(p), p += 4;
+ if(p + l > e)
goto Tooshort;
- switch(get16(p)){
- case 1: s = &i->main.text; break;
- case 2: s = &i->main.data; break;
- case 3: s = &i->init.text; break;
- case 4: s = &i->init.data; break;
- case 5: s = &i->boot.text; break;
- default:s = &dummy;
+
+ if((t >> 16) != 0 && (t >> 16) != alt)
+ continue;
+
+ switch(t & 0xFFFF){
+ case 1:
+ s = &i->main.text;
+ if(i->main.nsect < 1)
+ i->main.nsect = 1;
+ s->addr = 0x00000000;
+ break;
+ case 2:
+ s = &i->main.data;
+ if(i->main.nsect < 2)
+ i->main.nsect = 2;
+ s->addr = 0x00800000;
+ break;
+ case 3:
+ s = &i->init.text;
+ if(i->init.nsect < 1)
+ i->init.nsect = 1;
+ s->addr = 0x00000000;
+ break;
+ case 4:
+ s = &i->init.data;
+ if(i->init.nsect < 2)
+ i->init.nsect = 2;
+ s->addr = 0x00800000;
+ break;
+ case 5:
+ s = &i->boot.text;
+ s->addr = 0x00000000;
+ break;
+ case 19:
+ if(i->main.nsect >= nelem(i->main.sect))
+ return "too many main sections";
+ s = &i->main.sect[i->main.nsect++];
+ goto Chunk;
+ case 20:
+ if(i->init.nsect >= nelem(i->init.sect))
+ return "too many init sections";
+ s = &i->init.sect[i->init.nsect++];
+ Chunk:
+ if(l < 4)
+ goto Tooshort;
+ s->addr = get32(p);
+ p += 4, l -= 4;
+ break;
+
+ case 22:
+ if(l < 12)
+ goto Tooshort;
+ switch(get32(p)){
+ case 0:
+ i->main.defcalib.flowmask = get32(p+4);
+ i->main.defcalib.eventmask = get32(p+8);
+ break;
+ case 1:
+ i->init.defcalib.flowmask = get32(p+4);
+ i->init.defcalib.eventmask = get32(p+8);
+ break;
+ }
+ continue;
+
+ case 23:
+ if(l < 4)
+ goto Tooshort;
+ i->physku = get32(p);
+ continue;
+
+ case 29:
+ if(l < 8)
+ goto Tooshort;
+ t = get32(p);
+ if(t >= nelem(i->api))
+ goto Tooshort;
+ i->api[t] = get32(p+4);
+ continue;
+ case 30:
+ if(l < 8)
+ goto Tooshort;
+ t = get32(p);
+ if(t >= nelem(i->capa))
+ goto Tooshort;
+ i->capa[t] = get32(p+4);
+ continue;
+
+ case 32:
+ if(l < 4)
+ goto Tooshort;
+ i->pagedmemsize = get32(p) & -FWPagesize;
+ continue;
+
+ default:
+ continue;
}
- p += 2;
- if(get16(p) != 0 && get16(p) != alt)
- s = &dummy;
- p += 2;
- s->size = get32(p); p += 4;
+ s->size = l;
s->data = p;
- if((p + s->size) > e)
- goto Tooshort;
- p += (s->size + 3) & ~3;
}
} else {
if(((i->rev>>8) & 0xFF) < 2)
@@ -987,11 +1433,13 @@
}
if((p + 5*4) > e)
goto Tooshort;
+
i->main.text.size = get32(p); p += 4;
i->main.data.size = get32(p); p += 4;
i->init.text.size = get32(p); p += 4;
i->init.data.size = get32(p); p += 4;
i->boot.text.size = get32(p); p += 4;
+
i->main.text.data = p; p += i->main.text.size;
i->main.data.data = p; p += i->main.data.size;
i->init.text.data = p; p += i->init.text.size;
@@ -999,6 +1447,13 @@
i->boot.text.data = p; p += i->boot.text.size;
if(p > e)
goto Tooshort;
+
+ i->main.nsect = 2;
+ i->init.nsect = 2;
+ i->main.text.addr = 0x00000000;
+ i->main.data.addr = 0x00800000;
+ i->init.text.addr = 0x00000000;
+ i->init.data.addr = 0x00800000;
}
return 0;
}
@@ -1099,12 +1554,35 @@
}
static char*
-initring(Ctlr *ctlr)
+initmem(Ctlr *ctlr)
{
RXQ *rx;
TXQ *tx;
int i, q;
+ if(ctlr->fw->pagedmemsize > 0){
+ ctlr->fwmem.npage = ctlr->fw->pagedmemsize >> FWPageshift;
+ ctlr->fwmem.nblock = ctlr->fwmem.npage >> FWBlockshift;
+ if(ctlr->fwmem.nblock >= nelem(ctlr->fwmem.block)-1)
+ return "paged memory size too big";
+ for(i = 0; i < ctlr->fwmem.nblock; i++)
+ ctlr->fwmem.block[i].size = FWBlocksize;
+ ctlr->fwmem.block[i].size = (ctlr->fwmem.npage % FWBlockpages) << FWPageshift;
+ if(ctlr->fwmem.block[i].size != 0)
+ ctlr->fwmem.nblock++;
+ for(i = 0; i < ctlr->fwmem.nblock; i++){
+ if(ctlr->fwmem.block[i].p == nil){
+ ctlr->fwmem.block[i].p = mallocalign(ctlr->fwmem.block[i].size, FWPagesize, 0, 0);
+ if(ctlr->fwmem.block[i].p == nil)
+ return "no memory for firmware block";
+ }
+ }
+ if(ctlr->fwmem.css == nil){
+ if((ctlr->fwmem.css = mallocalign(FWPagesize, FWPagesize, 0, 0)) == nil)
+ return "no memory for firmware css page";
+ }
+ }
+
rx = &ctlr->rx;
if(rx->b == nil)
rx->b = malloc(sizeof(Block*) * Nrx);
@@ -1126,13 +1604,24 @@
}
rx->i = 0;
+ ctlr->ndma = 8;
+ ctlr->ntxq = 20;
+ if(ctlr->family >= 7000) {
+ ctlr->ntxq = 31;
+ } else {
+ if(ctlr->type == Type4965) {
+ ctlr->ndma = 7;
+ ctlr->ntxq = 16;
+ }
+ }
+
if(ctlr->sched.s == nil)
- ctlr->sched.s = mallocalign(512 * nelem(ctlr->tx) * 2, 1024, 0, 0);
+ ctlr->sched.s = mallocalign(512 * ctlr->ntxq * 2, 1024, 0, 0);
if(ctlr->sched.s == nil)
return "no memory for sched buffer";
- memset(ctlr->sched.s, 0, 512 * nelem(ctlr->tx));
+ memset(ctlr->sched.s, 0, 512 * ctlr->ntxq);
- for(q=0; q<nelem(ctlr->tx); q++){
+ for(q=0; q < nelem(ctlr->tx); q++){
tx = &ctlr->tx[q];
if(tx->b == nil)
tx->b = malloc(sizeof(Block*) * Ntx);
@@ -1146,7 +1635,7 @@
memset(tx->c, 0, Tcmdsize * Ntx);
for(i=0; i<Ntx; i++){
if(tx->b[i] != nil){
- freeb(tx->b[i]);
+ freeblist(tx->b[i]);
tx->b[i] = nil;
}
}
@@ -1168,46 +1657,69 @@
reset(Ctlr *ctlr)
{
char *err;
- int i, q;
+ int q, i;
if(ctlr->power)
poweroff(ctlr);
- if((err = initring(ctlr)) != nil)
+ if((err = initmem(ctlr)) != nil)
return err;
if((err = poweron(ctlr)) != nil)
return err;
- if((err = niclock(ctlr)) != nil)
- return err;
- prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain);
- nicunlock(ctlr);
+ if(ctlr->family <= 7000){
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain);
+ nicunlock(ctlr);
+ }
- csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi);
+ if(ctlr->family >= 7000){
+ u32int u;
- 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));
+ u = csr32r(ctlr, Cfg);
+
+ u &= ~(RadioSi|MacSi|CfgMacDashMask|CfgMacStepMask|CfgPhyTypeMask|CfgPhyStepMask|CfgPhyDashMask);
+
+ u |= (ctlr->step << CfgMacStepShift) & CfgMacStepMask;
+ u |= (ctlr->dash << CfgMacDashShift) & CfgMacDashMask;
+
+ u |= ctlr->rfcfg.type << CfgPhyTypeShift;
+ u |= ctlr->rfcfg.step << CfgPhyStepShift;
+ u |= ctlr->rfcfg.dash << CfgPhyDashShift;
+
+ csr32w(ctlr, Cfg, u);
+
+ } else {
+ csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi);
}
- 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(ctlr->family < 8000){
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ if(ctlr->family == 7000 || ctlr->type != Type4965)
+ prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | EarlyPwroffDis);
+ nicunlock(ctlr);
+ }
+ if(ctlr->family < 7000){
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ 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));
+ }
+ 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;
@@ -1222,45 +1734,89 @@
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);
+ if(ctlr->family >= 7000 || ctlr->type != Type4965)
+ prphwrite(ctlr, SchedTxFact, 0);
else
prphwrite(ctlr, SchedTxFact4965, 0);
+
+ if(ctlr->family >= 7000){
+ prphwrite(ctlr, SchedEnCtrl, 0);
+ prphwrite(ctlr, SchedGpCtrl, prphread(ctlr, SchedGpCtrl)
+ | Enable31Queues*(ctlr->ntxq == 31)
+ | AutoActiveMode);
+ for(q = 0; q < ctlr->ntxq; q++)
+ prphwrite(ctlr, (q<20? SchedQueueStatus: SchedQueueStatus20) + q*4, 1 << 19);
+ }
+
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(q = 0; q < ctlr->ntxq; q++){
+ i = q < nelem(ctlr->tx) ? q : nelem(ctlr->tx)-1;
+ if(q < 16)
+ csr32w(ctlr, FhCbbcQueue0 + q*4, PCIWADDR(ctlr->tx[i].d) >> 8);
+ else if(q < 20)
+ csr32w(ctlr, FhCbbcQueue16 + (q-16)*4, PCIWADDR(ctlr->tx[i].d) >> 8);
+ else
+ csr32w(ctlr, FhCbbcQueue20 + (q-20)*4, PCIWADDR(ctlr->tx[i].d) >> 8);
+ }
- for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--)
- csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna);
+ if(ctlr->family >= 7000 || ctlr->type >= Type6000)
+ csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff);
+ nicunlock(ctlr);
+
csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
+ ctlr->systime = 0;
+
ctlr->broken = 0;
ctlr->wait.m = 0;
ctlr->wait.w = 0;
+ ctlr->bcast.id = -1;
+ ctlr->bss.id = -1;
+
+ ctlr->phyid = -1;
+ ctlr->macid = -1;
+ ctlr->bindid = -1;
+ ctlr->timeid = -1;
+ ctlr->aid = 0;
+
ctlr->ie = Idefmask;
csr32w(ctlr, Imr, ctlr->ie);
csr32w(ctlr, Isr, ~0);
- if(ctlr->type >= Type6000)
- csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff);
+ csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
+ csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
+ csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
return nil;
}
static char*
+sendmccupdate(Ctlr *ctlr, char *mcc)
+{
+ uchar c[2+1+1+4+5*4], *p;
+
+ memset(p = c, 0, sizeof(c));
+ *p++ = mcc[1];
+ *p++ = mcc[0];
+ *p++ = 0;
+ *p++ = 0; // reserved
+ if(1){
+ p += 4;
+ p += 5*4;
+ }
+ return cmd(ctlr, 200, c, p - c);
+}
+
+static char*
sendbtcoexadv(Ctlr *ctlr)
{
static u32int btcoex3wire[12] = {
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
- 0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
+ 0xcc00ff28, 0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
};
@@ -1272,7 +1828,12 @@
memset(c, 0, sizeof(c));
p = c;
- if(ctlr->type == Type2030){
+ if(ctlr->family >= 7000){
+ put32(p, 3);
+ p += 4;
+ put32(p, (1<<4));
+ p += 4;
+ } else if(ctlr->type == Type2030){
*p++ = 145; /* flags */
p++; /* lead time */
*p++ = 5; /* max kill */
@@ -1300,6 +1861,9 @@
if((err = cmd(ctlr, 155, c, p-c)) != nil)
return err;
+ if(ctlr->family >= 7000)
+ return nil;
+
/* set BT priority */
memset(c, 0, sizeof(c));
p = c;
@@ -1332,203 +1896,1132 @@
}
static char*
-postboot(Ctlr *ctlr)
+sendpagingcmd(Ctlr *ctlr)
{
- uint ctxoff, ctxlen, dramaddr;
+ uchar c[3*4 + 4 + 32*4], *p;
+ int i;
+
+ p = c;
+ put32(p, (3<<8) | (ctlr->fwmem.npage % FWBlockpages));
+ p += 4;
+ put32(p, FWPageshift + FWBlockshift);
+ p += 4;
+ put32(p, ctlr->fwmem.nblock);
+ p += 4;
+
+ put32(p, PCIWADDR(ctlr->fwmem.css) >> FWPageshift);
+ p += 4;
+
+ for(i = 0; i < ctlr->fwmem.nblock; i++){
+ put32(p, PCIWADDR(ctlr->fwmem.block[i].p) >> FWPageshift);
+ p += 4;
+ }
+
+ for(; i < 32; i++){
+ put32(p, 0);
+ p += 4;
+ }
+
+ return cmd(ctlr, 79 | (1<<8), c, p-c);
+}
+
+static char*
+enablepaging(Ctlr *ctlr)
+{
+ FWSect *sect;
+ int nsect;
+ int i, j, o, n;
+
+ if(ctlr->fwmem.css == nil)
+ return nil;
+
+ if(1){
+ /* clear everything */
+ memset(ctlr->fwmem.css, 0, FWPagesize);
+ for(i = 0; i < ctlr->fwmem.nblock; i++)
+ memset(ctlr->fwmem.block[i].p, 0, ctlr->fwmem.block[i].size);
+ }
+
+ if(ctlr->calib.done == 0){
+ sect = ctlr->fw->init.sect;
+ nsect = ctlr->fw->init.nsect;
+ } else {
+ sect = ctlr->fw->main.sect;
+ nsect = ctlr->fw->main.nsect;
+ }
+
+ /* first CSS segment */
+ for(i = 0; i < nsect; i++) {
+ if(sect[i].addr == 0xAAAABBBB){
+ i++;
+ break;
+ }
+ }
+ if(i+1 >= nsect)
+ return "firmware misses CSS+paging sections";
+
+ if(sect[i].size > FWPagesize)
+ return "CSS section too big";
+ if(sect[i+1].size > (ctlr->fwmem.npage << FWPageshift))
+ return "paged section too big";
+
+ memmove(ctlr->fwmem.css, sect[i].data, sect[i].size);
+
+ for(j = 0, o = 0; o < sect[i+1].size; o += n, j++){
+ n = sect[i+1].size - o;
+ if(n > ctlr->fwmem.block[j].size)
+ n = ctlr->fwmem.block[j].size;
+ memmove(ctlr->fwmem.block[j].p, sect[i+1].data + o, n);
+ }
+
+ return sendpagingcmd(ctlr);
+}
+
+static int
+readnvmsect1(Ctlr *ctlr, int type, void *data, int len, int off)
+{
+ uchar c[2+2+2+2], *p;
char *err;
- int i, q;
- if((err = niclock(ctlr)) != nil)
- return err;
+ p = c;
+ *p++ = 0; // read op
+ *p++ = 0; // target
+ put16(p, type);
+ p += 2;
+ put16(p, off);
+ p += 2;
+ put16(p, len);
+ p += 2;
- if(ctlr->type != Type4965){
- dramaddr = SchedDramAddr5000;
- ctxoff = SchedCtxOff5000;
- ctxlen = SchedCtxLen5000;
+ ctlr->nvm.off = -1;
+ ctlr->nvm.ret = -1;
+ ctlr->nvm.type = -1;
+ ctlr->nvm.sts = -1;
+
+ ctlr->nvm.buf = data;
+ ctlr->nvm.len = len;
+
+ if((err = cmd(ctlr, 136, c, p - c)) != nil){
+ ctlr->nvm.buf = nil;
+ ctlr->nvm.len = 0;
+ print("readnvmsect: %s\n", err);
+ return -1;
+ }
+
+ if(ctlr->nvm.ret < len)
+ len = ctlr->nvm.ret;
+
+ if(ctlr->nvm.sts != 0 || ctlr->nvm.off != off || (ctlr->nvm.type & 0xFF) != type)
+ return -1;
+
+ return len;
+}
+
+static int
+readnvmsect(Ctlr *ctlr, int type, void *data, int len, int off)
+{
+ int n, r, o;
+
+ for(o = 0; o < len; o += n){
+ r = len - o;
+ if(r > 256)
+ r = 256;
+ if((n = readnvmsect1(ctlr, type, (char*)data + o, r, o+off)) < 0)
+ return -1;
+ if(n < r){
+ o += n;
+ break;
+ }
+ }
+ return o;
+}
+
+static char*
+readnvmconfig(Ctlr *ctlr)
+{
+ uchar *ea = ctlr->edev->ea;
+ uchar buf[8];
+ uint u;
+ char *err;
+
+ if(readnvmsect(ctlr, 1, buf, 8, 0) != 8)
+ return "can't read nvm version";
+
+ ctlr->nvm.version = get16(buf);
+ if (ctlr->family == 7000) {
+ u = get16(buf + 2);
+
+ ctlr->rfcfg.type = (u >> 4) & 3;
+ ctlr->rfcfg.step = (u >> 2) & 3;
+ ctlr->rfcfg.dash = (u >> 0) & 3;
+ ctlr->rfcfg.pnum = (u >> 6) & 3;
+
+ ctlr->rfcfg.txantmask = (u >> 8) & 15;
+ ctlr->rfcfg.rxantmask = (u >> 12) & 15;
+
} else {
- dramaddr = SchedDramAddr4965;
- ctxoff = SchedCtxOff4965;
- ctxlen = SchedCtxLen4965;
+ if(readnvmsect(ctlr, 12, buf, 8, 0) != 8)
+ return "can't read nvm phy config";
+
+ u = get32(buf);
+
+ ctlr->rfcfg.type = (u >> 12) & 0xFFF;
+ ctlr->rfcfg.step = (u >> 8) & 15;
+ ctlr->rfcfg.dash = (u >> 4) & 15;
+ ctlr->rfcfg.pnum = (u >> 6) & 3;
+
+ ctlr->rfcfg.txantmask = (u >> 24) & 15;
+ ctlr->rfcfg.rxantmask = (u >> 28) & 15;
}
+ if(ctlr->family >= 8000){
+ if(readnvmsect(ctlr, 11, ea, Eaddrlen, 0x01<<1) != Eaddrlen){
+ u32int a0, a1;
- ctlr->sched.base = prphread(ctlr, SchedSramAddr);
- for(i=0; i < ctxlen; i += 4)
- memwrite(ctlr, ctlr->sched.base + ctxoff + i, 0);
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ a0 = prphread(ctlr, 0xa03080);
+ a1 = prphread(ctlr, 0xa03084);
+ nicunlock(ctlr);
- prphwrite(ctlr, dramaddr, PCIWADDR(ctlr->sched.s)>>10);
+ ea[0] = a0 >> 24;
+ ea[1] = a0 >> 16;
+ ea[2] = a0 >> 8;
+ ea[3] = a0 >> 0;
+ ea[4] = a1 >> 8;
+ ea[5] = a1 >> 0;
+ }
+ } else {
+ readnvmsect(ctlr, 0, ea, Eaddrlen, 0x15<<1);
+ }
+ memmove(ctlr->edev->addr, ea, Eaddrlen);
- csr32w(ctlr, FhTxChicken, csr32r(ctlr, FhTxChicken) | 2);
+ return nil;
+}
- if(ctlr->type != Type4965){
- /* Enable chain mode for all queues, except command queue 4. */
- prphwrite(ctlr, SchedQChainSel5000, 0xfffef);
- prphwrite(ctlr, SchedAggrSel5000, 0);
+static char*
+sendtxantconfig(Ctlr *ctlr, uint val)
+{
+ uchar c[4];
+
+ put32(c, val);
+ return cmd(ctlr, 152, c, 4);
+}
- for(q=0; q<20; q++){
- prphwrite(ctlr, SchedQueueRdptr5000 + q*4, 0);
- csr32w(ctlr, HbusTargWptr, q << 8);
+static char*
+sendphyconfig(Ctlr *ctlr, u32int physku, u32int flowmask, u32int eventmask)
+{
+ uchar c[3*4];
- 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);
+ put32(c+0, physku);
+ put32(c+4, flowmask);
+ put32(c+8, eventmask);
+ return cmd(ctlr, 106, c, 3*4);
+}
+
+static char*
+setsmartfifo(Ctlr *ctlr, u32int state, int fullon)
+{
+ uchar c[4*(1 + 2 + 5*2 + 5*2)], *p;
+ int i;
+
+ memset(p = c, 0, sizeof(c));
+ put32(p, state);
+ p += 4;
+
+ /* watermark long delay on */
+ put32(p, 4096);
+ p += 4;
+
+ /* watermark full on */
+ put32(p, 4096);
+ p += 4;
+
+ /* long delay timeouts */
+ for(i = 0; i < 5*2; i++){
+ put32(p, 1000000);
+ p += 4;
+ }
+
+ /* full on timeouts */
+ if(fullon){
+ /* single unicast */
+ put32(p, 320);
+ p += 4;
+ put32(p, 2016);
+ p += 4;
+
+ /* agg unicast */
+ put32(p, 320);
+ p += 4;
+ put32(p, 2016);
+ p += 4;
+
+ /* mcast */
+ put32(p, 2016);
+ p += 4;
+ put32(p, 10016);
+ p += 4;
+
+ /* ba */
+ put32(p, 320);
+ p += 4;
+ put32(p, 2016);
+ p += 4;
+
+ /* tx re */
+ put32(p, 320);
+ p += 4;
+ put32(p, 2016);
+ p += 4;
+ } else {
+ for(i = 0; i < 5; i++){
+ put32(p, 160);
+ p += 4;
+ put32(p, 400);
+ p += 4;
}
- /* Enable interrupts for all our 20 queues. */
- prphwrite(ctlr, SchedIntrMask5000, 0xfffff);
+ }
+ return cmd(ctlr, 209, c, p - c);
+}
- /* Identify TX FIFO rings (0-7). */
- prphwrite(ctlr, SchedTxFact5000, 0xff);
+static char*
+delstation(Ctlr *ctlr, Station *sta)
+{
+ uchar c[4], *p;
+ char *err;
+
+ if(sta->id < 0)
+ return nil;
+
+ memset(p = c, 0, sizeof(c));
+ *p = sta->id;
+
+ if((err = cmd(ctlr, 25, c, 4)) != nil)
+ return err;
+
+ sta->id = -1;
+ return nil;
+}
+
+enum {
+ StaTypeLink = 0,
+ StaTypeGeneralPurpose,
+ StaTypeMulticast,
+ StaTypeTdlsLink,
+ StaTypeAux,
+};
+
+static char*
+setstation(Ctlr *ctlr, int id, int type, uchar addr[6], Station *sta)
+{
+ uchar c[Tcmdsize], *p;
+ char *err;
+
+ memset(p = c, 0, sizeof(c));
+
+ *p++ = 0; /* control (1 = update) */
+ p++; /* reserved */
+ if(ctlr->family >= 7000){
+ put16(p, 0xffff);
+ p += 2;
+ put32(p, ctlr->macid);
+ p += 4;
} else {
- /* Disable chain mode for all our 16 queues. */
- prphwrite(ctlr, SchedQChainSel4965, 0);
+ p += 2; /* reserved */
+ }
- for(q=0; q<16; q++) {
- prphwrite(ctlr, SchedQueueRdptr4965 + q*4, 0);
- csr32w(ctlr, HbusTargWptr, q << 8);
+ memmove(p, addr, 6);
+ p += 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);
+ *p++ = id; /* sta id */
+
+ if(ctlr->family >= 7000){
+ *p++ = 1 << 1; /* modify mask */
+ p += 2; /* reserved */
+
+ put32(p, 0<<26 | 0<<28);
+ p += 4; /* station_flags */
+
+ put32(p, 3<<26 | 3<<28);
+ p += 4; /* station_flags_mask */
+
+ p++; /* add_immediate_ba_tid */
+ p++; /* remove_immediate_ba_tid */
+ p += 2; /* add_immediate_ba_ssn */
+ p += 2; /* sleep_tx_count */
+ p++; /* sleep state flags */
+
+ *p++ = (ctlr->fw->api[0] & (1<<30)) != 0 ? type : 0; /* station_type */
+
+ p += 2; /* assoc id */
+
+ p += 2; /* beamform flags */
+
+ put32(p, 1<<0);
+ p += 4; /* tfd_queue_mask */
+
+ if(1){
+ p += 2; /* rx_ba_window */
+ p++; /* sp_length */
+ p++; /* uapsd_acs */
}
- /* Enable interrupts for all our 16 queues. */
- prphwrite(ctlr, SchedIntrMask4965, 0xffff);
+ } else {
+ p += 3;
+ p += 2; /* kflags */
+ p++; /* tcs2 */
+ p++; /* reserved */
+ p += 5*2; /* ttak */
+ p++; /* kid */
+ p++; /* reserved */
+ p += 16; /* key */
+ if(ctlr->type != Type4965){
+ 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 */
+ }
- /* Identify TX FIFO rings (0-7). */
- prphwrite(ctlr, SchedTxFact4965, 0xff);
+ if((err = cmd(ctlr, 24, c, p - c)) != nil)
+ return err;
+
+ sta->id = id;
+
+ return nil;
+}
+
+static char*
+setphycontext(Ctlr *ctlr, int amr)
+{
+ uchar c[Tcmdsize], *p;
+ int phyid;
+ char *err;
+
+ phyid = ctlr->phyid;
+ if(phyid < 0){
+ if(amr == CmdRemove)
+ return nil;
+ amr = CmdAdd;
+ phyid = 0;
+ } else if(amr == CmdAdd)
+ amr = CmdModify;
+
+ memset(p = c, 0, sizeof(c));
+ put32(p, phyid); // id and color
+ p += 4;
+ put32(p, amr);
+ p += 4;
+ put32(p, 0); // apply time 0 = immediate
+ p += 4;
+ put32(p, 0); // tx param color ????
+ p += 4;
+
+ *p++ = (ctlr->rxflags & RFlag24Ghz) != 0;
+ *p++ = ctlr->channel; // channel number
+ *p++ = 0; // channel width (20MHz<<val)
+ *p++ = 0; // pos1 below
+
+ put32(p, ctlr->rfcfg.txantmask);
+ p += 4;
+ put32(p, ctlr->rfcfg.rxantmask<<1 | (1<<10) | (1<<12));
+ p += 4;
+ put32(p, 0); // acquisition_data ????
+ p += 4;
+ put32(p, 0); // dsp_cfg_flags
+ p += 4;
+
+ if((err = cmd(ctlr, 8, c, p - c)) != nil)
+ return err;
+
+ if(amr == CmdRemove)
+ phyid = -1;
+ ctlr->phyid = phyid;
+ return nil;
+}
+
+static u32int
+reciprocal(u32int v)
+{
+ return v != 0 ? 0xFFFFFFFFU / v : 0;
+}
+
+static char*
+setmaccontext(Ether *edev, Ctlr *ctlr, int amr, Wnode *bss)
+{
+ uchar c[4+4 + 4+4 + 8+8 + 4+4+4+4+4+4+4 + 5*8 + 12*4], *p;
+ int macid, i;
+ char *err;
+
+ macid = ctlr->macid;
+ if(macid < 0){
+ if(amr == CmdRemove)
+ return nil;
+ amr = CmdAdd;
+ macid = 0;
+ } else if(amr == CmdAdd)
+ amr = CmdModify;
+
+ memset(p = c, 0, sizeof(c));
+ put32(p, macid);
+ p += 4;
+ put32(p, amr);
+ p += 4;
+
+ put32(p, 5); // mac type 5 = bss
+ p += 4;
+
+ put32(p, 0); // tsf id ???
+ p += 4;
+
+ memmove(p, edev->ea, 6);
+ p += 8;
+
+ memmove(p, ctlr->bssid, 6);
+ p += 8;
+
+ put32(p, bss == nil? 0xF : (bss->validrates & 0xF));
+ p += 4;
+ put32(p, bss == nil? 0xFF : (bss->validrates >> 4));
+ p += 4;
+
+ put32(p, 0); // protection flags
+ p += 4;
+
+ put32(p, ctlr->rxflags & RFlagShPreamble);
+ p += 4;
+ put32(p, ctlr->rxflags & RFlagShSlot);
+ p += 4;
+ put32(p, ctlr->rxfilter);
+ p += 4;
+
+ put32(p, 0); // qos flags
+ p += 4;
+
+ for(i = 0; i < 4; i++){
+ put16(p, 0x07); // cw_min
+ p += 2;
+ put16(p, 0x0f); // cw_max
+ p += 2;
+ *p++ = 2; // aifsn
+ *p++ = (1<<i); // fifos_mask
+ put16(p, 102*32); // edca_txop
+ p += 2;
}
+ p += 8;
- /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
- for(q=0; q<7; 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);
+ if(bss != nil){
+ int dtimoff = bss->ival * (int)bss->dtimcount * 1024;
+
+ /* is assoc */
+ put32(p, bss->aid != 0);
+ p += 4;
+
+ /* dtim time (system time) */
+ put32(p, bss->rs + dtimoff);
+ p += 4;
+
+ /* dtim tsf */
+ put64(p, bss->ts + dtimoff);
+ p += 8;
+
+ /* beacon interval */
+ put32(p, bss->ival);
+ p += 4;
+ put32(p, reciprocal(bss->ival));
+ p += 4;
+
+ /* dtim interval */
+ put32(p, bss->ival * bss->dtimperiod);
+ p += 4;
+ put32(p, reciprocal(bss->ival * bss->dtimperiod));
+ p += 4;
+
+ /* listen interval */
+ put32(p, 10);
+ p += 4;
+
+ /* assoc id */
+ put32(p, bss->aid & 0x3fff);
+ p += 4;
+
+ /* assoc beacon arrive time */
+ put32(p, bss->rs);
+ p += 4;
+ }
+ USED(p);
+
+ if((err = cmd(ctlr, 40, c, sizeof(c))) != nil)
+ return err;
+
+ if(amr == CmdRemove)
+ macid = -1;
+ ctlr->macid = macid;
+
+ return nil;
+}
+
+static char*
+setbindingcontext(Ctlr *ctlr, int amr)
+{
+ uchar c[Tcmdsize], *p;
+ int bindid;
+ char *err;
+ int i;
+
+ bindid = ctlr->bindid;
+ if(bindid < 0){
+ if(amr == CmdRemove)
+ return nil;
+ amr = CmdAdd;
+ bindid = 0;
+ } else if(amr == CmdAdd)
+ amr = CmdModify;
+
+ if(ctlr->phyid < 0)
+ return "setbindingcontext: no phyid";
+ if(ctlr->macid < 0)
+ return "setbindingcontext: no macid";
+
+ p = c;
+ put32(p, bindid);
+ p += 4;
+ put32(p, amr);
+ p += 4;
+
+ i = 0;
+ if(amr != CmdRemove){
+ put32(p, ctlr->macid);
+ p += 4;
+ i++;
+ }
+ for(; i < 3; i++){
+ put32(p, -1);
+ p += 4;
+ }
+ put32(p, ctlr->phyid);
+ p += 4;
+
+ if((err = cmd(ctlr, 43, c, p - c)) != nil)
+ return err;
+
+ if(amr == CmdRemove)
+ bindid = -1;
+ ctlr->bindid = bindid;
+ return nil;
+}
+
+static char*
+settimeevent(Ctlr *ctlr, int amr, int ival)
+{
+ int timeid, duration, delay;
+ uchar c[9*4], *p;
+ char *err;
+
+ if(ival){
+ duration = ival*2;
+ delay = ival/2;
+ } else {
+ duration = 1024;
+ delay = 0;
+ }
+
+ timeid = ctlr->timeid;
+ if(timeid < 0){
+ if(amr == CmdRemove)
+ return nil;
+ amr = CmdAdd;
+ timeid = 0;
+ } else {
+ if(amr == CmdAdd)
+ amr = CmdModify;
+ }
+ if(ctlr->macid < 0)
+ return "no mac id set";
+
+ memset(p = c, 0, sizeof(c));
+ put32(p, ctlr->macid);
+ p += 4;
+ put32(p, amr);
+ p += 4;
+ put32(p, timeid);
+ p += 4;
+
+ put32(p, 0); // apply time
+ p += 4;
+ put32(p, delay);
+ p += 4;
+ put32(p, 0); // depends on
+ p += 4;
+ put32(p, 1); // interval
+ p += 4;
+ put32(p, duration);
+ p += 4;
+ *p++ = 1; // repeat
+ *p++ = 0; // max frags
+ put16(p, 1<<0 | 1<<1 | 1<<11); // policy
+ p += 2;
+
+ if((err = cmd(ctlr, 41, c, p - c)) != nil)
+ return err;
+
+ if(amr == CmdRemove)
+ ctlr->timeid = -1;
+
+ return nil;
+}
+
+
+static char*
+setbindingquotas(Ctlr *ctlr, int bindid)
+{
+ uchar c[4*(3*4)], *p;
+ int i;
+
+ i = 0;
+ p = c;
+
+ if(bindid != -1){
+ put32(p, bindid);
+ p += 4;
+ put32(p, 128);
+ p += 4;
+ put32(p, 0);
+ p += 4;
+ i++;
+ }
+ for(; i < 4; i++){
+ put32(p, -1);
+ p += 4;
+ put32(p, 0);
+ p += 4;
+ put32(p, 0);
+ p += 4;
+ }
+
+ return cmd(ctlr, 44, c, p - c);
+}
+
+static char*
+setmcastfilter(Ctlr *ctlr)
+{
+ uchar *p;
+ char *err;
+ Block *b;
+
+ b = allocb(4+6+2);
+ p = b->rp;
+
+ *p++ = 1; // filter own
+ *p++ = 0; // port id
+ *p++ = 0; // count
+ *p++ = 1; // pass all
+
+ memmove(p, ctlr->bssid, 6);
+ p += 6;
+ *p++ = 0;
+ *p++ = 0;
+
+ b->wp = p;
+ if((err = qcmd(ctlr, 4, 208, nil, 0, b)) != nil){
+ freeb(b);
+ return err;
+ }
+ return flushq(ctlr, 4);
+}
+
+static char*
+setmacpowermode(Ctlr *ctlr)
+{
+ uchar c[4 + 2+2 + 4+4+4+4 + 1+1 + 2+2 + 1+1+1+1 + 1+1+1+1 + 1+1], *p;
+
+ p = c;
+ put32(p, ctlr->macid);
+ p += 4;
+
+ put16(p, 0); // flags
+ p += 2;
+ put16(p, 5); // keep alive seconds
+ p += 2;
+
+ put32(p, 0); // rx data timeout
+ p += 4;
+ put32(p, 0); // tx data timeout
+ p += 4;
+ put32(p, 0); // rx data timeout uapsd
+ p += 4;
+ put32(p, 0); // tx data timeout uapsd
+ p += 4;
+
+ *p++ = 0; // lprx rssi threshold
+ *p++ = 0; // skip dtim periods
+
+ put16(p, 0); // snooze interval
+ p += 2;
+ put16(p, 0); // snooze window
+ p += 2;
+
+ *p++ = 0; // snooze step
+ *p++ = 0; // qndp tid
+ *p++ = 0; // uapsd ac flags
+ *p++ = 0; // uapsd max sp
+
+ *p++ = 0; // heavy tx thld packets
+ *p++ = 0; // heavy rx thld packets
+
+ *p++ = 0; // heavy tx thld percentage
+ *p++ = 0; // heavy rx thld percentage
+
+ *p++ = 0; // limited ps threshold
+ *p++ = 0; // reserved
+
+ return cmd(ctlr, 169, c, p - c);
+}
+
+static char*
+disablebeaconfilter(Ctlr *ctlr)
+{
+ uchar c[11*4];
+
+ memset(c, 0, sizeof(c));
+ return cmd(ctlr, 210, c, 11*4);
+}
+
+static char*
+updatedevicepower(Ctlr *ctlr)
+{
+ uchar c[4];
+
+ memset(c, 0, sizeof(c));
+ put16(c, 0<<13 | 1<<0); // cont active off, pm enable
+
+ return cmd(ctlr, 119, c, 4);
+}
+
+static char*
+postboot7000(Ctlr *ctlr)
+{
+ char *err;
+
+ if(ctlr->calib.done == 0){
+ if((err = readnvmconfig(ctlr)) != nil)
+ return err;
+
+ if((err = setsmartfifo(ctlr, 3, 0)) != nil)
+ return err;
+ }
+
+ if((err = sendtxantconfig(ctlr, ctlr->rfcfg.txantmask)) != nil)
+ return err;
+
+ if(ctlr->calib.done == 0){
+ if((err = sendphyconfig(ctlr,
+ ctlr->fw->physku,
+ ctlr->fw->init.defcalib.flowmask,
+ ctlr->fw->init.defcalib.eventmask)) != 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 {
+ Block *b;
+ int i;
+
+ for(i = 0; i < nelem(ctlr->calib.cmd); i++){
+ if((b = ctlr->calib.cmd[i]) == nil)
+ continue;
+ b = copyblock(b, BLEN(b));
+ if((qcmd(ctlr, 4, 108, nil, 0, b)) != nil){
+ freeb(b);
+ return err;
+ }
+ if((err = flushq(ctlr, 4)) != nil)
+ return err;
+ }
+
+ if((err = sendphyconfig(ctlr,
+ ctlr->fw->physku,
+ ctlr->fw->main.defcalib.flowmask,
+ ctlr->fw->main.defcalib.eventmask)) != nil)
+ return err;
+
+ if((err = sendbtcoexadv(ctlr)) != nil)
+ return err;
+
+ if((err = updatedevicepower(ctlr)) != nil){
+ print("can't update device power: %s\n", err);
+ return err;
+ }
+ if((err = sendmccupdate(ctlr, "ZZ")) != nil){
+ print("can't disable beacon filter: %s\n", err);
+ return err;
+ }
+ if((err = disablebeaconfilter(ctlr)) != nil){
+ print("can't disable beacon filter: %s\n", err);
+ return err;
+ }
}
- nicunlock(ctlr);
- if(ctlr->type != Type4965){
- uchar c[Tcmdsize];
+ return nil;
+}
- /* disable wimax coexistance */
+static char*
+postboot6000(Ctlr *ctlr)
+{
+ uchar c[Tcmdsize];
+ char *err;
+
+ /* disable wimax coexistance */
+ memset(c, 0, sizeof(c));
+ if((err = cmd(ctlr, 90, c, 4+4*16)) != nil)
+ return err;
+
+ if(ctlr->type != Type5150){
+ /* calibrate crystal */
memset(c, 0, sizeof(c));
- if((err = cmd(ctlr, 90, c, 4+4*16)) != nil)
+ 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->type != Type5150){
- /* calibrate crystal */
+ 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};
+ int q;
+
+ /* send calibration records (runtime firmware) */
+ for(q=0; q<nelem(cmds); q++){
+ Block *b;
+ int i;
+
+ 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] = 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)
+ 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->calib.done == 0){
- /* query calibration (init firmware) */
+ 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, 0xffffffff);
- put32(c + 0*(5*4) + 8, 0xffffffff);
- put32(c + 2*(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;
+ }
- /* wait to collect calibration records */
- if(irqwait(ctlr, Ierr, 2000))
- return "calibration failed";
+ if((err = sendtxantconfig(ctlr, ctlr->rfcfg.txantmask & 7)) != nil)
+ return err;
- if(ctlr->calib.done == 0){
- print("iwl: no calibration results\n");
- ctlr->calib.done = 1;
- }
+ if(ctlr->type == Type2030){
+ if((err = sendbtcoexadv(ctlr)) != nil)
+ return err;
+ }
+ }
+ return nil;
+}
+
+static void
+initqueue(Ctlr *ctlr, int qid, int fifo, int chainmode, int window)
+{
+ csr32w(ctlr, HbusTargWptr, (qid << 8) | 0);
+
+ if(ctlr->family >= 7000 || ctlr->type != Type4965){
+ if(ctlr->family >= 7000)
+ prphwrite(ctlr, SchedQueueStatus + qid*4, 1 << 19);
+
+ if(chainmode)
+ prphwrite(ctlr, SchedQChainSel, prphread(ctlr, SchedQChainSel) | (1<<qid));
+ else
+ prphwrite(ctlr, SchedQChainSel, prphread(ctlr, SchedQChainSel) & ~(1<<qid));
+
+ prphwrite(ctlr, SchedAggrSel, prphread(ctlr, SchedAggrSel) & ~(1<<qid));
+
+ prphwrite(ctlr, SchedQueueRdptr + qid*4, 0);
+
+ /* Set scheduler window size and frame limit. */
+ memwrite(ctlr, ctlr->sched.base + SchedCtxOff + qid*8, 0);
+ memwrite(ctlr, ctlr->sched.base + SchedCtxOff + qid*8 + 4, window<<16 | window);
+
+ if(ctlr->family >= 7000){
+ prphwrite(ctlr, SchedQueueStatus + qid*4, 0x017f0018 | fifo);
} else {
- static uchar cmds[] = {8, 9, 11, 17, 16};
+ prphwrite(ctlr, SchedQueueStatus + qid*4, 0x00ff0018 | fifo);
+ }
+ } else {
+ if(chainmode)
+ prphwrite(ctlr, SchedQChainSel4965, prphread(ctlr, SchedQChainSel4965) | (1<<qid));
+ else
+ prphwrite(ctlr, SchedQChainSel4965, prphread(ctlr, SchedQChainSel4965) & ~(1<<qid));
- /* send calibration records (runtime firmware) */
- for(q=0; q<nelem(cmds); q++){
- Block *b;
+ prphwrite(ctlr, SchedQueueRdptr4965 + qid*4, 0);
- 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;
+ /* Set scheduler window size and frame limit. */
+ memwrite(ctlr, ctlr->sched.base + SchedCtxOff4965 + qid*8, window);
+ memwrite(ctlr, ctlr->sched.base + SchedCtxOff4965 + qid*8 + 4, window<<16);
- 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;
- }
+ prphwrite(ctlr, SchedQueueStatus4965 + qid*4, 0x0007fc01 | fifo<<1);
+ }
+}
- /* 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;
+static char*
+postboot(Ctlr *ctlr)
+{
+ uint ctxoff, ctxlen, dramaddr;
+ char *err;
+ int i, f;
- 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((err = niclock(ctlr)) != nil)
+ return err;
- 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;
- }
+ if(ctlr->family >= 7000 || ctlr->type != Type4965){
+ dramaddr = SchedDramAddr;
+ ctxoff = SchedCtxOff;
+ ctxlen = (SchedTransTblOff + 2*ctlr->ntxq) - ctxoff;
+ } else {
+ dramaddr = SchedDramAddr4965;
+ ctxoff = SchedCtxOff4965;
+ ctxlen = SchedCtxLen4965;
+ }
- /* set tx antenna config */
- put32(c, ctlr->rfcfg.txantmask & 7);
- if((err = cmd(ctlr, 152, c, 4)) != nil)
- return err;
+ ctlr->sched.base = prphread(ctlr, SchedSramAddr);
+ for(i=0; i < ctxlen; i += 4)
+ memwrite(ctlr, ctlr->sched.base + ctxoff + i, 0);
- if(ctlr->type == Type2030){
- if((err = sendbtcoexadv(ctlr)) != nil)
- return err;
- }
+ prphwrite(ctlr, dramaddr, PCIWADDR(ctlr->sched.s)>>10);
+
+ if(ctlr->family >= 7000) {
+ prphwrite(ctlr, SchedEnCtrl, 0);
+ prphwrite(ctlr, SchedChainExtEn, 0);
+ }
+
+ for(i = 0; i < nelem(ctlr->tx); i++){
+ if(i == 4 && ctlr->family < 7000 && ctlr->type == Type4965)
+ f = 4;
+ else {
+ static char qid2fifo[] = {
+ 3, 2, 1, 0, 7, 5, 6,
+ };
+ f = qid2fifo[i];
}
+ initqueue(ctlr, i, f, i != 4 && ctlr->type != Type4965, 64);
}
+ /* Enable interrupts for all queues. */
+ if(ctlr->family >= 7000){
+ prphwrite(ctlr, SchedEnCtrl, 1 << 4);
+ } else if(ctlr->type != Type4965) {
+ prphwrite(ctlr, SchedIntrMask, (1<<ctlr->ntxq)-1);
+ } else {
+ prphwrite(ctlr, SchedIntrMask4965, (1<<ctlr->ntxq)-1);
+ }
+
+ /* Identify TX FIFO rings (0-7). */
+ if(ctlr->family >= 7000 || ctlr->type != Type4965){
+ prphwrite(ctlr, SchedTxFact, 0xff);
+ } else {
+ prphwrite(ctlr, SchedTxFact4965, 0xff);
+ }
+
+ /* Enable DMA channels */
+ for(i = 0; i < ctlr->ndma; i++)
+ csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna);
+
+ /* Auto Retry Enable */
+ csr32w(ctlr, FhTxChicken, csr32r(ctlr, FhTxChicken) | 2);
+
+ nicunlock(ctlr);
+
+ if((err = enablepaging(ctlr)) != nil){
+ ctlr->calib.done = 0;
+ return err;
+ }
+
+ if(ctlr->family >= 7000)
+ return postboot7000(ctlr);
+ else if(ctlr->type != Type4965)
+ return postboot6000(ctlr);
+
return nil;
}
@@ -1535,15 +3028,28 @@
static char*
loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size)
{
+ enum { Maxchunk = 0x20000 };
uchar *dma;
char *err;
+ while(size > Maxchunk){
+ if((err = loadfirmware1(ctlr, dst, data, Maxchunk)) != nil)
+ return err;
+ size -= Maxchunk;
+ data += Maxchunk;
+ dst += Maxchunk;
+ }
+
dma = mallocalign(size, 16, 0, 0);
if(dma == nil)
return "no memory for dma";
memmove(dma, data, size);
coherence();
- if((err = niclock(ctlr)) != 0){
+
+ if(ctlr->family >= 7000 && dst >= 0x40000 && dst < 0x57fff)
+ prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) | ExtAddr);
+
+ if((err = niclock(ctlr)) != nil){
free(dma);
return err;
}
@@ -1557,15 +3063,74 @@
FhTxBufStatusTfbdValid);
csr32w(ctlr, FhTxConfig + 9*32, FhTxConfigDmaEna | FhTxConfigCirqHostEndTfd);
nicunlock(ctlr);
- if(irqwait(ctlr, Ifhtx|Ierr, 5000) != Ifhtx){
- free(dma);
- return "dma error / timeout";
- }
+
+ err = nil;
+ if(irqwait(ctlr, Ifhtx|Ierr, 5000) != Ifhtx)
+ err = "dma error / timeout";
+
+ if(ctlr->family >= 7000 && dst >= 0x40000 && dst < 0x57fff)
+ prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) & ~ExtAddr);
+
free(dma);
- return 0;
+
+ return err;
}
static char*
+setloadstatus(Ctlr *ctlr, u32int val)
+{
+ char *err;
+
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ csr32w(ctlr, UcodeLoadStatus, val);
+ nicunlock(ctlr);
+ return nil;
+}
+
+static char*
+loadsections(Ctlr *ctlr, FWSect *sect, int nsect)
+{
+ int i, num;
+ char *err;
+
+ if(ctlr->family >= 8000){
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ prphwrite(ctlr, ReleaseCpuReset, CpuResetBit);
+ nicunlock(ctlr);
+ }
+
+ num = 0;
+ for(i = 0; i < nsect; i++){
+ if(sect[i].addr == 0xAAAABBBB)
+ break;
+ if(sect[i].addr == 0xFFFFCCCC)
+ num = 16;
+ else {
+ if(sect[i].data == nil || sect[i].size == 0)
+ return "bad load section";
+ if((err = loadfirmware1(ctlr, sect[i].addr, sect[i].data, sect[i].size)) != nil)
+ return err;
+ num++;
+ }
+ if(ctlr->family >= 8000
+ && (err = setloadstatus(ctlr, (1ULL << num)-1)) != nil)
+ return err;
+ }
+ return nil;
+}
+
+static char*
+ucodestart(Ctlr *ctlr)
+{
+ if(ctlr->family >= 8000)
+ return setloadstatus(ctlr, -1);
+ csr32w(ctlr, Reset, 0);
+ return nil;
+}
+
+static char*
boot(Ctlr *ctlr)
{
int i, n, size;
@@ -1577,11 +3142,10 @@
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)
+ if((err = loadsections(ctlr, fw->init.sect, fw->init.nsect)) != nil)
return err;
- if((err = loadfirmware1(ctlr, 0x00800000, fw->init.data.data, fw->init.data.size)) != nil)
+ if((err = ucodestart(ctlr)) != nil)
return err;
- csr32w(ctlr, Reset, 0);
if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
return "init firmware boot failed";
if((err = postboot(ctlr)) != nil)
@@ -1589,16 +3153,18 @@
if((err = reset(ctlr)) != nil)
return err;
}
- if((err = loadfirmware1(ctlr, 0x00000000, fw->main.text.data, fw->main.text.size)) != nil)
+ if((err = loadsections(ctlr, fw->main.sect, fw->main.nsect)) != nil)
return err;
- if((err = loadfirmware1(ctlr, 0x00800000, fw->main.data.data, fw->main.data.size)) != nil)
+ if((err= ucodestart(ctlr)) != nil)
return err;
- csr32w(ctlr, Reset, 0);
if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
return "main firmware boot failed";
return postboot(ctlr);
}
+ if(ctlr->family >= 7000)
+ return "wrong firmware image";
+
size = ROUND(fw->init.data.size, 16) + ROUND(fw->init.text.size, 16);
dma = mallocalign(size, 16, 0, 0);
if(dma == nil)
@@ -1654,7 +3220,7 @@
csr32w(ctlr, Reset, 0);
if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){
free(dma);
- return "init firmware boot failed";
+ return "boot firmware boot failed";
}
free(dma);
@@ -1696,12 +3262,23 @@
static char*
qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
{
+ int hdrlen;
+ Block *bcmd;
uchar *d, *c;
TXQ *q;
- assert(qid < nelem(ctlr->tx));
- assert(size <= Tcmdsize-4);
+ assert(qid < ctlr->ntxq);
+ if(code & 0xFF00)
+ hdrlen = 8;
+ else
+ hdrlen = 4;
+
+ if(hdrlen+size > Tcmdsize)
+ bcmd = allocb(hdrlen + size);
+ else
+ bcmd = nil;
+
ilock(ctlr);
q = &ctlr->tx[qid];
while(q->n >= Ntxqmax && !ctlr->broken){
@@ -1719,24 +3296,40 @@
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;
+ if(bcmd != nil){
+ bcmd->next = q->b[q->i];
+ q->b[q->i] = bcmd;
- /* build command */
- c[0] = code;
- c[1] = 0; /* flags */
- c[2] = q->i;
- c[3] = qid;
+ c = bcmd->rp;
+ bcmd->wp = c + hdrlen + size;
+ } else {
+ c = q->c + q->i * Tcmdsize;
+ }
+ /* build command */
+ if(hdrlen == 8){
+ c[0] = code;
+ c[1] = code>>8; /* group id */
+ c[2] = q->i;
+ c[3] = qid;
+ put16(c+4, size);
+ c[6] = 0;
+ c[7] = code>>16;
+ } else {
+ c[0] = code;
+ c[1] = 0; /* flags */
+ c[2] = q->i;
+ c[3] = qid;
+ }
if(size > 0)
- memmove(c+4, data, size);
+ memmove(c+hdrlen, data, size);
+ size += hdrlen;
- size += 4;
-
/* build descriptor */
+ d = q->d + q->i * Tdscsize;
*d++ = 0;
*d++ = 0;
*d++ = 0;
@@ -1806,6 +3399,9 @@
{
uchar c[8];
+ if(ctlr->family >= 7000)
+ return; // TODO
+
csr32w(ctlr, Led, csr32r(ctlr, Led) & ~LedBsmCtrl);
memset(c, 0, sizeof(c));
@@ -1816,92 +3412,80 @@
cmd(ctlr, 72, c, sizeof(c));
}
-static void
-addnode(Ctlr *ctlr, uchar id, uchar *addr)
+static char*
+rxoff7000(Ether *edev, Ctlr *ctlr)
{
- uchar c[Tcmdsize], *p;
+ char *err;
- memset(p = c, 0, sizeof(c));
- *p++ = 0; /* control (1 = update) */
- p += 3; /* reserved */
- memmove(p, addr, 6);
- p += 6;
- p += 2; /* reserved */
- *p++ = id; /* node id */
- p++; /* flags */
- p += 2; /* reserved */
- p += 2; /* kflags */
- p++; /* tcs2 */
- p++; /* reserved */
- p += 5*2; /* ttak */
- p++; /* kid */
- p++; /* reserved */
- p += 16; /* key */
- if(ctlr->type != Type4965){
- p += 8; /* tcs */
- p += 8; /* rxmic */
- p += 8; /* txmic */
+ if((err = settimeevent(ctlr, CmdRemove, 0)) != nil){
+ print("can't remove timeevent: %s\n", err);
+ return err;
}
- 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);
+ if((err = setsmartfifo(ctlr, 2, 0)) != nil){
+ print("setsmartfifo: %s\n", err);
+ return err;
+ }
+ if((err = setbindingquotas(ctlr, -1)) != nil){
+ print("can't disable quotas: %s\n", err);
+ return err;
+ }
+ if((err = setbindingcontext(ctlr, CmdRemove)) != nil){
+ print("removing bindingcontext: %s\n", err);
+ return err;
+ }
+ if((err = setmaccontext(edev, ctlr, CmdRemove, nil)) != nil){
+ print("removing maccontext: %s\n", err);
+ return err;
+ }
+ if((err = setphycontext(ctlr, CmdRemove)) != nil){
+ print("setphycontext: %s\n", err);
+ return err;
+ }
+ return nil;
}
-static void
-rxon(Ether *edev, Wnode *bss)
+static char*
+rxon7000(Ether *edev, Ctlr *ctlr)
{
- 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;
+ if((err = setphycontext(ctlr, CmdAdd)) != nil){
+ print("setphycontext: %s\n", err);
+ return err;
}
- 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((err = setmaccontext(edev, ctlr, CmdAdd, nil)) != nil){
+ print("setmaccontext: %s\n", err);
+ return err;
}
+ if((err = setbindingcontext(ctlr, CmdAdd)) != nil){
+ print("removing bindingcontext: %s\n", err);
+ return err;
+ }
+ if((err = setsmartfifo(ctlr, 1, ctlr->aid != 0)) != nil){
+ print("setsmartfifo: %s\n", err);
+ return err;
+ }
+ if((err = setmcastfilter(ctlr)) != nil){
+ print("can't set mcast filter: %s\n", err);
+ return err;
+ }
+ if((err = setmacpowermode(ctlr)) != nil){
+ print("can't set mac power: %s\n", err);
+ return err;
+ }
+ if((err = setbindingquotas(ctlr, ctlr->bindid)) != nil){
+ print("can't set binding quotas: %s\n", err);
+ return err;
+ }
+ return nil;
+}
- 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 */
+static char*
+rxon6000(Ether *edev, Ctlr *ctlr)
+{
+ uchar c[Tcmdsize], *p;
+ char *err;
- 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 */
memmove(p, ctlr->bssid, 6); p += 8; /* bssid */
@@ -1915,9 +3499,9 @@
*p++ = 0x0f; /* cck mask (not yet negotiated) */
put16(p, ctlr->aid & 0x3fff);
p += 2; /* aid */
- put32(p, flags);
+ put32(p, ctlr->rxflags);
p += 4;
- put32(p, filter);
+ put32(p, ctlr->rxfilter);
p += 4;
*p++ = ctlr->channel;
p++; /* reserved */
@@ -1930,81 +3514,107 @@
p += 2; /* reserved */
}
if((err = cmd(ctlr, 16, c, p - c)) != nil){
- print("rxon: %s\n", err);
- return;
+ print("rxon6000: %s\n", err);
+ return err;
}
+ return nil;
+}
- if(ctlr->bcastnodeid == -1){
- ctlr->bcastnodeid = (ctlr->type != Type4965) ? 15 : 31;
- addnode(ctlr, ctlr->bcastnodeid, edev->bcast);
+static char*
+rxon(Ether *edev, Wnode *bss)
+{
+ Ctlr *ctlr = edev->ctlr;
+ char *err;
+
+ if(ctlr->family >= 7000){
+ delstation(ctlr, &ctlr->bss);
+ delstation(ctlr, &ctlr->bcast);
+ if((err = rxoff7000(edev, ctlr)) != nil)
+ goto Out;
}
- if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){
- ctlr->bssnodeid = 0;
- addnode(ctlr, ctlr->bssnodeid, bss->bssid);
- }
-}
-static struct ratetab {
- uchar rate;
- uchar plcp;
- uchar flags;
-} ratetab[] = {
- { 2, 10, RFlagCCK },
- { 4, 20, RFlagCCK },
- { 11, 55, RFlagCCK },
- { 22, 110, RFlagCCK },
+ ctlr->rxfilter = FilterNoDecrypt | FilterMulticast | FilterBeacon;
+ if(ctlr->family >= 7000)
+ ctlr->rxfilter |= FilterNoDecryptMcast;
+ if(ctlr->prom)
+ ctlr->rxfilter |= FilterPromisc;
- { 12, 0xd, 0 },
- { 18, 0xf, 0 },
- { 24, 0x5, 0 },
- { 36, 0x7, 0 },
- { 48, 0x9, 0 },
- { 72, 0xb, 0 },
- { 96, 0x1, 0 },
- { 108, 0x3, 0 },
- { 120, 0x3, 0 }
-};
+ ctlr->rxflags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto;
+ if(bss != nil){
+ ctlr->aid = bss->aid;
+ ctlr->channel = bss->channel;
+ memmove(ctlr->bssid, bss->bssid, sizeof(ctlr->bssid));
+ if(bss->cap & (1<<5))
+ ctlr->rxflags |= RFlagShPreamble;
+ if(bss->cap & (1<<10))
+ ctlr->rxflags |= RFlagShSlot;
+ if(ctlr->aid != 0){
+ ctlr->rxfilter |= FilterBSS;
+ ctlr->rxfilter &= ~FilterBeacon;
+ ctlr->bss.id = -1;
+ } else {
+ ctlr->bcast.id = -1;
+ }
+ } else {
+ ctlr->aid = 0;
+ memmove(ctlr->bssid, edev->bcast, sizeof(ctlr->bssid));
+ ctlr->bcast.id = -1;
+ ctlr->bss.id = -1;
+ }
-static uchar iwlrates[] = {
- 0x80 | 2,
- 0x80 | 4,
- 0x80 | 11,
- 0x80 | 22,
+ if(ctlr->aid != 0)
+ setled(ctlr, 2, 0, 1); /* on when associated */
+ else if(memcmp(ctlr->bssid, edev->bcast, sizeof(ctlr->bssid)) != 0)
+ setled(ctlr, 2, 10, 10); /* slow blink when connecting */
+ else
+ setled(ctlr, 2, 5, 5); /* fast blink when scanning */
- 0x80 | 12,
- 0x80 | 18,
- 0x80 | 24,
- 0x80 | 36,
- 0x80 | 48,
- 0x80 | 72,
- 0x80 | 96,
- 0x80 | 108,
- 0x80 | 120,
+ if(ctlr->wifi->debug)
+ print("#l%d: rxon: bssid %E, aid %x, channel %d, rxfilter %ux, rxflags %ux\n",
+ edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, ctlr->rxfilter, ctlr->rxflags);
- 0
-};
+ if(ctlr->family >= 7000)
+ err = rxon7000(edev, ctlr);
+ else
+ err = rxon6000(edev, ctlr);
+ if(err != nil)
+ goto Out;
-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,
-};
+ if(ctlr->bcast.id == -1){
+ if((err = setstation(ctlr,
+ (ctlr->type != Type4965)? 15: 31,
+ StaTypeGeneralPurpose,
+ edev->bcast,
+ &ctlr->bcast)) != nil)
+ goto Out;
+ }
+ if(ctlr->bss.id == -1 && bss != nil && ctlr->aid != 0){
+ if((err = setstation(ctlr,
+ 0,
+ StaTypeLink,
+ bss->bssid,
+ &ctlr->bss)) != nil)
+ goto Out;
+ if(ctlr->family >= 7000)
+ if((err = setmaccontext(edev, ctlr, CmdModify, bss)) != nil)
+ goto Out;
+ } else {
+ if(ctlr->family >= 7000)
+ if((err = settimeevent(ctlr, CmdAdd, (bss != nil)? bss->ival: 0)) != nil)
+ goto Out;
+ }
+Out:
+ return err;
+}
+
static void
transmit(Wifi *wifi, Wnode *wn, Block *b)
{
- int flags, nodeid, rate, ant;
+ int flags, rate, ant;
uchar c[Tcmdsize], *p;
Ether *edev;
+ Station *sta;
Ctlr *ctlr;
Wifipkt *w;
char *err;
@@ -2014,6 +3624,7 @@
qlock(ctlr);
if(ctlr->attached == 0 || ctlr->broken){
+Broken:
qunlock(ctlr);
freeb(b);
return;
@@ -2020,8 +3631,10 @@
}
if((wn->channel != ctlr->channel)
- || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)))
- rxon(edev, wn);
+ || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0))){
+ if(rxon(edev, wn) != nil)
+ goto Broken;
+ }
if(b == nil){
/* association note has no data to transmit */
@@ -2030,7 +3643,7 @@
}
flags = 0;
- nodeid = ctlr->bcastnodeid;
+ sta = &ctlr->bcast;
p = wn->minrate;
w = (Wifipkt*)b->rp;
if((w->a1[0] & 1) == 0){
@@ -2039,13 +3652,13 @@
if(BLEN(b) > 512-4)
flags |= TFlagNeedRTS;
- if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){
- nodeid = ctlr->bssnodeid;
+ if((w->fc[0] & 0x0c) == 0x08 && ctlr->bss.id != -1){
+ sta = &ctlr->bss;
p = wn->actrate;
}
if(flags & (TFlagNeedRTS|TFlagNeedCTS)){
- if(ctlr->type != Type4965){
+ if(ctlr->family >= 7000 || ctlr->type != Type4965){
flags &= ~(TFlagNeedRTS|TFlagNeedCTS);
flags |= TFlagNeedProtection;
} else
@@ -2052,11 +3665,14 @@
flags |= TFlagFullTxOp;
}
}
+
+ if(sta->id == -1)
+ goto Broken;
+
if(p >= wifi->rates)
rate = p - wifi->rates;
else
rate = 0;
- qunlock(ctlr);
/* select first available antenna */
ant = ctlr->rfcfg.txantmask & 7;
@@ -2076,7 +3692,7 @@
*p++ = ratetab[rate].flags | (ant<<6);
p += 2; /* xflags */
- *p++ = nodeid;
+ *p++ = sta->id; /* station id */
*p++ = 0; /* security */
*p++ = 0; /* linkq */
p++; /* reserved */
@@ -2096,8 +3712,10 @@
put16(p, 0); /* timeout */
p += 2;
p += 2; /* txop */
+ qunlock(ctlr);
+
if((err = qcmd(ctlr, 0, 28, c, p - c, b)) != nil){
- print("transmit: %s\n", err);
+ print("#l%d: transmit %s\n", edev->ctlrno, err);
freeb(b);
}
}
@@ -2187,9 +3805,6 @@
if(boot(ctlr) != nil)
break;
- ctlr->bcastnodeid = -1;
- ctlr->bssnodeid = -1;
- ctlr->aid = 0;
rxon(edev, ctlr->wifi->bss);
break;
}
@@ -2217,46 +3832,57 @@
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){
- 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";
+ char *fn;
+
+ fn = ctlr->fwname;
+ if(fn == nil){
+ 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",
+ print("#l%d: firmware: %s, rev %ux, build %ud, size [%d] %ux+%ux + [%d] %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,
+ fw->main.nsect, fw->main.text.size, fw->main.data.size,
+ fw->init.nsect, fw->init.text.size, fw->init.data.size,
fw->boot.text.size);
ctlr->fw = fw;
}
+ if(ctlr->family >= 7000){
+ u32int u = ctlr->fw->physku;
+
+ ctlr->rfcfg.type = u & 3; u >>= 2;
+ ctlr->rfcfg.step = u & 3; u >>= 2;
+ ctlr->rfcfg.dash = u & 3; u >>= 12;
+
+ ctlr->rfcfg.txantmask = u & 15; u >>= 4;
+ ctlr->rfcfg.rxantmask = u & 15;
+ }
+
if((err = reset(ctlr)) != nil)
error(err);
if((err = boot(ctlr)) != nil)
error(err);
- ctlr->bcastnodeid = -1;
- ctlr->bssnodeid = -1;
- ctlr->channel = 1;
- ctlr->aid = 0;
+ 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->family >= 7000 || ctlr->type != Type2030)
+ ctlr->wifi->rates = iwlrates;
+ }
+
setoptions(edev);
ctlr->attached = 1;
@@ -2280,10 +3906,8 @@
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;
+ int type, flags, idx, qid, len;
b = rx->b[rx->i];
if(b == nil)
@@ -2297,34 +3921,29 @@
idx = *d++;
qid = *d++;
- if(bb != nil){
- freeb(bb);
- bb = nil;
- }
- if((qid & 0x80) == 0 && qid < nelem(ctlr->tx)){
+ tx = nil;
+ bb = nil;
+ if((qid & 0x80) == 0 && qid < ctlr->ntxq){
tx = &ctlr->tx[qid];
- if(tx->n > 0){
- bb = tx->b[idx];
- tx->b[idx] = nil;
- tx->n--;
-
- wakeup(tx);
- }
+ bb = tx->b[idx];
+ tx->b[idx] = nil;
}
len &= 0x3fff;
- if(len < 4 || type == 0)
- continue;
-
len -= 4;
- switch(type){
+ if(len >= 0) switch(type){
case 1: /* microcontroller ready */
setfwinfo(ctlr, d, len);
break;
case 24: /* add node done */
+ if(len < 4)
+ break;
break;
case 28: /* tx done */
- if(ctlr->type == Type4965){
+ if(ctlr->family >= 7000){
+ if(len <= 36 || d[36] == 1 || d[36] == 2)
+ break;
+ } else if(ctlr->type == Type4965){
if(len <= 20 || d[20] == 1 || d[20] == 2)
break;
} else {
@@ -2331,13 +3950,21 @@
if(len <= 32 || d[32] == 1 || d[32] == 2)
break;
}
- wifitxfail(ctlr->wifi, bb);
+ if(ctlr->wifi != nil)
+ wifitxfail(ctlr->wifi, bb);
break;
+ case 41:
+ if(len >= 16 && get32(d) == 0 && ctlr->timeid == -1)
+ ctlr->timeid = get32(d+8);
+ break;
case 102: /* calibration result (Type5000 only) */
+ if(ctlr->family >= 7000)
+ break;
if(len < 4)
break;
idx = d[0];
- if(idx >= nelem(ctlr->calib.cmd))
+ Calib:
+ if(idx < 0 || idx >= nelem(ctlr->calib.cmd))
break;
if(rbplant(ctlr, rx->i) < 0)
break;
@@ -2346,23 +3973,77 @@
b->rp = d;
b->wp = d + len;
ctlr->calib.cmd[idx] = b;
- continue;
+ break;
+ case 4: /* init complete (>= 7000 family) */
+ if(ctlr->family < 7000)
+ break;
case 103: /* calibration done (Type5000 only) */
ctlr->calib.done = 1;
break;
+ case 107: /* calibration result (>= 7000 family) */
+ if(ctlr->family < 7000)
+ break;
+ len -= 4;
+ if(len < 0)
+ break;
+ idx = get16(d+2);
+ if(idx < len)
+ len = idx;
+ idx = -1;
+ switch(get16(d)){
+ case 1:
+ idx = &ctlr->calib.cfg - &ctlr->calib.cmd[0];
+ break;
+ case 2:
+ idx = &ctlr->calib.nch - &ctlr->calib.cmd[0];
+ break;
+ case 4:
+ if(len < 2)
+ break;
+ idx = &ctlr->calib.papd[get16(d+4) % nelem(ctlr->calib.papd)] - &ctlr->calib.cmd[0];
+ break;
+ case 5:
+ if(len < 2)
+ break;
+ idx = &ctlr->calib.txp[get16(d+4) % nelem(ctlr->calib.txp)] - &ctlr->calib.cmd[0];
+ break;
+ }
+ len += 4;
+ goto Calib;
case 130: /* start scan */
- break;
case 132: /* stop scan */
break;
- case 156: /* rx statistics */
+ case 136: /* NVM access (>= 7000 family) */
+ if(ctlr->family < 7000)
+ break;
+ len -= 8;
+ if(len < 0)
+ break;
+ if(ctlr->nvm.len < len)
+ len = ctlr->nvm.len;
+ ctlr->nvm.off = get16(d + 0);
+ ctlr->nvm.ret = get16(d + 2);
+ ctlr->nvm.type= get16(d + 4);
+ ctlr->nvm.sts = get16(d + 6);
+ d += 8;
+ if(ctlr->nvm.ret < len)
+ len = ctlr->nvm.ret;
+ if(ctlr->nvm.buf != nil && len > 0)
+ memmove(ctlr->nvm.buf, d, len);
+ ctlr->nvm.buf = nil;
+ ctlr->nvm.len = 0;
break;
+ case 156: /* rx statistics */
case 157: /* beacon statistics */
- break;
case 161: /* state changed */
- break;
case 162: /* beacon missed */
+ case 177: /* mduart load notification */
break;
case 192: /* rx phy */
+ if(len >= 8){
+ u32int dt = get32(d+4) - (u32int)ctlr->systime;
+ ctlr->systime += dt;
+ }
break;
case 195: /* rx done */
if(d + 2 > b->lim)
@@ -2375,7 +4056,7 @@
len = get16(d); d += 4;
if(d + len + 4 > b->lim)
break;
- if((get32(d + len) & 3) != 3)
+ if((d[len] & 3) != 3)
break;
if(ctlr->wifi == nil)
break;
@@ -2383,15 +4064,22 @@
break;
b->rp = d;
b->wp = d + len;
+
+ put64(d - 8, ctlr->systime);
+ b->flag |= Btimestamp;
+
wifiiq(ctlr->wifi, b);
- continue;
+ break;
case 197: /* rx compressed ba */
break;
}
+ freeblist(bb);
+ if(tx != nil && tx->n > 0){
+ tx->n--;
+ wakeup(tx);
+ }
}
csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7);
- if(bb != nil)
- freeb(bb);
}
static void
@@ -2447,6 +4135,8 @@
iwlpci(void)
{
Pcidev *pdev;
+ char *fwname;
+ int family;
pdev = nil;
while(pdev = pcimatch(pdev, 0, 0)) {
@@ -2487,7 +4177,13 @@
case 0x0091: /* Centrino Advanced-N 6030 */
case 0x088e: /* Centrino Advanced-N 6235 */
case 0x088f: /* Centrino Advanced-N 6235 */
+ family = 0;
+ fwname = nil;
break;
+ case 0x24fd: /* Wireless AC 8265 */
+ family = 8000;
+ fwname = "iwm-8265-34";
+ break;
}
ctlr = malloc(sizeof(Ctlr));
@@ -2504,6 +4200,8 @@
}
ctlr->nic = mem;
ctlr->pdev = pdev;
+ ctlr->fwname = fwname;
+ ctlr->family = family;
if(iwlhead != nil)
iwltail->link = ctlr;
@@ -2522,10 +4220,10 @@
iwlpci();
again:
for(ctlr = iwlhead; ctlr != nil; ctlr = ctlr->link){
- if(ctlr->active)
+ if(ctlr->edev != nil)
continue;
if(edev->port == 0 || edev->port == ctlr->port){
- ctlr->active = 1;
+ ctlr->edev = edev;
break;
}
}
@@ -2549,6 +4247,7 @@
pcienable(ctlr->pdev);
if(iwlinit(edev) < 0){
pcidisable(ctlr->pdev);
+ ctlr->edev = (void*)-1;
edev->ctlr = nil;
goto again;
}