1*43f728cbSDavid du Colombier #include "u.h"
2*43f728cbSDavid du Colombier #include "../port/lib.h"
3*43f728cbSDavid du Colombier #include "mem.h"
4*43f728cbSDavid du Colombier #include "dat.h"
5*43f728cbSDavid du Colombier #include "fns.h"
6*43f728cbSDavid du Colombier #include "io.h"
7*43f728cbSDavid du Colombier #include "../port/error.h"
8*43f728cbSDavid du Colombier #include "../port/netif.h"
9*43f728cbSDavid du Colombier
10*43f728cbSDavid du Colombier #include "etherif.h"
11*43f728cbSDavid du Colombier #include "wifi.h"
12*43f728cbSDavid du Colombier
13*43f728cbSDavid du Colombier enum {
14*43f728cbSDavid du Colombier Nrxlog = 6,
15*43f728cbSDavid du Colombier Nrx = 1<<Nrxlog,
16*43f728cbSDavid du Colombier Ntx = 256,
17*43f728cbSDavid du Colombier
18*43f728cbSDavid du Colombier Rbufsize = 3*1024,
19*43f728cbSDavid du Colombier Rdscsize = 8,
20*43f728cbSDavid du Colombier
21*43f728cbSDavid du Colombier Tdscsize = 64,
22*43f728cbSDavid du Colombier Tcmdsize = 128,
23*43f728cbSDavid du Colombier };
24*43f728cbSDavid du Colombier
25*43f728cbSDavid du Colombier /* registers */
26*43f728cbSDavid du Colombier enum {
27*43f728cbSDavid du Colombier Cfg = 0x000,
28*43f728cbSDavid du Colombier AlmMb = 1<<8,
29*43f728cbSDavid du Colombier AlmMm = 1<<9,
30*43f728cbSDavid du Colombier SkuMrc = 1<<10,
31*43f728cbSDavid du Colombier RevD = 1<<11,
32*43f728cbSDavid du Colombier TypeB = 1<<12,
33*43f728cbSDavid du Colombier Isr = 0x008,
34*43f728cbSDavid du Colombier Imr = 0x00c,
35*43f728cbSDavid du Colombier Ialive = 1<<0,
36*43f728cbSDavid du Colombier Iwakeup = 1<<1,
37*43f728cbSDavid du Colombier Iswrx = 1<<3,
38*43f728cbSDavid du Colombier Irftoggled = 1<<7,
39*43f728cbSDavid du Colombier Iswerr = 1<<25,
40*43f728cbSDavid du Colombier Ifhtx = 1<<27,
41*43f728cbSDavid du Colombier Ihwerr = 1<<29,
42*43f728cbSDavid du Colombier Ifhrx = 1<<31,
43*43f728cbSDavid du Colombier Ierr = Iswerr | Ihwerr,
44*43f728cbSDavid du Colombier Idefmask = Ierr | Ifhtx | Ifhrx | Ialive | Iwakeup | Iswrx | Irftoggled,
45*43f728cbSDavid du Colombier FhIsr = 0x010,
46*43f728cbSDavid du Colombier GpioIn = 0x018,
47*43f728cbSDavid du Colombier Reset = 0x020,
48*43f728cbSDavid du Colombier Nevo = 1<<0,
49*43f728cbSDavid du Colombier SW = 1<<7,
50*43f728cbSDavid du Colombier MasterDisabled = 1<<8,
51*43f728cbSDavid du Colombier StopMaster = 1<<9,
52*43f728cbSDavid du Colombier
53*43f728cbSDavid du Colombier Gpc = 0x024,
54*43f728cbSDavid du Colombier MacAccessEna = 1<<0,
55*43f728cbSDavid du Colombier MacClockReady = 1<<0,
56*43f728cbSDavid du Colombier InitDone = 1<<2,
57*43f728cbSDavid du Colombier MacAccessReq = 1<<3,
58*43f728cbSDavid du Colombier NicSleep = 1<<4,
59*43f728cbSDavid du Colombier RfKill = 1<<27,
60*43f728cbSDavid du Colombier Eeprom = 0x02c,
61*43f728cbSDavid du Colombier EepromGp = 0x030,
62*43f728cbSDavid du Colombier
63*43f728cbSDavid du Colombier UcodeGp1Clr = 0x05c,
64*43f728cbSDavid du Colombier UcodeGp1RfKill = 1<<1,
65*43f728cbSDavid du Colombier UcodeGp1CmdBlocked = 1<<2,
66*43f728cbSDavid du Colombier UcodeGp2 = 0x060,
67*43f728cbSDavid du Colombier
68*43f728cbSDavid du Colombier GioChicken = 0x100,
69*43f728cbSDavid du Colombier L1AnoL0Srx = 1<<23,
70*43f728cbSDavid du Colombier AnaPll = 0x20c,
71*43f728cbSDavid du Colombier Init = 1<<24,
72*43f728cbSDavid du Colombier
73*43f728cbSDavid du Colombier PrphWaddr = 0x444,
74*43f728cbSDavid du Colombier PrphRaddr = 0x448,
75*43f728cbSDavid du Colombier PrphWdata = 0x44c,
76*43f728cbSDavid du Colombier PrphRdata = 0x450,
77*43f728cbSDavid du Colombier HbusTargWptr = 0x460,
78*43f728cbSDavid du Colombier };
79*43f728cbSDavid du Colombier
80*43f728cbSDavid du Colombier /*
81*43f728cbSDavid du Colombier * Flow-Handler registers.
82*43f728cbSDavid du Colombier */
83*43f728cbSDavid du Colombier enum {
84*43f728cbSDavid du Colombier FhCbbcCtrl = 0x940,
85*43f728cbSDavid du Colombier FhCbbcBase = 0x944,
86*43f728cbSDavid du Colombier FhRxConfig = 0xc00,
87*43f728cbSDavid du Colombier FhRxConfigDmaEna = 1<<31,
88*43f728cbSDavid du Colombier FhRxConfigRdrbdEna = 1<<29,
89*43f728cbSDavid du Colombier FhRxConfigWrstatusEna = 1<<27,
90*43f728cbSDavid du Colombier FhRxConfigMaxfrag = 1<<24,
91*43f728cbSDavid du Colombier FhRxConfigIrqDstHost = 1<<12,
92*43f728cbSDavid du Colombier
93*43f728cbSDavid du Colombier FhRxConfigNrdbShift = 20,
94*43f728cbSDavid du Colombier FhRxConfigIrqRbthShift = 4,
95*43f728cbSDavid du Colombier FhRxBase = 0xc04,
96*43f728cbSDavid du Colombier FhRxWptr = 0xc20,
97*43f728cbSDavid du Colombier FhRxRptrAddr = 0xc24,
98*43f728cbSDavid du Colombier FhRssrTbl = 0xcc0,
99*43f728cbSDavid du Colombier FhRxStatus = 0xcc4,
100*43f728cbSDavid du Colombier FhTxConfig = 0xd00, // +q*32
101*43f728cbSDavid du Colombier FhTxBase = 0xe80,
102*43f728cbSDavid du Colombier FhMsgConfig = 0xe88,
103*43f728cbSDavid du Colombier FhTxStatus = 0xe90,
104*43f728cbSDavid du Colombier };
105*43f728cbSDavid du Colombier
106*43f728cbSDavid du Colombier /*
107*43f728cbSDavid du Colombier * NIC internal memory offsets.
108*43f728cbSDavid du Colombier */
109*43f728cbSDavid du Colombier enum {
110*43f728cbSDavid du Colombier AlmSchedMode = 0x2e00,
111*43f728cbSDavid du Colombier AlmSchedArastat = 0x2e04,
112*43f728cbSDavid du Colombier AlmSchedTxfact = 0x2e10,
113*43f728cbSDavid du Colombier AlmSchedTxf4mf = 0x2e14,
114*43f728cbSDavid du Colombier AlmSchedTxf5mf = 0x2e20,
115*43f728cbSDavid du Colombier AlmSchedBP1 = 0x2e2c,
116*43f728cbSDavid du Colombier AlmSchedBP2 = 0x2e30,
117*43f728cbSDavid du Colombier ApmgClkEna = 0x3004,
118*43f728cbSDavid du Colombier ApmgClkDis = 0x3008,
119*43f728cbSDavid du Colombier DmaClkRqt = 1<<9,
120*43f728cbSDavid du Colombier BsmClkRqt = 1<<11,
121*43f728cbSDavid du Colombier ApmgPs = 0x300c,
122*43f728cbSDavid du Colombier PwrSrcVMain = 0<<24,
123*43f728cbSDavid du Colombier PwrSrcMask = 3<<24,
124*43f728cbSDavid du Colombier
125*43f728cbSDavid du Colombier ApmgPciStt = 0x3010,
126*43f728cbSDavid du Colombier
127*43f728cbSDavid du Colombier BsmWrCtrl = 0x3400,
128*43f728cbSDavid du Colombier BsmWrMemSrc = 0x3404,
129*43f728cbSDavid du Colombier BsmWrMemDst = 0x3408,
130*43f728cbSDavid du Colombier BsmWrDwCount = 0x340c,
131*43f728cbSDavid du Colombier BsmDramTextAddr = 0x3490,
132*43f728cbSDavid du Colombier BsmDramTextSize = 0x3494,
133*43f728cbSDavid du Colombier BsmDramDataAddr = 0x3498,
134*43f728cbSDavid du Colombier BsmDramDataSize = 0x349c,
135*43f728cbSDavid du Colombier BsmSramBase = 0x3800,
136*43f728cbSDavid du Colombier };
137*43f728cbSDavid du Colombier
138*43f728cbSDavid du Colombier enum {
139*43f728cbSDavid du Colombier FilterPromisc = 1<<0,
140*43f728cbSDavid du Colombier FilterCtl = 1<<1,
141*43f728cbSDavid du Colombier FilterMulticast = 1<<2,
142*43f728cbSDavid du Colombier FilterNoDecrypt = 1<<3,
143*43f728cbSDavid du Colombier FilterBSS = 1<<5,
144*43f728cbSDavid du Colombier };
145*43f728cbSDavid du Colombier
146*43f728cbSDavid du Colombier enum {
147*43f728cbSDavid du Colombier RFlag24Ghz = 1<<0,
148*43f728cbSDavid du Colombier RFlagCCK = 1<<1,
149*43f728cbSDavid du Colombier RFlagAuto = 1<<2,
150*43f728cbSDavid du Colombier RFlagShSlot = 1<<4,
151*43f728cbSDavid du Colombier RFlagShPreamble = 1<<5,
152*43f728cbSDavid du Colombier RFlagNoDiversity = 1<<7,
153*43f728cbSDavid du Colombier RFlagAntennaA = 1<<8,
154*43f728cbSDavid du Colombier RFlagAntennaB = 1<<9,
155*43f728cbSDavid du Colombier RFlagTSF = 1<<15,
156*43f728cbSDavid du Colombier };
157*43f728cbSDavid du Colombier
158*43f728cbSDavid du Colombier typedef struct FWSect FWSect;
159*43f728cbSDavid du Colombier typedef struct FWImage FWImage;
160*43f728cbSDavid du Colombier
161*43f728cbSDavid du Colombier typedef struct TXQ TXQ;
162*43f728cbSDavid du Colombier typedef struct RXQ RXQ;
163*43f728cbSDavid du Colombier
164*43f728cbSDavid du Colombier typedef struct Shared Shared;
165*43f728cbSDavid du Colombier typedef struct Sample Sample;
166*43f728cbSDavid du Colombier typedef struct Powergrp Powergrp;
167*43f728cbSDavid du Colombier
168*43f728cbSDavid du Colombier typedef struct Ctlr Ctlr;
169*43f728cbSDavid du Colombier
170*43f728cbSDavid du Colombier struct FWSect
171*43f728cbSDavid du Colombier {
172*43f728cbSDavid du Colombier uchar *data;
173*43f728cbSDavid du Colombier uint size;
174*43f728cbSDavid du Colombier };
175*43f728cbSDavid du Colombier
176*43f728cbSDavid du Colombier struct FWImage
177*43f728cbSDavid du Colombier {
178*43f728cbSDavid du Colombier struct {
179*43f728cbSDavid du Colombier FWSect text;
180*43f728cbSDavid du Colombier FWSect data;
181*43f728cbSDavid du Colombier } init, main, boot;
182*43f728cbSDavid du Colombier
183*43f728cbSDavid du Colombier uint version;
184*43f728cbSDavid du Colombier uchar data[];
185*43f728cbSDavid du Colombier };
186*43f728cbSDavid du Colombier
187*43f728cbSDavid du Colombier struct TXQ
188*43f728cbSDavid du Colombier {
189*43f728cbSDavid du Colombier uint n;
190*43f728cbSDavid du Colombier uint i;
191*43f728cbSDavid du Colombier Block **b;
192*43f728cbSDavid du Colombier uchar *d;
193*43f728cbSDavid du Colombier uchar *c;
194*43f728cbSDavid du Colombier
195*43f728cbSDavid du Colombier uint lastcmd;
196*43f728cbSDavid du Colombier
197*43f728cbSDavid du Colombier Rendez;
198*43f728cbSDavid du Colombier QLock;
199*43f728cbSDavid du Colombier };
200*43f728cbSDavid du Colombier
201*43f728cbSDavid du Colombier struct RXQ
202*43f728cbSDavid du Colombier {
203*43f728cbSDavid du Colombier uint i;
204*43f728cbSDavid du Colombier Block **b;
205*43f728cbSDavid du Colombier u32int *p;
206*43f728cbSDavid du Colombier };
207*43f728cbSDavid du Colombier
208*43f728cbSDavid du Colombier struct Shared
209*43f728cbSDavid du Colombier {
210*43f728cbSDavid du Colombier u32int txbase[8];
211*43f728cbSDavid du Colombier u32int next;
212*43f728cbSDavid du Colombier u32int reserved[2];
213*43f728cbSDavid du Colombier };
214*43f728cbSDavid du Colombier
215*43f728cbSDavid du Colombier struct Sample
216*43f728cbSDavid du Colombier {
217*43f728cbSDavid du Colombier uchar index;
218*43f728cbSDavid du Colombier char power;
219*43f728cbSDavid du Colombier };
220*43f728cbSDavid du Colombier
221*43f728cbSDavid du Colombier struct Powergrp
222*43f728cbSDavid du Colombier {
223*43f728cbSDavid du Colombier uchar chan;
224*43f728cbSDavid du Colombier char maxpwr;
225*43f728cbSDavid du Colombier short temp;
226*43f728cbSDavid du Colombier Sample samples[5];
227*43f728cbSDavid du Colombier };
228*43f728cbSDavid du Colombier
229*43f728cbSDavid du Colombier struct Ctlr {
230*43f728cbSDavid du Colombier Lock;
231*43f728cbSDavid du Colombier QLock;
232*43f728cbSDavid du Colombier
233*43f728cbSDavid du Colombier Ctlr *link;
234*43f728cbSDavid du Colombier Pcidev *pdev;
235*43f728cbSDavid du Colombier Wifi *wifi;
236*43f728cbSDavid du Colombier
237*43f728cbSDavid du Colombier int port;
238*43f728cbSDavid du Colombier int power;
239*43f728cbSDavid du Colombier int active;
240*43f728cbSDavid du Colombier int broken;
241*43f728cbSDavid du Colombier int attached;
242*43f728cbSDavid du Colombier
243*43f728cbSDavid du Colombier int temp;
244*43f728cbSDavid du Colombier u32int ie;
245*43f728cbSDavid du Colombier u32int *nic;
246*43f728cbSDavid du Colombier
247*43f728cbSDavid du Colombier /* assigned node ids in hardware node table or -1 if unassigned */
248*43f728cbSDavid du Colombier int bcastnodeid;
249*43f728cbSDavid du Colombier int bssnodeid;
250*43f728cbSDavid du Colombier
251*43f728cbSDavid du Colombier /* current receiver settings */
252*43f728cbSDavid du Colombier uchar bssid[Eaddrlen];
253*43f728cbSDavid du Colombier int channel;
254*43f728cbSDavid du Colombier int prom;
255*43f728cbSDavid du Colombier int aid;
256*43f728cbSDavid du Colombier
257*43f728cbSDavid du Colombier RXQ rx;
258*43f728cbSDavid du Colombier TXQ tx[8];
259*43f728cbSDavid du Colombier
260*43f728cbSDavid du Colombier struct {
261*43f728cbSDavid du Colombier Rendez;
262*43f728cbSDavid du Colombier u32int m;
263*43f728cbSDavid du Colombier u32int w;
264*43f728cbSDavid du Colombier } wait;
265*43f728cbSDavid du Colombier
266*43f728cbSDavid du Colombier struct {
267*43f728cbSDavid du Colombier uchar cap;
268*43f728cbSDavid du Colombier u16int rev;
269*43f728cbSDavid du Colombier uchar type;
270*43f728cbSDavid du Colombier
271*43f728cbSDavid du Colombier char regdom[4+1];
272*43f728cbSDavid du Colombier
273*43f728cbSDavid du Colombier Powergrp pwrgrps[5];
274*43f728cbSDavid du Colombier } eeprom;
275*43f728cbSDavid du Colombier
276*43f728cbSDavid du Colombier char maxpwr[256];
277*43f728cbSDavid du Colombier
278*43f728cbSDavid du Colombier Shared *shared;
279*43f728cbSDavid du Colombier
280*43f728cbSDavid du Colombier FWImage *fw;
281*43f728cbSDavid du Colombier };
282*43f728cbSDavid du Colombier
283*43f728cbSDavid du Colombier static void setled(Ctlr *ctlr, int which, int on, int off);
284*43f728cbSDavid du Colombier
285*43f728cbSDavid du Colombier #define csr32r(c, r) (*((c)->nic+((r)/4)))
286*43f728cbSDavid du Colombier #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
287*43f728cbSDavid du Colombier
288*43f728cbSDavid du Colombier static uint
get32(uchar * p)289*43f728cbSDavid du Colombier get32(uchar *p){
290*43f728cbSDavid du Colombier return *((u32int*)p);
291*43f728cbSDavid du Colombier }
292*43f728cbSDavid du Colombier static uint
get16(uchar * p)293*43f728cbSDavid du Colombier get16(uchar *p)
294*43f728cbSDavid du Colombier {
295*43f728cbSDavid du Colombier return *((u16int*)p);
296*43f728cbSDavid du Colombier }
297*43f728cbSDavid du Colombier static void
put32(uchar * p,uint v)298*43f728cbSDavid du Colombier put32(uchar *p, uint v){
299*43f728cbSDavid du Colombier *((u32int*)p) = v;
300*43f728cbSDavid du Colombier }
301*43f728cbSDavid du Colombier static void
put16(uchar * p,uint v)302*43f728cbSDavid du Colombier put16(uchar *p, uint v){
303*43f728cbSDavid du Colombier *((u16int*)p) = v;
304*43f728cbSDavid du Colombier };
305*43f728cbSDavid du Colombier
306*43f728cbSDavid du Colombier static char*
niclock(Ctlr * ctlr)307*43f728cbSDavid du Colombier niclock(Ctlr *ctlr)
308*43f728cbSDavid du Colombier {
309*43f728cbSDavid du Colombier int i;
310*43f728cbSDavid du Colombier
311*43f728cbSDavid du Colombier csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | MacAccessReq);
312*43f728cbSDavid du Colombier for(i=0; i<1000; i++){
313*43f728cbSDavid du Colombier if((csr32r(ctlr, Gpc) & (NicSleep | MacAccessEna)) == MacAccessEna)
314*43f728cbSDavid du Colombier return 0;
315*43f728cbSDavid du Colombier delay(10);
316*43f728cbSDavid du Colombier }
317*43f728cbSDavid du Colombier return "niclock: timeout";
318*43f728cbSDavid du Colombier }
319*43f728cbSDavid du Colombier
320*43f728cbSDavid du Colombier static void
nicunlock(Ctlr * ctlr)321*43f728cbSDavid du Colombier nicunlock(Ctlr *ctlr)
322*43f728cbSDavid du Colombier {
323*43f728cbSDavid du Colombier csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) & ~MacAccessReq);
324*43f728cbSDavid du Colombier }
325*43f728cbSDavid du Colombier
326*43f728cbSDavid du Colombier static u32int
prphread(Ctlr * ctlr,uint off)327*43f728cbSDavid du Colombier prphread(Ctlr *ctlr, uint off)
328*43f728cbSDavid du Colombier {
329*43f728cbSDavid du Colombier csr32w(ctlr, PrphRaddr, ((sizeof(u32int)-1)<<24) | off);
330*43f728cbSDavid du Colombier coherence();
331*43f728cbSDavid du Colombier return csr32r(ctlr, PrphRdata);
332*43f728cbSDavid du Colombier }
333*43f728cbSDavid du Colombier static void
prphwrite(Ctlr * ctlr,uint off,u32int data)334*43f728cbSDavid du Colombier prphwrite(Ctlr *ctlr, uint off, u32int data)
335*43f728cbSDavid du Colombier {
336*43f728cbSDavid du Colombier csr32w(ctlr, PrphWaddr, ((sizeof(u32int)-1)<<24) | off);
337*43f728cbSDavid du Colombier coherence();
338*43f728cbSDavid du Colombier csr32w(ctlr, PrphWdata, data);
339*43f728cbSDavid du Colombier }
340*43f728cbSDavid du Colombier
341*43f728cbSDavid du Colombier static char*
eepromread(Ctlr * ctlr,void * data,int count,uint off)342*43f728cbSDavid du Colombier eepromread(Ctlr *ctlr, void *data, int count, uint off)
343*43f728cbSDavid du Colombier {
344*43f728cbSDavid du Colombier uchar *out = data;
345*43f728cbSDavid du Colombier char *err;
346*43f728cbSDavid du Colombier u32int w = 0;
347*43f728cbSDavid du Colombier int i;
348*43f728cbSDavid du Colombier
349*43f728cbSDavid du Colombier if((err = niclock(ctlr)) != nil)
350*43f728cbSDavid du Colombier return err;
351*43f728cbSDavid du Colombier
352*43f728cbSDavid du Colombier for(; count > 0; count -= 2, off++){
353*43f728cbSDavid du Colombier csr32w(ctlr, Eeprom, off << 2);
354*43f728cbSDavid du Colombier csr32w(ctlr, Eeprom, csr32r(ctlr, Eeprom) & ~(1<<1));
355*43f728cbSDavid du Colombier
356*43f728cbSDavid du Colombier for(i = 0; i < 10; i++){
357*43f728cbSDavid du Colombier w = csr32r(ctlr, Eeprom);
358*43f728cbSDavid du Colombier if(w & 1)
359*43f728cbSDavid du Colombier break;
360*43f728cbSDavid du Colombier delay(5);
361*43f728cbSDavid du Colombier }
362*43f728cbSDavid du Colombier if(i == 10)
363*43f728cbSDavid du Colombier break;
364*43f728cbSDavid du Colombier *out++ = w >> 16;
365*43f728cbSDavid du Colombier if(count > 1)
366*43f728cbSDavid du Colombier *out++ = w >> 24;
367*43f728cbSDavid du Colombier }
368*43f728cbSDavid du Colombier nicunlock(ctlr);
369*43f728cbSDavid du Colombier
370*43f728cbSDavid du Colombier if(count > 0)
371*43f728cbSDavid du Colombier return "eeprompread: timeout";
372*43f728cbSDavid du Colombier return nil;
373*43f728cbSDavid du Colombier }
374*43f728cbSDavid du Colombier
375*43f728cbSDavid du Colombier static char*
clockwait(Ctlr * ctlr)376*43f728cbSDavid du Colombier clockwait(Ctlr *ctlr)
377*43f728cbSDavid du Colombier {
378*43f728cbSDavid du Colombier int i;
379*43f728cbSDavid du Colombier
380*43f728cbSDavid du Colombier /* Set "initialization complete" bit. */
381*43f728cbSDavid du Colombier csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | InitDone);
382*43f728cbSDavid du Colombier for(i=0; i<2500; i++){
383*43f728cbSDavid du Colombier if(csr32r(ctlr, Gpc) & MacClockReady)
384*43f728cbSDavid du Colombier return nil;
385*43f728cbSDavid du Colombier delay(10);
386*43f728cbSDavid du Colombier }
387*43f728cbSDavid du Colombier return "clockwait: timeout";
388*43f728cbSDavid du Colombier }
389*43f728cbSDavid du Colombier
390*43f728cbSDavid du Colombier static char*
poweron(Ctlr * ctlr)391*43f728cbSDavid du Colombier poweron(Ctlr *ctlr)
392*43f728cbSDavid du Colombier {
393*43f728cbSDavid du Colombier char *err;
394*43f728cbSDavid du Colombier
395*43f728cbSDavid du Colombier if(ctlr->power)
396*43f728cbSDavid du Colombier return nil;
397*43f728cbSDavid du Colombier
398*43f728cbSDavid du Colombier csr32w(ctlr, AnaPll, csr32r(ctlr, AnaPll) | Init);
399*43f728cbSDavid du Colombier /* Disable L0s. */
400*43f728cbSDavid du Colombier csr32w(ctlr, GioChicken, csr32r(ctlr, GioChicken) | L1AnoL0Srx);
401*43f728cbSDavid du Colombier
402*43f728cbSDavid du Colombier if((err = clockwait(ctlr)) != nil)
403*43f728cbSDavid du Colombier return err;
404*43f728cbSDavid du Colombier
405*43f728cbSDavid du Colombier if((err = niclock(ctlr)) != nil)
406*43f728cbSDavid du Colombier return err;
407*43f728cbSDavid du Colombier
408*43f728cbSDavid du Colombier prphwrite(ctlr, ApmgClkEna, DmaClkRqt | BsmClkRqt);
409*43f728cbSDavid du Colombier delay(20);
410*43f728cbSDavid du Colombier
411*43f728cbSDavid du Colombier /* Disable L1. */
412*43f728cbSDavid du Colombier prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11));
413*43f728cbSDavid du Colombier
414*43f728cbSDavid du Colombier nicunlock(ctlr);
415*43f728cbSDavid du Colombier
416*43f728cbSDavid du Colombier ctlr->power = 1;
417*43f728cbSDavid du Colombier
418*43f728cbSDavid du Colombier return nil;
419*43f728cbSDavid du Colombier }
420*43f728cbSDavid du Colombier
421*43f728cbSDavid du Colombier static void
poweroff(Ctlr * ctlr)422*43f728cbSDavid du Colombier poweroff(Ctlr *ctlr)
423*43f728cbSDavid du Colombier {
424*43f728cbSDavid du Colombier int i, j;
425*43f728cbSDavid du Colombier
426*43f728cbSDavid du Colombier csr32w(ctlr, Reset, Nevo);
427*43f728cbSDavid du Colombier
428*43f728cbSDavid du Colombier /* Disable interrupts. */
429*43f728cbSDavid du Colombier csr32w(ctlr, Imr, 0);
430*43f728cbSDavid du Colombier csr32w(ctlr, Isr, ~0);
431*43f728cbSDavid du Colombier csr32w(ctlr, FhIsr, ~0);
432*43f728cbSDavid du Colombier
433*43f728cbSDavid du Colombier if(niclock(ctlr) == nil){
434*43f728cbSDavid du Colombier /* Stop TX scheduler. */
435*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedMode, 0);
436*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedTxfact, 0);
437*43f728cbSDavid du Colombier
438*43f728cbSDavid du Colombier /* Stop all DMA channels */
439*43f728cbSDavid du Colombier for(i = 0; i < 6; i++){
440*43f728cbSDavid du Colombier csr32w(ctlr, FhTxConfig + i*32, 0);
441*43f728cbSDavid du Colombier for(j = 0; j < 100; j++){
442*43f728cbSDavid du Colombier if((csr32r(ctlr, FhTxStatus) & (0x1010000<<i)) == (0x1010000<<i))
443*43f728cbSDavid du Colombier break;
444*43f728cbSDavid du Colombier delay(10);
445*43f728cbSDavid du Colombier }
446*43f728cbSDavid du Colombier }
447*43f728cbSDavid du Colombier nicunlock(ctlr);
448*43f728cbSDavid du Colombier }
449*43f728cbSDavid du Colombier
450*43f728cbSDavid du Colombier /* Stop RX ring. */
451*43f728cbSDavid du Colombier if(niclock(ctlr) == nil){
452*43f728cbSDavid du Colombier csr32w(ctlr, FhRxConfig, 0);
453*43f728cbSDavid du Colombier for(j = 0; j < 100; j++){
454*43f728cbSDavid du Colombier if(csr32r(ctlr, FhRxStatus) & (1<<24))
455*43f728cbSDavid du Colombier break;
456*43f728cbSDavid du Colombier delay(10);
457*43f728cbSDavid du Colombier }
458*43f728cbSDavid du Colombier nicunlock(ctlr);
459*43f728cbSDavid du Colombier }
460*43f728cbSDavid du Colombier
461*43f728cbSDavid du Colombier if(niclock(ctlr) == nil){
462*43f728cbSDavid du Colombier prphwrite(ctlr, ApmgClkDis, DmaClkRqt);
463*43f728cbSDavid du Colombier nicunlock(ctlr);
464*43f728cbSDavid du Colombier }
465*43f728cbSDavid du Colombier delay(5);
466*43f728cbSDavid du Colombier
467*43f728cbSDavid du Colombier csr32w(ctlr, Reset, csr32r(ctlr, Reset) | StopMaster);
468*43f728cbSDavid du Colombier
469*43f728cbSDavid du Colombier if((csr32r(ctlr, Gpc) & (7<<24)) != (4<<24)){
470*43f728cbSDavid du Colombier for(j = 0; j < 100; j++){
471*43f728cbSDavid du Colombier if(csr32r(ctlr, Reset) & MasterDisabled)
472*43f728cbSDavid du Colombier break;
473*43f728cbSDavid du Colombier delay(10);
474*43f728cbSDavid du Colombier }
475*43f728cbSDavid du Colombier }
476*43f728cbSDavid du Colombier
477*43f728cbSDavid du Colombier csr32w(ctlr, Reset, csr32r(ctlr, Reset) | SW);
478*43f728cbSDavid du Colombier
479*43f728cbSDavid du Colombier ctlr->power = 0;
480*43f728cbSDavid du Colombier }
481*43f728cbSDavid du Colombier
482*43f728cbSDavid du Colombier static struct {
483*43f728cbSDavid du Colombier u32int addr; /* offset in EEPROM */
484*43f728cbSDavid du Colombier u8int nchan;
485*43f728cbSDavid du Colombier u8int chan[14];
486*43f728cbSDavid du Colombier } bands[5] = {
487*43f728cbSDavid du Colombier { 0x63, 14,
488*43f728cbSDavid du Colombier { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } },
489*43f728cbSDavid du Colombier { 0x72, 13,
490*43f728cbSDavid du Colombier { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } },
491*43f728cbSDavid du Colombier { 0x80, 12,
492*43f728cbSDavid du Colombier { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } },
493*43f728cbSDavid du Colombier { 0x8d, 11,
494*43f728cbSDavid du Colombier { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } },
495*43f728cbSDavid du Colombier { 0x99, 6,
496*43f728cbSDavid du Colombier { 145, 149, 153, 157, 161, 165 } }
497*43f728cbSDavid du Colombier };
498*43f728cbSDavid du Colombier
499*43f728cbSDavid du Colombier static int
wpiinit(Ether * edev)500*43f728cbSDavid du Colombier wpiinit(Ether *edev)
501*43f728cbSDavid du Colombier {
502*43f728cbSDavid du Colombier Ctlr *ctlr;
503*43f728cbSDavid du Colombier char *err;
504*43f728cbSDavid du Colombier uchar b[64];
505*43f728cbSDavid du Colombier int i, j;
506*43f728cbSDavid du Colombier Powergrp *g;
507*43f728cbSDavid du Colombier
508*43f728cbSDavid du Colombier ctlr = edev->ctlr;
509*43f728cbSDavid du Colombier if((err = poweron(ctlr)) != nil)
510*43f728cbSDavid du Colombier goto Err;
511*43f728cbSDavid du Colombier if((csr32r(ctlr, EepromGp) & 0x6) == 0){
512*43f728cbSDavid du Colombier err = "bad rom signature";
513*43f728cbSDavid du Colombier goto Err;
514*43f728cbSDavid du Colombier }
515*43f728cbSDavid du Colombier /* Clear HW ownership of EEPROM. */
516*43f728cbSDavid du Colombier csr32w(ctlr, EepromGp, csr32r(ctlr, EepromGp) & ~0x180);
517*43f728cbSDavid du Colombier
518*43f728cbSDavid du Colombier if((err = eepromread(ctlr, b, 1, 0x45)) != nil)
519*43f728cbSDavid du Colombier goto Err;
520*43f728cbSDavid du Colombier ctlr->eeprom.cap = b[0];
521*43f728cbSDavid du Colombier if((err = eepromread(ctlr, b, 2, 0x35)) != nil)
522*43f728cbSDavid du Colombier goto Err;
523*43f728cbSDavid du Colombier ctlr->eeprom.rev = get16(b);
524*43f728cbSDavid du Colombier if((err = eepromread(ctlr, b, 1, 0x4a)) != nil)
525*43f728cbSDavid du Colombier goto Err;
526*43f728cbSDavid du Colombier ctlr->eeprom.type = b[0];
527*43f728cbSDavid du Colombier if((err = eepromread(ctlr, b, 4, 0x60)) != nil)
528*43f728cbSDavid du Colombier goto Err;
529*43f728cbSDavid du Colombier strncpy(ctlr->eeprom.regdom, (char*)b, 4);
530*43f728cbSDavid du Colombier ctlr->eeprom.regdom[4] = '\0';
531*43f728cbSDavid du Colombier
532*43f728cbSDavid du Colombier print("wpi: %X %X %X %s\n", ctlr->eeprom.cap, ctlr->eeprom.rev, ctlr->eeprom.type, ctlr->eeprom.regdom);
533*43f728cbSDavid du Colombier
534*43f728cbSDavid du Colombier if((err = eepromread(ctlr, b, 6, 0x15)) != nil)
535*43f728cbSDavid du Colombier goto Err;
536*43f728cbSDavid du Colombier memmove(edev->ea, b, Eaddrlen);
537*43f728cbSDavid du Colombier
538*43f728cbSDavid du Colombier memset(ctlr->maxpwr, 0, sizeof(ctlr->maxpwr));
539*43f728cbSDavid du Colombier for(i = 0; i < nelem(bands); i++){
540*43f728cbSDavid du Colombier if((err = eepromread(ctlr, b, 2*bands[i].nchan, bands[i].addr)) != nil)
541*43f728cbSDavid du Colombier goto Err;
542*43f728cbSDavid du Colombier for(j = 0; j < bands[i].nchan; j++){
543*43f728cbSDavid du Colombier if(!(b[j*2] & 1))
544*43f728cbSDavid du Colombier continue;
545*43f728cbSDavid du Colombier ctlr->maxpwr[bands[i].chan[j]] = b[j*2+1];
546*43f728cbSDavid du Colombier }
547*43f728cbSDavid du Colombier }
548*43f728cbSDavid du Colombier
549*43f728cbSDavid du Colombier for(i = 0; i < nelem(ctlr->eeprom.pwrgrps); i++){
550*43f728cbSDavid du Colombier if((err = eepromread(ctlr, b, 64, 0x100 + i*32)) != nil)
551*43f728cbSDavid du Colombier goto Err;
552*43f728cbSDavid du Colombier g = &ctlr->eeprom.pwrgrps[i];
553*43f728cbSDavid du Colombier g->maxpwr = b[60];
554*43f728cbSDavid du Colombier g->chan = b[61];
555*43f728cbSDavid du Colombier g->temp = get16(b+62);
556*43f728cbSDavid du Colombier for(j = 0; j < 5; j++){
557*43f728cbSDavid du Colombier g->samples[j].index = b[j*4];
558*43f728cbSDavid du Colombier g->samples[j].power = b[j*4+1];
559*43f728cbSDavid du Colombier }
560*43f728cbSDavid du Colombier }
561*43f728cbSDavid du Colombier
562*43f728cbSDavid du Colombier poweroff(ctlr);
563*43f728cbSDavid du Colombier return 0;
564*43f728cbSDavid du Colombier Err:
565*43f728cbSDavid du Colombier print("wpiinit: %s\n", err);
566*43f728cbSDavid du Colombier poweroff(ctlr);
567*43f728cbSDavid du Colombier return -1;
568*43f728cbSDavid du Colombier }
569*43f728cbSDavid du Colombier
570*43f728cbSDavid du Colombier static char*
crackfw(FWImage * i,uchar * data,uint size)571*43f728cbSDavid du Colombier crackfw(FWImage *i, uchar *data, uint size)
572*43f728cbSDavid du Colombier {
573*43f728cbSDavid du Colombier uchar *p, *e;
574*43f728cbSDavid du Colombier
575*43f728cbSDavid du Colombier memset(i, 0, sizeof(*i));
576*43f728cbSDavid du Colombier if(size < 4*6){
577*43f728cbSDavid du Colombier Tooshort:
578*43f728cbSDavid du Colombier return "firmware image too short";
579*43f728cbSDavid du Colombier }
580*43f728cbSDavid du Colombier p = data;
581*43f728cbSDavid du Colombier e = p + size;
582*43f728cbSDavid du Colombier i->version = get32(p); p += 4;
583*43f728cbSDavid du Colombier i->main.text.size = get32(p); p += 4;
584*43f728cbSDavid du Colombier i->main.data.size = get32(p); p += 4;
585*43f728cbSDavid du Colombier i->init.text.size = get32(p); p += 4;
586*43f728cbSDavid du Colombier i->init.data.size = get32(p); p += 4;
587*43f728cbSDavid du Colombier i->boot.text.size = get32(p); p += 4;
588*43f728cbSDavid du Colombier i->main.text.data = p; p += i->main.text.size;
589*43f728cbSDavid du Colombier i->main.data.data = p; p += i->main.data.size;
590*43f728cbSDavid du Colombier i->init.text.data = p; p += i->init.text.size;
591*43f728cbSDavid du Colombier i->init.data.data = p; p += i->init.data.size;
592*43f728cbSDavid du Colombier i->boot.text.data = p; p += i->boot.text.size;
593*43f728cbSDavid du Colombier if(p > e)
594*43f728cbSDavid du Colombier goto Tooshort;
595*43f728cbSDavid du Colombier return nil;
596*43f728cbSDavid du Colombier }
597*43f728cbSDavid du Colombier
598*43f728cbSDavid du Colombier static FWImage*
readfirmware(void)599*43f728cbSDavid du Colombier readfirmware(void)
600*43f728cbSDavid du Colombier {
601*43f728cbSDavid du Colombier uchar dirbuf[sizeof(Dir)+100], *data;
602*43f728cbSDavid du Colombier char *err;
603*43f728cbSDavid du Colombier FWImage *fw;
604*43f728cbSDavid du Colombier int n, r;
605*43f728cbSDavid du Colombier Chan *c;
606*43f728cbSDavid du Colombier Dir d;
607*43f728cbSDavid du Colombier
608*43f728cbSDavid du Colombier if(!iseve())
609*43f728cbSDavid du Colombier error(Eperm);
610*43f728cbSDavid du Colombier if(!waserror()){
611*43f728cbSDavid du Colombier c = namec("/boot/wpi-3945abg", Aopen, OREAD, 0);
612*43f728cbSDavid du Colombier poperror();
613*43f728cbSDavid du Colombier }else
614*43f728cbSDavid du Colombier c = namec("/lib/firmware/wpi-3945abg", Aopen, OREAD, 0);
615*43f728cbSDavid du Colombier if(waserror()){
616*43f728cbSDavid du Colombier cclose(c);
617*43f728cbSDavid du Colombier nexterror();
618*43f728cbSDavid du Colombier }
619*43f728cbSDavid du Colombier n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf);
620*43f728cbSDavid du Colombier if(n <= 0)
621*43f728cbSDavid du Colombier error("can't stat firmware");
622*43f728cbSDavid du Colombier convM2D(dirbuf, n, &d, nil);
623*43f728cbSDavid du Colombier fw = smalloc(sizeof(*fw) + 16 + d.length);
624*43f728cbSDavid du Colombier data = (uchar*)(fw+1);
625*43f728cbSDavid du Colombier if(waserror()){
626*43f728cbSDavid du Colombier free(fw);
627*43f728cbSDavid du Colombier nexterror();
628*43f728cbSDavid du Colombier }
629*43f728cbSDavid du Colombier r = 0;
630*43f728cbSDavid du Colombier while(r < d.length){
631*43f728cbSDavid du Colombier n = devtab[c->type]->read(c, data+r, d.length-r, (vlong)r);
632*43f728cbSDavid du Colombier if(n <= 0)
633*43f728cbSDavid du Colombier break;
634*43f728cbSDavid du Colombier r += n;
635*43f728cbSDavid du Colombier }
636*43f728cbSDavid du Colombier if((err = crackfw(fw, data, r)) != nil)
637*43f728cbSDavid du Colombier error(err);
638*43f728cbSDavid du Colombier poperror();
639*43f728cbSDavid du Colombier poperror();
640*43f728cbSDavid du Colombier cclose(c);
641*43f728cbSDavid du Colombier return fw;
642*43f728cbSDavid du Colombier }
643*43f728cbSDavid du Colombier
644*43f728cbSDavid du Colombier static int
gotirq(void * arg)645*43f728cbSDavid du Colombier gotirq(void *arg)
646*43f728cbSDavid du Colombier {
647*43f728cbSDavid du Colombier Ctlr *ctlr = arg;
648*43f728cbSDavid du Colombier return (ctlr->wait.m & ctlr->wait.w) != 0;
649*43f728cbSDavid du Colombier }
650*43f728cbSDavid du Colombier
651*43f728cbSDavid du Colombier static u32int
irqwait(Ctlr * ctlr,u32int mask,int timeout)652*43f728cbSDavid du Colombier irqwait(Ctlr *ctlr, u32int mask, int timeout)
653*43f728cbSDavid du Colombier {
654*43f728cbSDavid du Colombier u32int r;
655*43f728cbSDavid du Colombier
656*43f728cbSDavid du Colombier ilock(ctlr);
657*43f728cbSDavid du Colombier r = ctlr->wait.m & mask;
658*43f728cbSDavid du Colombier if(r == 0){
659*43f728cbSDavid du Colombier ctlr->wait.w = mask;
660*43f728cbSDavid du Colombier iunlock(ctlr);
661*43f728cbSDavid du Colombier if(!waserror()){
662*43f728cbSDavid du Colombier tsleep(&ctlr->wait, gotirq, ctlr, timeout);
663*43f728cbSDavid du Colombier poperror();
664*43f728cbSDavid du Colombier }
665*43f728cbSDavid du Colombier ilock(ctlr);
666*43f728cbSDavid du Colombier ctlr->wait.w = 0;
667*43f728cbSDavid du Colombier r = ctlr->wait.m & mask;
668*43f728cbSDavid du Colombier }
669*43f728cbSDavid du Colombier ctlr->wait.m &= ~r;
670*43f728cbSDavid du Colombier iunlock(ctlr);
671*43f728cbSDavid du Colombier return r;
672*43f728cbSDavid du Colombier }
673*43f728cbSDavid du Colombier
674*43f728cbSDavid du Colombier static int
rbplant(Ctlr * ctlr,int i)675*43f728cbSDavid du Colombier rbplant(Ctlr *ctlr, int i)
676*43f728cbSDavid du Colombier {
677*43f728cbSDavid du Colombier Block *b;
678*43f728cbSDavid du Colombier
679*43f728cbSDavid du Colombier b = iallocb(Rbufsize+127);
680*43f728cbSDavid du Colombier if(b == nil)
681*43f728cbSDavid du Colombier return -1;
682*43f728cbSDavid du Colombier b->rp = b->wp = (uchar*)((((uintptr)b->base+127)&~127));
683*43f728cbSDavid du Colombier memset(b->rp, 0, Rdscsize);
684*43f728cbSDavid du Colombier coherence();
685*43f728cbSDavid du Colombier ctlr->rx.b[i] = b;
686*43f728cbSDavid du Colombier ctlr->rx.p[i] = PCIWADDR(b->rp);
687*43f728cbSDavid du Colombier return 0;
688*43f728cbSDavid du Colombier }
689*43f728cbSDavid du Colombier
690*43f728cbSDavid du Colombier static char*
initring(Ctlr * ctlr)691*43f728cbSDavid du Colombier initring(Ctlr *ctlr)
692*43f728cbSDavid du Colombier {
693*43f728cbSDavid du Colombier RXQ *rx;
694*43f728cbSDavid du Colombier TXQ *tx;
695*43f728cbSDavid du Colombier int i, q;
696*43f728cbSDavid du Colombier
697*43f728cbSDavid du Colombier rx = &ctlr->rx;
698*43f728cbSDavid du Colombier if(rx->b == nil)
699*43f728cbSDavid du Colombier rx->b = malloc(sizeof(Block*) * Nrx);
700*43f728cbSDavid du Colombier if(rx->p == nil)
701*43f728cbSDavid du Colombier rx->p = mallocalign(sizeof(u32int) * Nrx, 16 * 1024, 0, 0);
702*43f728cbSDavid du Colombier if(rx->b == nil || rx->p == nil)
703*43f728cbSDavid du Colombier return "no memory for rx ring";
704*43f728cbSDavid du Colombier for(i = 0; i<Nrx; i++){
705*43f728cbSDavid du Colombier rx->p[i] = 0;
706*43f728cbSDavid du Colombier if(rx->b[i] != nil){
707*43f728cbSDavid du Colombier freeb(rx->b[i]);
708*43f728cbSDavid du Colombier rx->b[i] = nil;
709*43f728cbSDavid du Colombier }
710*43f728cbSDavid du Colombier if(rbplant(ctlr, i) < 0)
711*43f728cbSDavid du Colombier return "no memory for rx descriptors";
712*43f728cbSDavid du Colombier }
713*43f728cbSDavid du Colombier rx->i = 0;
714*43f728cbSDavid du Colombier
715*43f728cbSDavid du Colombier if(ctlr->shared == nil)
716*43f728cbSDavid du Colombier ctlr->shared = mallocalign(4096, 4096, 0, 0);
717*43f728cbSDavid du Colombier if(ctlr->shared == nil)
718*43f728cbSDavid du Colombier return "no memory for shared buffer";
719*43f728cbSDavid du Colombier memset(ctlr->shared, 0, 4096);
720*43f728cbSDavid du Colombier
721*43f728cbSDavid du Colombier for(q=0; q<nelem(ctlr->tx); q++){
722*43f728cbSDavid du Colombier tx = &ctlr->tx[q];
723*43f728cbSDavid du Colombier if(tx->b == nil)
724*43f728cbSDavid du Colombier tx->b = malloc(sizeof(Block*) * Ntx);
725*43f728cbSDavid du Colombier if(tx->d == nil)
726*43f728cbSDavid du Colombier tx->d = mallocalign(Tdscsize * Ntx, 16 * 1024, 0, 0);
727*43f728cbSDavid du Colombier if(tx->c == nil)
728*43f728cbSDavid du Colombier tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0);
729*43f728cbSDavid du Colombier if(tx->b == nil || tx->d == nil || tx->c == nil)
730*43f728cbSDavid du Colombier return "no memory for tx ring";
731*43f728cbSDavid du Colombier memset(tx->d, 0, Tdscsize * Ntx);
732*43f728cbSDavid du Colombier memset(tx->c, 0, Tcmdsize * Ntx);
733*43f728cbSDavid du Colombier for(i=0; i<Ntx; i++){
734*43f728cbSDavid du Colombier if(tx->b[i] != nil){
735*43f728cbSDavid du Colombier freeb(tx->b[i]);
736*43f728cbSDavid du Colombier tx->b[i] = nil;
737*43f728cbSDavid du Colombier }
738*43f728cbSDavid du Colombier }
739*43f728cbSDavid du Colombier ctlr->shared->txbase[q] = PCIWADDR(tx->d);
740*43f728cbSDavid du Colombier tx->i = 0;
741*43f728cbSDavid du Colombier tx->n = 0;
742*43f728cbSDavid du Colombier tx->lastcmd = 0;
743*43f728cbSDavid du Colombier }
744*43f728cbSDavid du Colombier return nil;
745*43f728cbSDavid du Colombier }
746*43f728cbSDavid du Colombier
747*43f728cbSDavid du Colombier static char*
reset(Ctlr * ctlr)748*43f728cbSDavid du Colombier reset(Ctlr *ctlr)
749*43f728cbSDavid du Colombier {
750*43f728cbSDavid du Colombier uchar rev;
751*43f728cbSDavid du Colombier char *err;
752*43f728cbSDavid du Colombier int i;
753*43f728cbSDavid du Colombier
754*43f728cbSDavid du Colombier if(ctlr->power)
755*43f728cbSDavid du Colombier poweroff(ctlr);
756*43f728cbSDavid du Colombier if((err = initring(ctlr)) != nil)
757*43f728cbSDavid du Colombier return err;
758*43f728cbSDavid du Colombier if((err = poweron(ctlr)) != nil)
759*43f728cbSDavid du Colombier return err;
760*43f728cbSDavid du Colombier
761*43f728cbSDavid du Colombier /* Select VMAIN power source. */
762*43f728cbSDavid du Colombier if((err = niclock(ctlr)) != nil)
763*43f728cbSDavid du Colombier return err;
764*43f728cbSDavid du Colombier prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain);
765*43f728cbSDavid du Colombier nicunlock(ctlr);
766*43f728cbSDavid du Colombier /* Spin until VMAIN gets selected. */
767*43f728cbSDavid du Colombier for(i = 0; i < 5000; i++){
768*43f728cbSDavid du Colombier if(csr32r(ctlr, GpioIn) & (1 << 9))
769*43f728cbSDavid du Colombier break;
770*43f728cbSDavid du Colombier delay(10);
771*43f728cbSDavid du Colombier }
772*43f728cbSDavid du Colombier
773*43f728cbSDavid du Colombier /* Perform adapter initialization. */
774*43f728cbSDavid du Colombier rev = ctlr->pdev->rid;
775*43f728cbSDavid du Colombier if((rev & 0xc0) == 0x40)
776*43f728cbSDavid du Colombier csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | AlmMb);
777*43f728cbSDavid du Colombier else if(!(rev & 0x80))
778*43f728cbSDavid du Colombier csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | AlmMm);
779*43f728cbSDavid du Colombier
780*43f728cbSDavid du Colombier if(ctlr->eeprom.cap == 0x80)
781*43f728cbSDavid du Colombier csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | SkuMrc);
782*43f728cbSDavid du Colombier
783*43f728cbSDavid du Colombier if((ctlr->eeprom.rev & 0xf0) == 0xd0)
784*43f728cbSDavid du Colombier csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RevD);
785*43f728cbSDavid du Colombier else
786*43f728cbSDavid du Colombier csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) & ~RevD);
787*43f728cbSDavid du Colombier
788*43f728cbSDavid du Colombier if(ctlr->eeprom.type > 1)
789*43f728cbSDavid du Colombier csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | TypeB);
790*43f728cbSDavid du Colombier
791*43f728cbSDavid du Colombier /* Initialize RX ring. */
792*43f728cbSDavid du Colombier if((err = niclock(ctlr)) != nil)
793*43f728cbSDavid du Colombier return err;
794*43f728cbSDavid du Colombier
795*43f728cbSDavid du Colombier coherence();
796*43f728cbSDavid du Colombier csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p));
797*43f728cbSDavid du Colombier csr32w(ctlr, FhRxRptrAddr, PCIWADDR(&ctlr->shared->next));
798*43f728cbSDavid du Colombier csr32w(ctlr, FhRxWptr, 0);
799*43f728cbSDavid du Colombier csr32w(ctlr, FhRxConfig,
800*43f728cbSDavid du Colombier FhRxConfigDmaEna |
801*43f728cbSDavid du Colombier FhRxConfigRdrbdEna |
802*43f728cbSDavid du Colombier FhRxConfigWrstatusEna |
803*43f728cbSDavid du Colombier FhRxConfigMaxfrag |
804*43f728cbSDavid du Colombier (Nrxlog << FhRxConfigNrdbShift) |
805*43f728cbSDavid du Colombier FhRxConfigIrqDstHost |
806*43f728cbSDavid du Colombier (1 << FhRxConfigIrqRbthShift));
807*43f728cbSDavid du Colombier USED(csr32r(ctlr, FhRssrTbl));
808*43f728cbSDavid du Colombier csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7);
809*43f728cbSDavid du Colombier nicunlock(ctlr);
810*43f728cbSDavid du Colombier
811*43f728cbSDavid du Colombier /* Initialize TX rings. */
812*43f728cbSDavid du Colombier if((err = niclock(ctlr)) != nil)
813*43f728cbSDavid du Colombier return err;
814*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedMode, 2);
815*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedArastat, 1);
816*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedTxfact, 0x3f);
817*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedBP1, 0x10000);
818*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedBP2, 0x30002);
819*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedTxf4mf, 4);
820*43f728cbSDavid du Colombier prphwrite(ctlr, AlmSchedTxf5mf, 5);
821*43f728cbSDavid du Colombier csr32w(ctlr, FhTxBase, PCIWADDR(ctlr->shared));
822*43f728cbSDavid du Colombier csr32w(ctlr, FhMsgConfig, 0xffff05a5);
823*43f728cbSDavid du Colombier for(i = 0; i < 6; i++){
824*43f728cbSDavid du Colombier csr32w(ctlr, FhCbbcCtrl+i*8, 0);
825*43f728cbSDavid du Colombier csr32w(ctlr, FhCbbcBase+i*8, 0);
826*43f728cbSDavid du Colombier csr32w(ctlr, FhTxConfig+i*32, 0x80200008);
827*43f728cbSDavid du Colombier }
828*43f728cbSDavid du Colombier nicunlock(ctlr);
829*43f728cbSDavid du Colombier USED(csr32r(ctlr, FhTxBase));
830*43f728cbSDavid du Colombier
831*43f728cbSDavid du Colombier csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
832*43f728cbSDavid du Colombier csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
833*43f728cbSDavid du Colombier
834*43f728cbSDavid du Colombier ctlr->broken = 0;
835*43f728cbSDavid du Colombier ctlr->wait.m = 0;
836*43f728cbSDavid du Colombier ctlr->wait.w = 0;
837*43f728cbSDavid du Colombier
838*43f728cbSDavid du Colombier ctlr->ie = Idefmask;
839*43f728cbSDavid du Colombier csr32w(ctlr, Imr, ctlr->ie);
840*43f728cbSDavid du Colombier csr32w(ctlr, Isr, ~0);
841*43f728cbSDavid du Colombier
842*43f728cbSDavid du Colombier csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
843*43f728cbSDavid du Colombier csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
844*43f728cbSDavid du Colombier
845*43f728cbSDavid du Colombier return nil;
846*43f728cbSDavid du Colombier }
847*43f728cbSDavid du Colombier
848*43f728cbSDavid du Colombier static char*
849*43f728cbSDavid du Colombier postboot(Ctlr *);
850*43f728cbSDavid du Colombier
851*43f728cbSDavid du Colombier static char*
boot(Ctlr * ctlr)852*43f728cbSDavid du Colombier boot(Ctlr *ctlr)
853*43f728cbSDavid du Colombier {
854*43f728cbSDavid du Colombier int i, n, size;
855*43f728cbSDavid du Colombier uchar *dma, *p;
856*43f728cbSDavid du Colombier FWImage *fw;
857*43f728cbSDavid du Colombier char *err;
858*43f728cbSDavid du Colombier
859*43f728cbSDavid du Colombier fw = ctlr->fw;
860*43f728cbSDavid du Colombier /* 16 byte padding may not be necessary. */
861*43f728cbSDavid du Colombier size = ROUND(fw->init.data.size, 16) + ROUND(fw->init.text.size, 16);
862*43f728cbSDavid du Colombier dma = mallocalign(size, 16, 0, 0);
863*43f728cbSDavid du Colombier if(dma == nil)
864*43f728cbSDavid du Colombier return "no memory for dma";
865*43f728cbSDavid du Colombier
866*43f728cbSDavid du Colombier if((err = niclock(ctlr)) != nil){
867*43f728cbSDavid du Colombier free(dma);
868*43f728cbSDavid du Colombier return err;
869*43f728cbSDavid du Colombier }
870*43f728cbSDavid du Colombier
871*43f728cbSDavid du Colombier p = dma;
872*43f728cbSDavid du Colombier memmove(p, fw->init.data.data, fw->init.data.size);
873*43f728cbSDavid du Colombier coherence();
874*43f728cbSDavid du Colombier prphwrite(ctlr, BsmDramDataAddr, PCIWADDR(p));
875*43f728cbSDavid du Colombier prphwrite(ctlr, BsmDramDataSize, fw->init.data.size);
876*43f728cbSDavid du Colombier p += ROUND(fw->init.data.size, 16);
877*43f728cbSDavid du Colombier memmove(p, fw->init.text.data, fw->init.text.size);
878*43f728cbSDavid du Colombier coherence();
879*43f728cbSDavid du Colombier prphwrite(ctlr, BsmDramTextAddr, PCIWADDR(p));
880*43f728cbSDavid du Colombier prphwrite(ctlr, BsmDramTextSize, fw->init.text.size);
881*43f728cbSDavid du Colombier
882*43f728cbSDavid du Colombier nicunlock(ctlr);
883*43f728cbSDavid du Colombier if((err = niclock(ctlr)) != nil){
884*43f728cbSDavid du Colombier free(dma);
885*43f728cbSDavid du Colombier return err;
886*43f728cbSDavid du Colombier }
887*43f728cbSDavid du Colombier
888*43f728cbSDavid du Colombier /* Copy microcode image into NIC memory. */
889*43f728cbSDavid du Colombier p = fw->boot.text.data;
890*43f728cbSDavid du Colombier n = fw->boot.text.size/4;
891*43f728cbSDavid du Colombier for(i=0; i<n; i++, p += 4)
892*43f728cbSDavid du Colombier prphwrite(ctlr, BsmSramBase+i*4, get32(p));
893*43f728cbSDavid du Colombier
894*43f728cbSDavid du Colombier prphwrite(ctlr, BsmWrMemSrc, 0);
895*43f728cbSDavid du Colombier prphwrite(ctlr, BsmWrMemDst, 0);
896*43f728cbSDavid du Colombier prphwrite(ctlr, BsmWrDwCount, n);
897*43f728cbSDavid du Colombier
898*43f728cbSDavid du Colombier /* Start boot load now. */
899*43f728cbSDavid du Colombier prphwrite(ctlr, BsmWrCtrl, 1<<31);
900*43f728cbSDavid du Colombier
901*43f728cbSDavid du Colombier /* Wait for transfer to complete. */
902*43f728cbSDavid du Colombier for(i=0; i<1000; i++){
903*43f728cbSDavid du Colombier if((prphread(ctlr, BsmWrCtrl) & (1<<31)) == 0)
904*43f728cbSDavid du Colombier break;
905*43f728cbSDavid du Colombier delay(10);
906*43f728cbSDavid du Colombier }
907*43f728cbSDavid du Colombier if(i == 1000){
908*43f728cbSDavid du Colombier nicunlock(ctlr);
909*43f728cbSDavid du Colombier free(dma);
910*43f728cbSDavid du Colombier return "bootcode timeout";
911*43f728cbSDavid du Colombier }
912*43f728cbSDavid du Colombier
913*43f728cbSDavid du Colombier /* Enable boot after power up. */
914*43f728cbSDavid du Colombier prphwrite(ctlr, BsmWrCtrl, 1<<30);
915*43f728cbSDavid du Colombier nicunlock(ctlr);
916*43f728cbSDavid du Colombier
917*43f728cbSDavid du Colombier /* Now press "execute". */
918*43f728cbSDavid du Colombier csr32w(ctlr, Reset, 0);
919*43f728cbSDavid du Colombier
920*43f728cbSDavid du Colombier /* Wait at most one second for first alive notification. */
921*43f728cbSDavid du Colombier if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){
922*43f728cbSDavid du Colombier free(dma);
923*43f728cbSDavid du Colombier return "init firmware boot failed";
924*43f728cbSDavid du Colombier }
925*43f728cbSDavid du Colombier free(dma);
926*43f728cbSDavid du Colombier
927*43f728cbSDavid du Colombier size = ROUND(fw->main.data.size, 16) + ROUND(fw->main.text.size, 16);
928*43f728cbSDavid du Colombier dma = mallocalign(size, 16, 0, 0);
929*43f728cbSDavid du Colombier if(dma == nil)
930*43f728cbSDavid du Colombier return "no memory for dma";
931*43f728cbSDavid du Colombier if((err = niclock(ctlr)) != nil){
932*43f728cbSDavid du Colombier free(dma);
933*43f728cbSDavid du Colombier return err;
934*43f728cbSDavid du Colombier }
935*43f728cbSDavid du Colombier p = dma;
936*43f728cbSDavid du Colombier memmove(p, fw->main.data.data, fw->main.data.size);
937*43f728cbSDavid du Colombier coherence();
938*43f728cbSDavid du Colombier prphwrite(ctlr, BsmDramDataAddr, PCIWADDR(p));
939*43f728cbSDavid du Colombier prphwrite(ctlr, BsmDramDataSize, fw->main.data.size);
940*43f728cbSDavid du Colombier p += ROUND(fw->main.data.size, 16);
941*43f728cbSDavid du Colombier memmove(p, fw->main.text.data, fw->main.text.size);
942*43f728cbSDavid du Colombier coherence();
943*43f728cbSDavid du Colombier prphwrite(ctlr, BsmDramTextAddr, PCIWADDR(p));
944*43f728cbSDavid du Colombier prphwrite(ctlr, BsmDramTextSize, fw->main.text.size | (1<<31));
945*43f728cbSDavid du Colombier nicunlock(ctlr);
946*43f728cbSDavid du Colombier
947*43f728cbSDavid du Colombier if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){
948*43f728cbSDavid du Colombier free(dma);
949*43f728cbSDavid du Colombier return "main firmware boot failed";
950*43f728cbSDavid du Colombier }
951*43f728cbSDavid du Colombier free(dma);
952*43f728cbSDavid du Colombier return postboot(ctlr);
953*43f728cbSDavid du Colombier }
954*43f728cbSDavid du Colombier
955*43f728cbSDavid du Colombier static int
txqready(void * arg)956*43f728cbSDavid du Colombier txqready(void *arg)
957*43f728cbSDavid du Colombier {
958*43f728cbSDavid du Colombier TXQ *q = arg;
959*43f728cbSDavid du Colombier return q->n < Ntx;
960*43f728cbSDavid du Colombier }
961*43f728cbSDavid du Colombier
962*43f728cbSDavid du Colombier static char*
qcmd(Ctlr * ctlr,uint qid,uint code,uchar * data,int size,Block * block)963*43f728cbSDavid du Colombier qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
964*43f728cbSDavid du Colombier {
965*43f728cbSDavid du Colombier uchar *d, *c;
966*43f728cbSDavid du Colombier int pad;
967*43f728cbSDavid du Colombier TXQ *q;
968*43f728cbSDavid du Colombier
969*43f728cbSDavid du Colombier assert(qid < nelem(ctlr->tx));
970*43f728cbSDavid du Colombier assert(size <= Tcmdsize-4);
971*43f728cbSDavid du Colombier
972*43f728cbSDavid du Colombier ilock(ctlr);
973*43f728cbSDavid du Colombier q = &ctlr->tx[qid];
974*43f728cbSDavid du Colombier while(q->n >= Ntx && !ctlr->broken){
975*43f728cbSDavid du Colombier iunlock(ctlr);
976*43f728cbSDavid du Colombier qlock(q);
977*43f728cbSDavid du Colombier if(!waserror()){
978*43f728cbSDavid du Colombier tsleep(q, txqready, q, 10);
979*43f728cbSDavid du Colombier poperror();
980*43f728cbSDavid du Colombier }
981*43f728cbSDavid du Colombier qunlock(q);
982*43f728cbSDavid du Colombier ilock(ctlr);
983*43f728cbSDavid du Colombier }
984*43f728cbSDavid du Colombier if(ctlr->broken){
985*43f728cbSDavid du Colombier iunlock(ctlr);
986*43f728cbSDavid du Colombier return "qcmd: broken";
987*43f728cbSDavid du Colombier }
988*43f728cbSDavid du Colombier q->n++;
989*43f728cbSDavid du Colombier
990*43f728cbSDavid du Colombier q->lastcmd = code;
991*43f728cbSDavid du Colombier q->b[q->i] = block;
992*43f728cbSDavid du Colombier c = q->c + q->i * Tcmdsize;
993*43f728cbSDavid du Colombier d = q->d + q->i * Tdscsize;
994*43f728cbSDavid du Colombier
995*43f728cbSDavid du Colombier /* build command */
996*43f728cbSDavid du Colombier c[0] = code;
997*43f728cbSDavid du Colombier c[1] = 0; /* flags */
998*43f728cbSDavid du Colombier c[2] = q->i;
999*43f728cbSDavid du Colombier c[3] = qid;
1000*43f728cbSDavid du Colombier
1001*43f728cbSDavid du Colombier if(size > 0)
1002*43f728cbSDavid du Colombier memmove(c+4, data, size);
1003*43f728cbSDavid du Colombier size += 4;
1004*43f728cbSDavid du Colombier
1005*43f728cbSDavid du Colombier memset(d, 0, Tdscsize);
1006*43f728cbSDavid du Colombier
1007*43f728cbSDavid du Colombier pad = size - 4;
1008*43f728cbSDavid du Colombier if(block != nil)
1009*43f728cbSDavid du Colombier pad += BLEN(block);
1010*43f728cbSDavid du Colombier pad = ((pad + 3) & ~3) - pad;
1011*43f728cbSDavid du Colombier
1012*43f728cbSDavid du Colombier put32(d, (pad << 28) | ((1 + (block != nil)) << 24)), d += 4;
1013*43f728cbSDavid du Colombier put32(d, PCIWADDR(c)), d += 4;
1014*43f728cbSDavid du Colombier put32(d, size), d += 4;
1015*43f728cbSDavid du Colombier
1016*43f728cbSDavid du Colombier if(block != nil){
1017*43f728cbSDavid du Colombier size = BLEN(block);
1018*43f728cbSDavid du Colombier put32(d, PCIWADDR(block->rp)), d += 4;
1019*43f728cbSDavid du Colombier put32(d, size), d += 4;
1020*43f728cbSDavid du Colombier }
1021*43f728cbSDavid du Colombier
1022*43f728cbSDavid du Colombier USED(d);
1023*43f728cbSDavid du Colombier
1024*43f728cbSDavid du Colombier coherence();
1025*43f728cbSDavid du Colombier
1026*43f728cbSDavid du Colombier q->i = (q->i+1) % Ntx;
1027*43f728cbSDavid du Colombier csr32w(ctlr, HbusTargWptr, (qid<<8) | q->i);
1028*43f728cbSDavid du Colombier
1029*43f728cbSDavid du Colombier iunlock(ctlr);
1030*43f728cbSDavid du Colombier
1031*43f728cbSDavid du Colombier return nil;
1032*43f728cbSDavid du Colombier }
1033*43f728cbSDavid du Colombier
1034*43f728cbSDavid du Colombier static int
txqempty(void * arg)1035*43f728cbSDavid du Colombier txqempty(void *arg)
1036*43f728cbSDavid du Colombier {
1037*43f728cbSDavid du Colombier TXQ *q = arg;
1038*43f728cbSDavid du Colombier return q->n == 0;
1039*43f728cbSDavid du Colombier }
1040*43f728cbSDavid du Colombier
1041*43f728cbSDavid du Colombier static char*
flushq(Ctlr * ctlr,uint qid)1042*43f728cbSDavid du Colombier flushq(Ctlr *ctlr, uint qid)
1043*43f728cbSDavid du Colombier {
1044*43f728cbSDavid du Colombier TXQ *q;
1045*43f728cbSDavid du Colombier int i;
1046*43f728cbSDavid du Colombier
1047*43f728cbSDavid du Colombier q = &ctlr->tx[qid];
1048*43f728cbSDavid du Colombier qlock(q);
1049*43f728cbSDavid du Colombier for(i = 0; i < 200 && !ctlr->broken; i++){
1050*43f728cbSDavid du Colombier if(txqempty(q)){
1051*43f728cbSDavid du Colombier qunlock(q);
1052*43f728cbSDavid du Colombier return nil;
1053*43f728cbSDavid du Colombier }
1054*43f728cbSDavid du Colombier if(islo() && !waserror()){
1055*43f728cbSDavid du Colombier tsleep(q, txqempty, q, 10);
1056*43f728cbSDavid du Colombier poperror();
1057*43f728cbSDavid du Colombier }
1058*43f728cbSDavid du Colombier }
1059*43f728cbSDavid du Colombier qunlock(q);
1060*43f728cbSDavid du Colombier if(ctlr->broken)
1061*43f728cbSDavid du Colombier return "flushq: broken";
1062*43f728cbSDavid du Colombier return "flushq: timeout";
1063*43f728cbSDavid du Colombier }
1064*43f728cbSDavid du Colombier
1065*43f728cbSDavid du Colombier static char*
cmd(Ctlr * ctlr,uint code,uchar * data,int size)1066*43f728cbSDavid du Colombier cmd(Ctlr *ctlr, uint code, uchar *data, int size)
1067*43f728cbSDavid du Colombier {
1068*43f728cbSDavid du Colombier char *err;
1069*43f728cbSDavid du Colombier
1070*43f728cbSDavid du Colombier if((err = qcmd(ctlr, 4, code, data, size, nil)) != nil)
1071*43f728cbSDavid du Colombier return err;
1072*43f728cbSDavid du Colombier return flushq(ctlr, 4);
1073*43f728cbSDavid du Colombier }
1074*43f728cbSDavid du Colombier
1075*43f728cbSDavid du Colombier static void
setled(Ctlr * ctlr,int which,int on,int off)1076*43f728cbSDavid du Colombier setled(Ctlr *ctlr, int which, int on, int off)
1077*43f728cbSDavid du Colombier {
1078*43f728cbSDavid du Colombier uchar c[8];
1079*43f728cbSDavid du Colombier
1080*43f728cbSDavid du Colombier memset(c, 0, sizeof(c));
1081*43f728cbSDavid du Colombier put32(c, 10000);
1082*43f728cbSDavid du Colombier c[4] = which;
1083*43f728cbSDavid du Colombier c[5] = on;
1084*43f728cbSDavid du Colombier c[6] = off;
1085*43f728cbSDavid du Colombier cmd(ctlr, 72, c, sizeof(c));
1086*43f728cbSDavid du Colombier }
1087*43f728cbSDavid du Colombier
1088*43f728cbSDavid du Colombier static char*
btcoex(Ctlr * ctlr)1089*43f728cbSDavid du Colombier btcoex(Ctlr *ctlr)
1090*43f728cbSDavid du Colombier {
1091*43f728cbSDavid du Colombier uchar c[Tcmdsize], *p;
1092*43f728cbSDavid du Colombier
1093*43f728cbSDavid du Colombier /* configure bluetooth coexistance. */
1094*43f728cbSDavid du Colombier p = c;
1095*43f728cbSDavid du Colombier *p++ = 3; /* flags WPI_BT_COEX_MODE_4WIRE */
1096*43f728cbSDavid du Colombier *p++ = 30; /* lead time */
1097*43f728cbSDavid du Colombier *p++ = 5; /* max kill */
1098*43f728cbSDavid du Colombier *p++ = 0; /* reserved */
1099*43f728cbSDavid du Colombier put32(p, 0), p += 4; /* kill_ack */
1100*43f728cbSDavid du Colombier put32(p, 0), p += 4; /* kill_cts */
1101*43f728cbSDavid du Colombier return cmd(ctlr, 155, c, p-c);
1102*43f728cbSDavid du Colombier }
1103*43f728cbSDavid du Colombier
1104*43f728cbSDavid du Colombier static char*
powermode(Ctlr * ctlr)1105*43f728cbSDavid du Colombier powermode(Ctlr *ctlr)
1106*43f728cbSDavid du Colombier {
1107*43f728cbSDavid du Colombier uchar c[Tcmdsize];
1108*43f728cbSDavid du Colombier int capoff, reg;
1109*43f728cbSDavid du Colombier
1110*43f728cbSDavid du Colombier memset(c, 0, sizeof(c));
1111*43f728cbSDavid du Colombier capoff = pcicap(ctlr->pdev, PciCapPCIe);
1112*43f728cbSDavid du Colombier if(capoff >= 0){
1113*43f728cbSDavid du Colombier reg = pcicfgr8(ctlr->pdev, capoff+1);
1114*43f728cbSDavid du Colombier if((reg & 1) == 0) /* PCI_PCIE_LCR_ASPM_L0S */
1115*43f728cbSDavid du Colombier c[0] |= 1<<3; /* WPI_PS_PCI_PMGT */
1116*43f728cbSDavid du Colombier }
1117*43f728cbSDavid du Colombier return cmd(ctlr, 119, c, 4*(3+5));
1118*43f728cbSDavid du Colombier }
1119*43f728cbSDavid du Colombier
1120*43f728cbSDavid du Colombier static char*
postboot(Ctlr * ctlr)1121*43f728cbSDavid du Colombier postboot(Ctlr *ctlr)
1122*43f728cbSDavid du Colombier {
1123*43f728cbSDavid du Colombier while((ctlr->temp = (int)csr32r(ctlr, UcodeGp2)) == 0)
1124*43f728cbSDavid du Colombier delay(10);
1125*43f728cbSDavid du Colombier
1126*43f728cbSDavid du Colombier if(0){
1127*43f728cbSDavid du Colombier char *err;
1128*43f728cbSDavid du Colombier
1129*43f728cbSDavid du Colombier if((err = btcoex(ctlr)) != nil)
1130*43f728cbSDavid du Colombier print("btcoex: %s\n", err);
1131*43f728cbSDavid du Colombier if((err = powermode(ctlr)) != nil)
1132*43f728cbSDavid du Colombier print("powermode: %s\n", err);
1133*43f728cbSDavid du Colombier }
1134*43f728cbSDavid du Colombier
1135*43f728cbSDavid du Colombier return nil;
1136*43f728cbSDavid du Colombier }
1137*43f728cbSDavid du Colombier
1138*43f728cbSDavid du Colombier static uchar wpirates[] = {
1139*43f728cbSDavid du Colombier 0x80 | 12,
1140*43f728cbSDavid du Colombier 0x80 | 18,
1141*43f728cbSDavid du Colombier 0x80 | 24,
1142*43f728cbSDavid du Colombier 0x80 | 36,
1143*43f728cbSDavid du Colombier 0x80 | 48,
1144*43f728cbSDavid du Colombier 0x80 | 72,
1145*43f728cbSDavid du Colombier 0x80 | 96,
1146*43f728cbSDavid du Colombier 0x80 | 108,
1147*43f728cbSDavid du Colombier 0x80 | 2,
1148*43f728cbSDavid du Colombier 0x80 | 4,
1149*43f728cbSDavid du Colombier 0x80 | 11,
1150*43f728cbSDavid du Colombier 0x80 | 22,
1151*43f728cbSDavid du Colombier
1152*43f728cbSDavid du Colombier 0
1153*43f728cbSDavid du Colombier };
1154*43f728cbSDavid du Colombier
1155*43f728cbSDavid du Colombier static struct {
1156*43f728cbSDavid du Colombier uchar rate;
1157*43f728cbSDavid du Colombier uchar plcp;
1158*43f728cbSDavid du Colombier } ratetab[] = {
1159*43f728cbSDavid du Colombier { 12, 0xd },
1160*43f728cbSDavid du Colombier { 18, 0xf },
1161*43f728cbSDavid du Colombier { 24, 0x5 },
1162*43f728cbSDavid du Colombier { 36, 0x7 },
1163*43f728cbSDavid du Colombier { 48, 0x9 },
1164*43f728cbSDavid du Colombier { 72, 0xb },
1165*43f728cbSDavid du Colombier { 96, 0x1 },
1166*43f728cbSDavid du Colombier { 108, 0x3 },
1167*43f728cbSDavid du Colombier { 2, 10 },
1168*43f728cbSDavid du Colombier { 4, 20 },
1169*43f728cbSDavid du Colombier { 11, 55 },
1170*43f728cbSDavid du Colombier { 22, 110 },
1171*43f728cbSDavid du Colombier };
1172*43f728cbSDavid du Colombier
1173*43f728cbSDavid du Colombier static u8int rfgain_2ghz[] = {
1174*43f728cbSDavid du Colombier 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xbb, 0xbb,
1175*43f728cbSDavid du Colombier 0xbb, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xd3, 0xd3, 0xb3, 0xb3, 0xb3,
1176*43f728cbSDavid du Colombier 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x73, 0xeb, 0xeb, 0xeb,
1177*43f728cbSDavid du Colombier 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xab, 0xab, 0xab, 0x8b,
1178*43f728cbSDavid du Colombier 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xc3, 0xc3, 0xc3, 0xc3, 0xa3,
1179*43f728cbSDavid du Colombier 0xa3, 0xa3, 0xa3, 0x83, 0x83, 0x83, 0x83, 0x63, 0x63, 0x63, 0x63,
1180*43f728cbSDavid du Colombier 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, 0x03, 0x03, 0x03,
1181*43f728cbSDavid du Colombier 0x03
1182*43f728cbSDavid du Colombier };
1183*43f728cbSDavid du Colombier
1184*43f728cbSDavid du Colombier static u8int dspgain_2ghz[] = {
1185*43f728cbSDavid du Colombier 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x6e, 0x69, 0x62, 0x7d, 0x73, 0x6c,
1186*43f728cbSDavid du Colombier 0x63, 0x77, 0x6f, 0x69, 0x61, 0x5c, 0x6a, 0x64, 0x78, 0x71, 0x6b,
1187*43f728cbSDavid du Colombier 0x7d, 0x77, 0x70, 0x6a, 0x65, 0x61, 0x5b, 0x6b, 0x79, 0x73, 0x6d,
1188*43f728cbSDavid du Colombier 0x7f, 0x79, 0x73, 0x6c, 0x66, 0x60, 0x5c, 0x6e, 0x68, 0x62, 0x74,
1189*43f728cbSDavid du Colombier 0x7d, 0x77, 0x71, 0x6b, 0x65, 0x60, 0x71, 0x6a, 0x66, 0x5f, 0x71,
1190*43f728cbSDavid du Colombier 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f,
1191*43f728cbSDavid du Colombier 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66,
1192*43f728cbSDavid du Colombier 0x5f
1193*43f728cbSDavid du Colombier };
1194*43f728cbSDavid du Colombier
1195*43f728cbSDavid du Colombier static int
pwridx(Ctlr * ctlr,Powergrp * pwgr,int chan,int rate)1196*43f728cbSDavid du Colombier pwridx(Ctlr *ctlr, Powergrp *pwgr, int chan, int rate)
1197*43f728cbSDavid du Colombier {
1198*43f728cbSDavid du Colombier /* Fixed-point arithmetic division using a n-bit fractional part. */
1199*43f728cbSDavid du Colombier #define fdivround(a, b, n) \
1200*43f728cbSDavid du Colombier ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n))
1201*43f728cbSDavid du Colombier
1202*43f728cbSDavid du Colombier /* Linear interpolation. */
1203*43f728cbSDavid du Colombier #define interpolate(x, x1, y1, x2, y2, n) \
1204*43f728cbSDavid du Colombier ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n))
1205*43f728cbSDavid du Colombier
1206*43f728cbSDavid du Colombier int pwr;
1207*43f728cbSDavid du Colombier Sample *sample;
1208*43f728cbSDavid du Colombier int idx;
1209*43f728cbSDavid du Colombier
1210*43f728cbSDavid du Colombier /* Default TX power is group maximum TX power minus 3dB. */
1211*43f728cbSDavid du Colombier pwr = pwgr->maxpwr / 2;
1212*43f728cbSDavid du Colombier
1213*43f728cbSDavid du Colombier /* Decrease TX power for highest OFDM rates to reduce distortion. */
1214*43f728cbSDavid du Colombier switch(rate){
1215*43f728cbSDavid du Colombier case 5: /* WPI_RIDX_OFDM36 */
1216*43f728cbSDavid du Colombier pwr -= 0;
1217*43f728cbSDavid du Colombier break;
1218*43f728cbSDavid du Colombier case 6: /* WPI_RIDX_OFDM48 */
1219*43f728cbSDavid du Colombier pwr -=7;
1220*43f728cbSDavid du Colombier break;
1221*43f728cbSDavid du Colombier case 7: /* WPI_RIDX_OFDM54 */
1222*43f728cbSDavid du Colombier pwr -= 9;
1223*43f728cbSDavid du Colombier break;
1224*43f728cbSDavid du Colombier }
1225*43f728cbSDavid du Colombier
1226*43f728cbSDavid du Colombier /* Never exceed the channel maximum allowed TX power. */
1227*43f728cbSDavid du Colombier pwr = MIN(pwr, ctlr->maxpwr[chan]);
1228*43f728cbSDavid du Colombier
1229*43f728cbSDavid du Colombier /* Retrieve TX power index into gain tables from samples. */
1230*43f728cbSDavid du Colombier for(sample = pwgr->samples; sample < &pwgr->samples[3]; sample++)
1231*43f728cbSDavid du Colombier if(pwr > sample[1].power)
1232*43f728cbSDavid du Colombier break;
1233*43f728cbSDavid du Colombier /* Fixed-point linear interpolation using a 19-bit fractional part. */
1234*43f728cbSDavid du Colombier idx = interpolate(pwr, sample[0].power, sample[0].index,
1235*43f728cbSDavid du Colombier sample[1].power, sample[1].index, 19);
1236*43f728cbSDavid du Colombier
1237*43f728cbSDavid du Colombier /*-
1238*43f728cbSDavid du Colombier * Adjust power index based on current temperature:
1239*43f728cbSDavid du Colombier * - if cooler than factory-calibrated: decrease output power
1240*43f728cbSDavid du Colombier * - if warmer than factory-calibrated: increase output power
1241*43f728cbSDavid du Colombier */
1242*43f728cbSDavid du Colombier idx -= (ctlr->temp - pwgr->temp) * 11 / 100;
1243*43f728cbSDavid du Colombier
1244*43f728cbSDavid du Colombier /* Decrease TX power for CCK rates (-5dB). */
1245*43f728cbSDavid du Colombier if (rate >= 8)
1246*43f728cbSDavid du Colombier idx += 10;
1247*43f728cbSDavid du Colombier
1248*43f728cbSDavid du Colombier /* Make sure idx stays in a valid range. */
1249*43f728cbSDavid du Colombier if (idx < 0)
1250*43f728cbSDavid du Colombier idx = 0;
1251*43f728cbSDavid du Colombier else if (idx >= nelem(rfgain_2ghz))
1252*43f728cbSDavid du Colombier idx = nelem(rfgain_2ghz)-1;
1253*43f728cbSDavid du Colombier return idx;
1254*43f728cbSDavid du Colombier #undef fdivround
1255*43f728cbSDavid du Colombier #undef interpolate
1256*43f728cbSDavid du Colombier }
1257*43f728cbSDavid du Colombier
1258*43f728cbSDavid du Colombier static void
addnode(Ctlr * ctlr,uchar id,uchar * addr,int plcp,int antenna)1259*43f728cbSDavid du Colombier addnode(Ctlr *ctlr, uchar id, uchar *addr, int plcp, int antenna)
1260*43f728cbSDavid du Colombier {
1261*43f728cbSDavid du Colombier uchar c[Tcmdsize], *p;
1262*43f728cbSDavid du Colombier
1263*43f728cbSDavid du Colombier memset(p = c, 0, sizeof(c));
1264*43f728cbSDavid du Colombier *p++ = 0; /* control (1 = update) */
1265*43f728cbSDavid du Colombier p += 3; /* reserved */
1266*43f728cbSDavid du Colombier memmove(p, addr, 6);
1267*43f728cbSDavid du Colombier p += 6;
1268*43f728cbSDavid du Colombier p += 2; /* reserved */
1269*43f728cbSDavid du Colombier *p++ = id; /* node id */
1270*43f728cbSDavid du Colombier p++; /* flags */
1271*43f728cbSDavid du Colombier p += 2; /* reserved */
1272*43f728cbSDavid du Colombier p += 2; /* kflags */
1273*43f728cbSDavid du Colombier p++; /* tcs2 */
1274*43f728cbSDavid du Colombier p++; /* reserved */
1275*43f728cbSDavid du Colombier p += 5*2; /* ttak */
1276*43f728cbSDavid du Colombier p += 2; /* reserved */
1277*43f728cbSDavid du Colombier p += 16; /* key */
1278*43f728cbSDavid du Colombier put32(p, 4); /* action (4 = set_rate) */
1279*43f728cbSDavid du Colombier p += 4;
1280*43f728cbSDavid du Colombier p += 4; /* mask */
1281*43f728cbSDavid du Colombier p += 2; /* tid */
1282*43f728cbSDavid du Colombier *p++ = plcp; /* plcp */
1283*43f728cbSDavid du Colombier *p++ = antenna; /* antenna */
1284*43f728cbSDavid du Colombier p++; /* add_imm */
1285*43f728cbSDavid du Colombier p++; /* del_imm */
1286*43f728cbSDavid du Colombier p++; /* add_imm_start */
1287*43f728cbSDavid du Colombier cmd(ctlr, 24, c, p - c);
1288*43f728cbSDavid du Colombier }
1289*43f728cbSDavid du Colombier
1290*43f728cbSDavid du Colombier static void
rxon(Ether * edev,Wnode * bss)1291*43f728cbSDavid du Colombier rxon(Ether *edev, Wnode *bss)
1292*43f728cbSDavid du Colombier {
1293*43f728cbSDavid du Colombier uchar c[Tcmdsize], *p;
1294*43f728cbSDavid du Colombier int filter, flags, rate;
1295*43f728cbSDavid du Colombier Ctlr *ctlr;
1296*43f728cbSDavid du Colombier char *err;
1297*43f728cbSDavid du Colombier int idx;
1298*43f728cbSDavid du Colombier
1299*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1300*43f728cbSDavid du Colombier filter = FilterNoDecrypt | FilterMulticast;
1301*43f728cbSDavid du Colombier if(ctlr->prom){
1302*43f728cbSDavid du Colombier filter |= FilterPromisc;
1303*43f728cbSDavid du Colombier if(bss != nil)
1304*43f728cbSDavid du Colombier ctlr->channel = bss->channel;
1305*43f728cbSDavid du Colombier bss = nil;
1306*43f728cbSDavid du Colombier }
1307*43f728cbSDavid du Colombier flags = RFlagTSF | RFlag24Ghz | RFlagAuto;
1308*43f728cbSDavid du Colombier if(bss != nil){
1309*43f728cbSDavid du Colombier if(bss->cap & (1<<5))
1310*43f728cbSDavid du Colombier flags |= RFlagShPreamble;
1311*43f728cbSDavid du Colombier if(bss->cap & (1<<10))
1312*43f728cbSDavid du Colombier flags |= RFlagShSlot;
1313*43f728cbSDavid du Colombier ctlr->channel = bss->channel;
1314*43f728cbSDavid du Colombier memmove(ctlr->bssid, bss->bssid, Eaddrlen);
1315*43f728cbSDavid du Colombier ctlr->aid = bss->aid;
1316*43f728cbSDavid du Colombier if(ctlr->aid != 0){
1317*43f728cbSDavid du Colombier filter |= FilterBSS;
1318*43f728cbSDavid du Colombier ctlr->bssnodeid = -1;
1319*43f728cbSDavid du Colombier }else
1320*43f728cbSDavid du Colombier ctlr->bcastnodeid = -1;
1321*43f728cbSDavid du Colombier }else{
1322*43f728cbSDavid du Colombier memmove(ctlr->bssid, edev->bcast, Eaddrlen);
1323*43f728cbSDavid du Colombier ctlr->aid = 0;
1324*43f728cbSDavid du Colombier ctlr->bcastnodeid = -1;
1325*43f728cbSDavid du Colombier ctlr->bssnodeid = -1;
1326*43f728cbSDavid du Colombier }
1327*43f728cbSDavid du Colombier
1328*43f728cbSDavid du Colombier if(ctlr->aid != 0)
1329*43f728cbSDavid du Colombier setled(ctlr, 2, 0, 1); /* on when associated */
1330*43f728cbSDavid du Colombier else if(memcmp(ctlr->bssid, edev->bcast, Eaddrlen) != 0)
1331*43f728cbSDavid du Colombier setled(ctlr, 2, 10, 10); /* slow blink when connecting */
1332*43f728cbSDavid du Colombier else
1333*43f728cbSDavid du Colombier setled(ctlr, 2, 5, 5); /* fast blink when scanning */
1334*43f728cbSDavid du Colombier
1335*43f728cbSDavid du Colombier memset(p = c, 0, sizeof(c));
1336*43f728cbSDavid du Colombier memmove(p, edev->ea, 6); p += 8; /* myaddr */
1337*43f728cbSDavid du Colombier memmove(p, ctlr->bssid, 6); p += 16; /* bssid */
1338*43f728cbSDavid du Colombier *p++ = 3; /* mode (STA) */
1339*43f728cbSDavid du Colombier p += 3;
1340*43f728cbSDavid du Colombier *p++ = 0xff; /* ofdm mask (not yet negotiated) */
1341*43f728cbSDavid du Colombier *p++ = 0x0f; /* cck mask (not yet negotiated) */
1342*43f728cbSDavid du Colombier put16(p, ctlr->aid & 0x3fff); /* associd */
1343*43f728cbSDavid du Colombier p += 2;
1344*43f728cbSDavid du Colombier put32(p, flags);
1345*43f728cbSDavid du Colombier p += 4;
1346*43f728cbSDavid du Colombier put32(p, filter);
1347*43f728cbSDavid du Colombier p += 4;
1348*43f728cbSDavid du Colombier *p++ = ctlr->channel;
1349*43f728cbSDavid du Colombier p += 3;
1350*43f728cbSDavid du Colombier
1351*43f728cbSDavid du Colombier if((err = cmd(ctlr, 16, c, p - c)) != nil){
1352*43f728cbSDavid du Colombier print("rxon: %s\n", err);
1353*43f728cbSDavid du Colombier return;
1354*43f728cbSDavid du Colombier }
1355*43f728cbSDavid du Colombier
1356*43f728cbSDavid du Colombier if(ctlr->maxpwr[ctlr->channel] != 0){
1357*43f728cbSDavid du Colombier /* tx power */
1358*43f728cbSDavid du Colombier memset(p = c, 0, sizeof(c));
1359*43f728cbSDavid du Colombier *p++ = 1; /* band (0 = 5ghz) */
1360*43f728cbSDavid du Colombier p++; /* reserved */
1361*43f728cbSDavid du Colombier put16(p, ctlr->channel), p += 2;
1362*43f728cbSDavid du Colombier for(rate = 0; rate < nelem(ratetab); rate++){
1363*43f728cbSDavid du Colombier idx = pwridx(ctlr, &ctlr->eeprom.pwrgrps[0], ctlr->channel, rate);
1364*43f728cbSDavid du Colombier *p++ = ratetab[rate].plcp;
1365*43f728cbSDavid du Colombier *p++ = rfgain_2ghz[idx]; /* rf_gain */
1366*43f728cbSDavid du Colombier *p++ = dspgain_2ghz[idx]; /* dsp_gain */
1367*43f728cbSDavid du Colombier p++; /* reservd */
1368*43f728cbSDavid du Colombier }
1369*43f728cbSDavid du Colombier cmd(ctlr, 151, c, p - c);
1370*43f728cbSDavid du Colombier }
1371*43f728cbSDavid du Colombier
1372*43f728cbSDavid du Colombier if(ctlr->bcastnodeid == -1){
1373*43f728cbSDavid du Colombier ctlr->bcastnodeid = 24;
1374*43f728cbSDavid du Colombier addnode(ctlr, ctlr->bcastnodeid, edev->bcast, ratetab[0].plcp, 3<<6);
1375*43f728cbSDavid du Colombier }
1376*43f728cbSDavid du Colombier if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){
1377*43f728cbSDavid du Colombier ctlr->bssnodeid = 0;
1378*43f728cbSDavid du Colombier addnode(ctlr, ctlr->bssnodeid, bss->bssid, ratetab[0].plcp, 3<<6);
1379*43f728cbSDavid du Colombier }
1380*43f728cbSDavid du Colombier }
1381*43f728cbSDavid du Colombier
1382*43f728cbSDavid du Colombier enum {
1383*43f728cbSDavid du Colombier TFlagNeedRTS = 1<<1,
1384*43f728cbSDavid du Colombier TFlagNeedCTS = 1<<2,
1385*43f728cbSDavid du Colombier TFlagNeedACK = 1<<3,
1386*43f728cbSDavid du Colombier TFlagFullTxOp = 1<<7,
1387*43f728cbSDavid du Colombier TFlagBtDis = 1<<12,
1388*43f728cbSDavid du Colombier TFlagAutoSeq = 1<<13,
1389*43f728cbSDavid du Colombier TFlagInsertTs = 1<<16,
1390*43f728cbSDavid du Colombier };
1391*43f728cbSDavid du Colombier
1392*43f728cbSDavid du Colombier static void
transmit(Wifi * wifi,Wnode * wn,Block * b)1393*43f728cbSDavid du Colombier transmit(Wifi *wifi, Wnode *wn, Block *b)
1394*43f728cbSDavid du Colombier {
1395*43f728cbSDavid du Colombier uchar c[Tcmdsize], *p;
1396*43f728cbSDavid du Colombier Ether *edev;
1397*43f728cbSDavid du Colombier Ctlr *ctlr;
1398*43f728cbSDavid du Colombier Wifipkt *w;
1399*43f728cbSDavid du Colombier int flags, nodeid, rate, timeout;
1400*43f728cbSDavid du Colombier char *err;
1401*43f728cbSDavid du Colombier
1402*43f728cbSDavid du Colombier edev = wifi->ether;
1403*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1404*43f728cbSDavid du Colombier
1405*43f728cbSDavid du Colombier qlock(ctlr);
1406*43f728cbSDavid du Colombier if(ctlr->attached == 0 || ctlr->broken){
1407*43f728cbSDavid du Colombier qunlock(ctlr);
1408*43f728cbSDavid du Colombier freeb(b);
1409*43f728cbSDavid du Colombier return;
1410*43f728cbSDavid du Colombier }
1411*43f728cbSDavid du Colombier
1412*43f728cbSDavid du Colombier if((wn->channel != ctlr->channel)
1413*43f728cbSDavid du Colombier || (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)))
1414*43f728cbSDavid du Colombier rxon(edev, wn);
1415*43f728cbSDavid du Colombier
1416*43f728cbSDavid du Colombier if(b == nil){
1417*43f728cbSDavid du Colombier /* association note has no data to transmit */
1418*43f728cbSDavid du Colombier qunlock(ctlr);
1419*43f728cbSDavid du Colombier return;
1420*43f728cbSDavid du Colombier }
1421*43f728cbSDavid du Colombier
1422*43f728cbSDavid du Colombier flags = 0;
1423*43f728cbSDavid du Colombier timeout = 3;
1424*43f728cbSDavid du Colombier nodeid = ctlr->bcastnodeid;
1425*43f728cbSDavid du Colombier p = wn->minrate;
1426*43f728cbSDavid du Colombier w = (Wifipkt*)b->rp;
1427*43f728cbSDavid du Colombier if((w->a1[0] & 1) == 0){
1428*43f728cbSDavid du Colombier flags |= TFlagNeedACK;
1429*43f728cbSDavid du Colombier
1430*43f728cbSDavid du Colombier if(BLEN(b) > 512-4)
1431*43f728cbSDavid du Colombier flags |= TFlagNeedRTS|TFlagFullTxOp;
1432*43f728cbSDavid du Colombier
1433*43f728cbSDavid du Colombier if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){
1434*43f728cbSDavid du Colombier timeout = 0;
1435*43f728cbSDavid du Colombier nodeid = ctlr->bssnodeid;
1436*43f728cbSDavid du Colombier p = wn->actrate;
1437*43f728cbSDavid du Colombier }
1438*43f728cbSDavid du Colombier }
1439*43f728cbSDavid du Colombier qunlock(ctlr);
1440*43f728cbSDavid du Colombier
1441*43f728cbSDavid du Colombier rate = 0;
1442*43f728cbSDavid du Colombier if(p >= wpirates && p < &wpirates[nelem(ratetab)])
1443*43f728cbSDavid du Colombier rate = p - wpirates;
1444*43f728cbSDavid du Colombier
1445*43f728cbSDavid du Colombier memset(p = c, 0, sizeof(c));
1446*43f728cbSDavid du Colombier put16(p, BLEN(b)), p += 2;
1447*43f728cbSDavid du Colombier put16(p, 0), p += 2; /* lnext */
1448*43f728cbSDavid du Colombier put32(p, flags), p += 4;
1449*43f728cbSDavid du Colombier *p++ = ratetab[rate].plcp;
1450*43f728cbSDavid du Colombier *p++ = nodeid;
1451*43f728cbSDavid du Colombier *p++ = 0; /* tid */
1452*43f728cbSDavid du Colombier *p++ = 0; /* security */
1453*43f728cbSDavid du Colombier p += 16+8; /* key/iv */
1454*43f728cbSDavid du Colombier put32(p, 0), p += 4; /* fnext */
1455*43f728cbSDavid du Colombier put32(p, 0xffffffff), p += 4; /* livetime infinite */
1456*43f728cbSDavid du Colombier *p++ = 0xff;
1457*43f728cbSDavid du Colombier *p++ = 0x0f;
1458*43f728cbSDavid du Colombier *p++ = 7;
1459*43f728cbSDavid du Colombier *p++ = 15;
1460*43f728cbSDavid du Colombier put16(p, timeout), p += 2;
1461*43f728cbSDavid du Colombier put16(p, 0), p += 2; /* txop */
1462*43f728cbSDavid du Colombier
1463*43f728cbSDavid du Colombier if((err = qcmd(ctlr, 0, 28, c, p - c, b)) != nil){
1464*43f728cbSDavid du Colombier print("transmit: %s\n", err);
1465*43f728cbSDavid du Colombier freeb(b);
1466*43f728cbSDavid du Colombier }
1467*43f728cbSDavid du Colombier }
1468*43f728cbSDavid du Colombier
1469*43f728cbSDavid du Colombier static long
wpictl(Ether * edev,void * buf,long n)1470*43f728cbSDavid du Colombier wpictl(Ether *edev, void *buf, long n)
1471*43f728cbSDavid du Colombier {
1472*43f728cbSDavid du Colombier Ctlr *ctlr;
1473*43f728cbSDavid du Colombier
1474*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1475*43f728cbSDavid du Colombier if(n >= 5 && memcmp(buf, "reset", 5) == 0){
1476*43f728cbSDavid du Colombier ctlr->broken = 1;
1477*43f728cbSDavid du Colombier return n;
1478*43f728cbSDavid du Colombier }
1479*43f728cbSDavid du Colombier if(ctlr->wifi)
1480*43f728cbSDavid du Colombier return wifictl(ctlr->wifi, buf, n);
1481*43f728cbSDavid du Colombier return 0;
1482*43f728cbSDavid du Colombier }
1483*43f728cbSDavid du Colombier
1484*43f728cbSDavid du Colombier static long
wpiifstat(Ether * edev,void * buf,long n,ulong off)1485*43f728cbSDavid du Colombier wpiifstat(Ether *edev, void *buf, long n, ulong off)
1486*43f728cbSDavid du Colombier {
1487*43f728cbSDavid du Colombier Ctlr *ctlr;
1488*43f728cbSDavid du Colombier
1489*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1490*43f728cbSDavid du Colombier if(ctlr->wifi)
1491*43f728cbSDavid du Colombier return wifistat(ctlr->wifi, buf, n, off);
1492*43f728cbSDavid du Colombier return 0;
1493*43f728cbSDavid du Colombier }
1494*43f728cbSDavid du Colombier
1495*43f728cbSDavid du Colombier static void
setoptions(Ether * edev)1496*43f728cbSDavid du Colombier setoptions(Ether *edev)
1497*43f728cbSDavid du Colombier {
1498*43f728cbSDavid du Colombier Ctlr *ctlr;
1499*43f728cbSDavid du Colombier int i;
1500*43f728cbSDavid du Colombier
1501*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1502*43f728cbSDavid du Colombier for(i = 0; i < edev->nopt; i++)
1503*43f728cbSDavid du Colombier wificfg(ctlr->wifi, edev->opt[i]);
1504*43f728cbSDavid du Colombier }
1505*43f728cbSDavid du Colombier
1506*43f728cbSDavid du Colombier static void
wpipromiscuous(void * arg,int on)1507*43f728cbSDavid du Colombier wpipromiscuous(void *arg, int on)
1508*43f728cbSDavid du Colombier {
1509*43f728cbSDavid du Colombier Ether *edev;
1510*43f728cbSDavid du Colombier Ctlr *ctlr;
1511*43f728cbSDavid du Colombier
1512*43f728cbSDavid du Colombier edev = arg;
1513*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1514*43f728cbSDavid du Colombier qlock(ctlr);
1515*43f728cbSDavid du Colombier ctlr->prom = on;
1516*43f728cbSDavid du Colombier rxon(edev, ctlr->wifi->bss);
1517*43f728cbSDavid du Colombier qunlock(ctlr);
1518*43f728cbSDavid du Colombier }
1519*43f728cbSDavid du Colombier
1520*43f728cbSDavid du Colombier static void
wpimulticast(void *,uchar *,int)1521*43f728cbSDavid du Colombier wpimulticast(void *, uchar*, int)
1522*43f728cbSDavid du Colombier {
1523*43f728cbSDavid du Colombier }
1524*43f728cbSDavid du Colombier
1525*43f728cbSDavid du Colombier static void
wpirecover(void * arg)1526*43f728cbSDavid du Colombier wpirecover(void *arg)
1527*43f728cbSDavid du Colombier {
1528*43f728cbSDavid du Colombier Ether *edev;
1529*43f728cbSDavid du Colombier Ctlr *ctlr;
1530*43f728cbSDavid du Colombier
1531*43f728cbSDavid du Colombier edev = arg;
1532*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1533*43f728cbSDavid du Colombier while(waserror())
1534*43f728cbSDavid du Colombier ;
1535*43f728cbSDavid du Colombier for(;;){
1536*43f728cbSDavid du Colombier tsleep(&up->sleep, return0, 0, 4000);
1537*43f728cbSDavid du Colombier
1538*43f728cbSDavid du Colombier qlock(ctlr);
1539*43f728cbSDavid du Colombier for(;;){
1540*43f728cbSDavid du Colombier if(ctlr->broken == 0)
1541*43f728cbSDavid du Colombier break;
1542*43f728cbSDavid du Colombier
1543*43f728cbSDavid du Colombier if(ctlr->power)
1544*43f728cbSDavid du Colombier poweroff(ctlr);
1545*43f728cbSDavid du Colombier
1546*43f728cbSDavid du Colombier if((csr32r(ctlr, Gpc) & RfKill) == 0)
1547*43f728cbSDavid du Colombier break;
1548*43f728cbSDavid du Colombier
1549*43f728cbSDavid du Colombier if(reset(ctlr) != nil)
1550*43f728cbSDavid du Colombier break;
1551*43f728cbSDavid du Colombier if(boot(ctlr) != nil)
1552*43f728cbSDavid du Colombier break;
1553*43f728cbSDavid du Colombier
1554*43f728cbSDavid du Colombier ctlr->bcastnodeid = -1;
1555*43f728cbSDavid du Colombier ctlr->bssnodeid = -1;
1556*43f728cbSDavid du Colombier ctlr->aid = 0;
1557*43f728cbSDavid du Colombier rxon(edev, ctlr->wifi->bss);
1558*43f728cbSDavid du Colombier break;
1559*43f728cbSDavid du Colombier }
1560*43f728cbSDavid du Colombier qunlock(ctlr);
1561*43f728cbSDavid du Colombier }
1562*43f728cbSDavid du Colombier }
1563*43f728cbSDavid du Colombier
1564*43f728cbSDavid du Colombier static void
wpiattach(Ether * edev)1565*43f728cbSDavid du Colombier wpiattach(Ether *edev)
1566*43f728cbSDavid du Colombier {
1567*43f728cbSDavid du Colombier FWImage *fw;
1568*43f728cbSDavid du Colombier Ctlr *ctlr;
1569*43f728cbSDavid du Colombier char *err;
1570*43f728cbSDavid du Colombier
1571*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1572*43f728cbSDavid du Colombier qlock(ctlr);
1573*43f728cbSDavid du Colombier if(waserror()){
1574*43f728cbSDavid du Colombier print("#l%d: %s\n", edev->ctlrno, up->errstr);
1575*43f728cbSDavid du Colombier if(ctlr->power)
1576*43f728cbSDavid du Colombier poweroff(ctlr);
1577*43f728cbSDavid du Colombier qunlock(ctlr);
1578*43f728cbSDavid du Colombier nexterror();
1579*43f728cbSDavid du Colombier }
1580*43f728cbSDavid du Colombier if(ctlr->attached == 0){
1581*43f728cbSDavid du Colombier if((csr32r(ctlr, Gpc) & RfKill) == 0)
1582*43f728cbSDavid du Colombier error("wifi disabled by switch");
1583*43f728cbSDavid du Colombier
1584*43f728cbSDavid du Colombier if(ctlr->wifi == nil){
1585*43f728cbSDavid du Colombier ctlr->wifi = wifiattach(edev, transmit);
1586*43f728cbSDavid du Colombier ctlr->wifi->rates = wpirates;
1587*43f728cbSDavid du Colombier }
1588*43f728cbSDavid du Colombier
1589*43f728cbSDavid du Colombier if(ctlr->fw == nil){
1590*43f728cbSDavid du Colombier fw = readfirmware();
1591*43f728cbSDavid du Colombier print("#l%d: firmware: %ux, size: %ux+%ux+%ux+%ux+%ux\n",
1592*43f728cbSDavid du Colombier edev->ctlrno, fw->version,
1593*43f728cbSDavid du Colombier fw->main.text.size, fw->main.data.size,
1594*43f728cbSDavid du Colombier fw->init.text.size, fw->init.data.size,
1595*43f728cbSDavid du Colombier fw->boot.text.size);
1596*43f728cbSDavid du Colombier ctlr->fw = fw;
1597*43f728cbSDavid du Colombier }
1598*43f728cbSDavid du Colombier
1599*43f728cbSDavid du Colombier if((err = reset(ctlr)) != nil)
1600*43f728cbSDavid du Colombier error(err);
1601*43f728cbSDavid du Colombier if((err = boot(ctlr)) != nil)
1602*43f728cbSDavid du Colombier error(err);
1603*43f728cbSDavid du Colombier
1604*43f728cbSDavid du Colombier ctlr->bcastnodeid = -1;
1605*43f728cbSDavid du Colombier ctlr->bssnodeid = -1;
1606*43f728cbSDavid du Colombier ctlr->channel = 1;
1607*43f728cbSDavid du Colombier ctlr->aid = 0;
1608*43f728cbSDavid du Colombier
1609*43f728cbSDavid du Colombier setoptions(edev);
1610*43f728cbSDavid du Colombier
1611*43f728cbSDavid du Colombier ctlr->attached = 1;
1612*43f728cbSDavid du Colombier
1613*43f728cbSDavid du Colombier kproc("wpirecover", wpirecover, edev);
1614*43f728cbSDavid du Colombier }
1615*43f728cbSDavid du Colombier qunlock(ctlr);
1616*43f728cbSDavid du Colombier poperror();
1617*43f728cbSDavid du Colombier }
1618*43f728cbSDavid du Colombier
1619*43f728cbSDavid du Colombier static void
receive(Ctlr * ctlr)1620*43f728cbSDavid du Colombier receive(Ctlr *ctlr)
1621*43f728cbSDavid du Colombier {
1622*43f728cbSDavid du Colombier Block *b, *bb;
1623*43f728cbSDavid du Colombier uchar *d;
1624*43f728cbSDavid du Colombier RXQ *rx;
1625*43f728cbSDavid du Colombier TXQ *tx;
1626*43f728cbSDavid du Colombier u32int hw;
1627*43f728cbSDavid du Colombier
1628*43f728cbSDavid du Colombier rx = &ctlr->rx;
1629*43f728cbSDavid du Colombier if(ctlr->broken || ctlr->shared == nil || rx->b == nil)
1630*43f728cbSDavid du Colombier return;
1631*43f728cbSDavid du Colombier
1632*43f728cbSDavid du Colombier bb = nil;
1633*43f728cbSDavid du Colombier for(hw = ctlr->shared->next % Nrx; rx->i != hw; rx->i = (rx->i + 1) % Nrx){
1634*43f728cbSDavid du Colombier uchar type, flags, idx, qid;
1635*43f728cbSDavid du Colombier u32int len;
1636*43f728cbSDavid du Colombier
1637*43f728cbSDavid du Colombier b = rx->b[rx->i];
1638*43f728cbSDavid du Colombier if(b == nil)
1639*43f728cbSDavid du Colombier continue;
1640*43f728cbSDavid du Colombier
1641*43f728cbSDavid du Colombier d = b->rp;
1642*43f728cbSDavid du Colombier len = get32(d); d += 4;
1643*43f728cbSDavid du Colombier type = *d++;
1644*43f728cbSDavid du Colombier flags = *d++;
1645*43f728cbSDavid du Colombier idx = *d++;
1646*43f728cbSDavid du Colombier qid = *d++;
1647*43f728cbSDavid du Colombier
1648*43f728cbSDavid du Colombier USED(len);
1649*43f728cbSDavid du Colombier USED(flags);
1650*43f728cbSDavid du Colombier
1651*43f728cbSDavid du Colombier if(0) iprint("rxdesc[%d] type=%d len=%d idx=%d qid=%d\n", rx->i, type, len, idx, qid);
1652*43f728cbSDavid du Colombier
1653*43f728cbSDavid du Colombier if(bb != nil){
1654*43f728cbSDavid du Colombier freeb(bb);
1655*43f728cbSDavid du Colombier bb = nil;
1656*43f728cbSDavid du Colombier }
1657*43f728cbSDavid du Colombier if((qid & 0x80) == 0 && qid < nelem(ctlr->tx)){
1658*43f728cbSDavid du Colombier tx = &ctlr->tx[qid];
1659*43f728cbSDavid du Colombier if(tx->n > 0){
1660*43f728cbSDavid du Colombier bb = tx->b[idx];
1661*43f728cbSDavid du Colombier tx->b[idx] = nil;
1662*43f728cbSDavid du Colombier tx->n--;
1663*43f728cbSDavid du Colombier wakeup(tx);
1664*43f728cbSDavid du Colombier }
1665*43f728cbSDavid du Colombier }
1666*43f728cbSDavid du Colombier
1667*43f728cbSDavid du Colombier switch(type){
1668*43f728cbSDavid du Colombier case 1: /* uc ready */
1669*43f728cbSDavid du Colombier break;
1670*43f728cbSDavid du Colombier
1671*43f728cbSDavid du Colombier case 24: /* add node done */
1672*43f728cbSDavid du Colombier break;
1673*43f728cbSDavid du Colombier
1674*43f728cbSDavid du Colombier case 27: /* rx done */
1675*43f728cbSDavid du Colombier if(d + 1 > b->lim)
1676*43f728cbSDavid du Colombier break;
1677*43f728cbSDavid du Colombier d += d[0];
1678*43f728cbSDavid du Colombier d += 8;
1679*43f728cbSDavid du Colombier if(d + 6 + 2 > b->lim){
1680*43f728cbSDavid du Colombier break;
1681*43f728cbSDavid du Colombier }
1682*43f728cbSDavid du Colombier len = get16(d+6);
1683*43f728cbSDavid du Colombier d += 8;
1684*43f728cbSDavid du Colombier if(d + len + 4 > b->lim){
1685*43f728cbSDavid du Colombier break;
1686*43f728cbSDavid du Colombier }
1687*43f728cbSDavid du Colombier if((get32(d + len) & 3) != 3){
1688*43f728cbSDavid du Colombier break;
1689*43f728cbSDavid du Colombier }
1690*43f728cbSDavid du Colombier if(ctlr->wifi == nil)
1691*43f728cbSDavid du Colombier break;
1692*43f728cbSDavid du Colombier if(rbplant(ctlr, rx->i) < 0)
1693*43f728cbSDavid du Colombier break;
1694*43f728cbSDavid du Colombier b->rp = d;
1695*43f728cbSDavid du Colombier b->wp = d + len;
1696*43f728cbSDavid du Colombier wifiiq(ctlr->wifi, b);
1697*43f728cbSDavid du Colombier continue;
1698*43f728cbSDavid du Colombier
1699*43f728cbSDavid du Colombier case 28: /* tx done */
1700*43f728cbSDavid du Colombier if(len <= 8 || d[8] == 1)
1701*43f728cbSDavid du Colombier break;
1702*43f728cbSDavid du Colombier wifitxfail(ctlr->wifi, bb);
1703*43f728cbSDavid du Colombier break;
1704*43f728cbSDavid du Colombier
1705*43f728cbSDavid du Colombier case 130: /* start scan */
1706*43f728cbSDavid du Colombier break;
1707*43f728cbSDavid du Colombier
1708*43f728cbSDavid du Colombier case 132: /* stop scan */
1709*43f728cbSDavid du Colombier break;
1710*43f728cbSDavid du Colombier
1711*43f728cbSDavid du Colombier case 161: /* state change */
1712*43f728cbSDavid du Colombier break;
1713*43f728cbSDavid du Colombier }
1714*43f728cbSDavid du Colombier }
1715*43f728cbSDavid du Colombier csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7);
1716*43f728cbSDavid du Colombier if(bb != nil)
1717*43f728cbSDavid du Colombier freeb(bb);
1718*43f728cbSDavid du Colombier }
1719*43f728cbSDavid du Colombier
1720*43f728cbSDavid du Colombier static void
wpiinterrupt(Ureg *,void * arg)1721*43f728cbSDavid du Colombier wpiinterrupt(Ureg*, void *arg)
1722*43f728cbSDavid du Colombier {
1723*43f728cbSDavid du Colombier u32int isr, fhisr;
1724*43f728cbSDavid du Colombier Ether *edev;
1725*43f728cbSDavid du Colombier Ctlr *ctlr;
1726*43f728cbSDavid du Colombier
1727*43f728cbSDavid du Colombier edev = arg;
1728*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1729*43f728cbSDavid du Colombier ilock(ctlr);
1730*43f728cbSDavid du Colombier csr32w(ctlr, Imr, 0);
1731*43f728cbSDavid du Colombier isr = csr32r(ctlr, Isr);
1732*43f728cbSDavid du Colombier fhisr = csr32r(ctlr, FhIsr);
1733*43f728cbSDavid du Colombier if(isr == 0xffffffff || (isr & 0xfffffff0) == 0xa5a5a5a0){
1734*43f728cbSDavid du Colombier iunlock(ctlr);
1735*43f728cbSDavid du Colombier return;
1736*43f728cbSDavid du Colombier }
1737*43f728cbSDavid du Colombier if(isr == 0 && fhisr == 0)
1738*43f728cbSDavid du Colombier goto done;
1739*43f728cbSDavid du Colombier csr32w(ctlr, Isr, isr);
1740*43f728cbSDavid du Colombier csr32w(ctlr, FhIsr, fhisr);
1741*43f728cbSDavid du Colombier if((isr & (Iswrx | Ifhrx)) || (fhisr & Ifhrx))
1742*43f728cbSDavid du Colombier receive(ctlr);
1743*43f728cbSDavid du Colombier if(isr & Ierr){
1744*43f728cbSDavid du Colombier ctlr->broken = 1;
1745*43f728cbSDavid du Colombier iprint("#l%d: fatal firmware error, lastcmd %ud\n", edev->ctlrno, ctlr->tx[4].lastcmd);
1746*43f728cbSDavid du Colombier }
1747*43f728cbSDavid du Colombier ctlr->wait.m |= isr;
1748*43f728cbSDavid du Colombier if(ctlr->wait.m & ctlr->wait.w)
1749*43f728cbSDavid du Colombier wakeup(&ctlr->wait);
1750*43f728cbSDavid du Colombier done:
1751*43f728cbSDavid du Colombier csr32w(ctlr, Imr, ctlr->ie);
1752*43f728cbSDavid du Colombier iunlock(ctlr);
1753*43f728cbSDavid du Colombier }
1754*43f728cbSDavid du Colombier
1755*43f728cbSDavid du Colombier static void
wpishutdown(Ether * edev)1756*43f728cbSDavid du Colombier wpishutdown(Ether *edev)
1757*43f728cbSDavid du Colombier {
1758*43f728cbSDavid du Colombier Ctlr *ctlr;
1759*43f728cbSDavid du Colombier
1760*43f728cbSDavid du Colombier ctlr = edev->ctlr;
1761*43f728cbSDavid du Colombier if(ctlr->power)
1762*43f728cbSDavid du Colombier poweroff(ctlr);
1763*43f728cbSDavid du Colombier ctlr->broken = 0;
1764*43f728cbSDavid du Colombier }
1765*43f728cbSDavid du Colombier
1766*43f728cbSDavid du Colombier static Ctlr *wpihead, *wpitail;
1767*43f728cbSDavid du Colombier
1768*43f728cbSDavid du Colombier static void
wpipci(void)1769*43f728cbSDavid du Colombier wpipci(void)
1770*43f728cbSDavid du Colombier {
1771*43f728cbSDavid du Colombier Pcidev *pdev;
1772*43f728cbSDavid du Colombier
1773*43f728cbSDavid du Colombier pdev = nil;
1774*43f728cbSDavid du Colombier while(pdev = pcimatch(pdev, 0x8086, 0)){
1775*43f728cbSDavid du Colombier Ctlr *ctlr;
1776*43f728cbSDavid du Colombier void *mem;
1777*43f728cbSDavid du Colombier switch(pdev->did){
1778*43f728cbSDavid du Colombier default:
1779*43f728cbSDavid du Colombier continue;
1780*43f728cbSDavid du Colombier case 0x4227:
1781*43f728cbSDavid du Colombier break;
1782*43f728cbSDavid du Colombier }
1783*43f728cbSDavid du Colombier
1784*43f728cbSDavid du Colombier /* Clear device-specific "PCI retry timeout" register (41h). */
1785*43f728cbSDavid du Colombier if(pcicfgr8(pdev, 0x41) != 0)
1786*43f728cbSDavid du Colombier pcicfgw8(pdev, 0x41, 0);
1787*43f728cbSDavid du Colombier
1788*43f728cbSDavid du Colombier pcisetbme(pdev);
1789*43f728cbSDavid du Colombier pcisetpms(pdev, 0);
1790*43f728cbSDavid du Colombier
1791*43f728cbSDavid du Colombier ctlr = malloc(sizeof(Ctlr));
1792*43f728cbSDavid du Colombier if(ctlr == nil) {
1793*43f728cbSDavid du Colombier print("wpi: unable to alloc Ctlr\n");
1794*43f728cbSDavid du Colombier continue;
1795*43f728cbSDavid du Colombier }
1796*43f728cbSDavid du Colombier ctlr->port = pdev->mem[0].bar & ~0x0F;
1797*43f728cbSDavid du Colombier mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size);
1798*43f728cbSDavid du Colombier if(mem == nil) {
1799*43f728cbSDavid du Colombier print("wpi: can't map %8.8luX\n", pdev->mem[0].bar);
1800*43f728cbSDavid du Colombier free(ctlr);
1801*43f728cbSDavid du Colombier continue;
1802*43f728cbSDavid du Colombier }
1803*43f728cbSDavid du Colombier ctlr->nic = mem;
1804*43f728cbSDavid du Colombier ctlr->pdev = pdev;
1805*43f728cbSDavid du Colombier
1806*43f728cbSDavid du Colombier if(wpihead != nil)
1807*43f728cbSDavid du Colombier wpitail->link = ctlr;
1808*43f728cbSDavid du Colombier else
1809*43f728cbSDavid du Colombier wpihead = ctlr;
1810*43f728cbSDavid du Colombier wpitail = ctlr;
1811*43f728cbSDavid du Colombier }
1812*43f728cbSDavid du Colombier }
1813*43f728cbSDavid du Colombier
1814*43f728cbSDavid du Colombier static int
wpipnp(Ether * edev)1815*43f728cbSDavid du Colombier wpipnp(Ether *edev)
1816*43f728cbSDavid du Colombier {
1817*43f728cbSDavid du Colombier Ctlr *ctlr;
1818*43f728cbSDavid du Colombier
1819*43f728cbSDavid du Colombier if(wpihead == nil)
1820*43f728cbSDavid du Colombier wpipci();
1821*43f728cbSDavid du Colombier
1822*43f728cbSDavid du Colombier again:
1823*43f728cbSDavid du Colombier for(ctlr = wpihead; ctlr != nil; ctlr = ctlr->link){
1824*43f728cbSDavid du Colombier if(ctlr->active)
1825*43f728cbSDavid du Colombier continue;
1826*43f728cbSDavid du Colombier if(edev->port == 0 || edev->port == ctlr->port){
1827*43f728cbSDavid du Colombier ctlr->active = 1;
1828*43f728cbSDavid du Colombier break;
1829*43f728cbSDavid du Colombier }
1830*43f728cbSDavid du Colombier }
1831*43f728cbSDavid du Colombier
1832*43f728cbSDavid du Colombier if(ctlr == nil)
1833*43f728cbSDavid du Colombier return -1;
1834*43f728cbSDavid du Colombier
1835*43f728cbSDavid du Colombier edev->ctlr = ctlr;
1836*43f728cbSDavid du Colombier edev->port = ctlr->port;
1837*43f728cbSDavid du Colombier edev->irq = ctlr->pdev->intl;
1838*43f728cbSDavid du Colombier edev->tbdf = ctlr->pdev->tbdf;
1839*43f728cbSDavid du Colombier edev->arg = edev;
1840*43f728cbSDavid du Colombier edev->interrupt = wpiinterrupt;
1841*43f728cbSDavid du Colombier edev->attach = wpiattach;
1842*43f728cbSDavid du Colombier edev->ifstat = wpiifstat;
1843*43f728cbSDavid du Colombier edev->ctl = wpictl;
1844*43f728cbSDavid du Colombier edev->shutdown = wpishutdown;
1845*43f728cbSDavid du Colombier edev->promiscuous = wpipromiscuous;
1846*43f728cbSDavid du Colombier edev->multicast = wpimulticast;
1847*43f728cbSDavid du Colombier edev->mbps = 54;
1848*43f728cbSDavid du Colombier
1849*43f728cbSDavid du Colombier if(wpiinit(edev) < 0){
1850*43f728cbSDavid du Colombier edev->ctlr = nil;
1851*43f728cbSDavid du Colombier goto again;
1852*43f728cbSDavid du Colombier }
1853*43f728cbSDavid du Colombier
1854*43f728cbSDavid du Colombier return 0;
1855*43f728cbSDavid du Colombier }
1856*43f728cbSDavid du Colombier
1857*43f728cbSDavid du Colombier void
etherwpilink(void)1858*43f728cbSDavid du Colombier etherwpilink(void)
1859*43f728cbSDavid du Colombier {
1860*43f728cbSDavid du Colombier addethercard("wpi", wpipnp);
1861*43f728cbSDavid du Colombier }
1862