+ 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->te.id = -1;
+ ctlr->te.active = 0;
+ ctlr->aid = 0;
+
+ if(ctlr->family >= 9000)
+ csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | 0x4000000);
+
+ ctlr->ie = Idefmask;
+ csr32w(ctlr, Imr, ctlr->ie);
+ csr32w(ctlr, Isr, ~0);
+
+ 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,
+ 0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
+ };
+
+ uchar c[Tcmdsize], *p;
+ char *err;
+ int i;
+
+ /* set BT config */
+ memset(c, 0, sizeof(c));
+ p = c;
+
+ 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 */
+ *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;
+
+ if(ctlr->family >= 7000)
+ return nil;
+
+ /* 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*
+sendpagingcmd(Ctlr *ctlr)
+{
+ 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;
+
+ p = c;
+ *p++ = 0; // read op
+ *p++ = 0; // target
+ put16(p, type);
+ p += 2;
+ put16(p, off);
+ p += 2;
+ put16(p, len);
+ p += 2;
+
+ 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 {
+ 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;
+
+ if((err = niclock(ctlr)) != nil)
+ return err;
+ a0 = prphread(ctlr, 0xa03080);
+ a1 = prphread(ctlr, 0xa03084);
+ nicunlock(ctlr);
+
+ 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);
+
+ return nil;
+}
+
+static char*
+sendtxantconfig(Ctlr *ctlr, uint val)
+{
+ uchar c[4];
+
+ put32(c, val);
+ return cmd(ctlr, 152, c, 4);
+}
+
+static char*
+sendphyconfig(Ctlr *ctlr, u32int physku, u32int flowmask, u32int eventmask)
+{
+ uchar c[3*4];
+
+ put32(c+0, physku);
+ put32(c+4, flowmask);
+ put32(c+8, eventmask);
+ return cmd(ctlr, 106, c, 3*4);
+}
+
+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 {
+ p += 2; /* reserved */
+ }
+
+ memmove(p, addr, 6);
+ p += 8;
+
+ *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 */
+ }
+ } 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 */
+ }
+
+ 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;
+
+ 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 int
+timeeventdone(void *arg)
+{
+ Ctlr *ctlr = arg;
+ return ctlr->te.id == -1 || ctlr->te.active != 0;
+}
+
+static char*
+settimeevent(Ctlr *ctlr, int amr, int ival)
+{
+ int duration, delay, timeid;
+ uchar c[9*4], *p;
+ char *err;
+
+ switch(amr){
+ case CmdAdd:
+ timeid = ctlr->te.id;
+ if(timeid == -1)
+ timeid = 0;
+ else {
+ if(ctlr->te.active)
+ return nil;
+ amr = CmdModify;
+ }
+ break;
+ default:
+ timeid = ctlr->te.id;
+ if(timeid == -1)
+ return nil;
+ break;
+ }
+
+ if(ival){
+ duration = ival*2;
+ delay = ival/2;
+ } else {
+ duration = 1024;
+ delay = 0;
+ }
+
+ 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;
+
+ ctlr->te.active = 0;
+ if((err = cmd(ctlr, 41, c, p - c)) != nil)
+ return err;
+
+ if(amr == CmdRemove){
+ ctlr->te.active = 0;
+ ctlr->te.id = -1;
+ return nil;
+ }
+ tsleep(&ctlr->te, timeeventdone, ctlr, 100);
+ return ctlr->te.active? nil: "timeevent did not start";
+}
+
+
+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 void
+tttxbackoff(Ctlr *ctlr)
+{
+ uchar c[4];
+
+ put32(c, 0);
+ cmd(ctlr, 126, c, sizeof(c));
+}
+
+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 = 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";