xref: /plan9-contrib/sys/src/9/pc/etherwpi.c (revision 43f728cba48694b0a42c538a38fdf975a0ae5433)
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