xref: /plan9-contrib/sys/src/9/bcm/ether4330.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1*5c47fe09SDavid du Colombier /*
2*5c47fe09SDavid du Colombier  * Broadcom bcm4330 wifi (sdio interface)
3*5c47fe09SDavid du Colombier  */
4*5c47fe09SDavid du Colombier 
5*5c47fe09SDavid du Colombier #include "u.h"
6*5c47fe09SDavid du Colombier #include "../port/lib.h"
7*5c47fe09SDavid du Colombier #include "mem.h"
8*5c47fe09SDavid du Colombier #include "dat.h"
9*5c47fe09SDavid du Colombier #include "fns.h"
10*5c47fe09SDavid du Colombier #include "io.h"
11*5c47fe09SDavid du Colombier #include "../port/error.h"
12*5c47fe09SDavid du Colombier #include "../port/netif.h"
13*5c47fe09SDavid du Colombier #include "../port/sd.h"
14*5c47fe09SDavid du Colombier 
15*5c47fe09SDavid du Colombier extern int sdiocardintr(int);
16*5c47fe09SDavid du Colombier 
17*5c47fe09SDavid du Colombier #include "etherif.h"
18*5c47fe09SDavid du Colombier #define CACHELINESZ 64	/* temp */
19*5c47fe09SDavid du Colombier 
20*5c47fe09SDavid du Colombier enum{
21*5c47fe09SDavid du Colombier 	SDIODEBUG = 0,
22*5c47fe09SDavid du Colombier 	SBDEBUG = 0,
23*5c47fe09SDavid du Colombier 	EVENTDEBUG = 0,
24*5c47fe09SDavid du Colombier 	VARDEBUG = 0,
25*5c47fe09SDavid du Colombier 	FWDEBUG  = 0,
26*5c47fe09SDavid du Colombier 
27*5c47fe09SDavid du Colombier 	Corescansz = 512,
28*5c47fe09SDavid du Colombier 	Uploadsz = 2048,
29*5c47fe09SDavid du Colombier 
30*5c47fe09SDavid du Colombier 	Wifichan = 0,		/* default channel */
31*5c47fe09SDavid du Colombier 	Firmwarecmp	= 1,
32*5c47fe09SDavid du Colombier 
33*5c47fe09SDavid du Colombier 	ARMcm3		= 0x82A,
34*5c47fe09SDavid du Colombier 	ARM7tdmi	= 0x825,
35*5c47fe09SDavid du Colombier 	ARMcr4		= 0x83E,
36*5c47fe09SDavid du Colombier 
37*5c47fe09SDavid du Colombier 	Fn0	= 0,
38*5c47fe09SDavid du Colombier 	Fn1 	= 1,
39*5c47fe09SDavid du Colombier 	Fn2	= 2,
40*5c47fe09SDavid du Colombier 	Fbr1	= 0x100,
41*5c47fe09SDavid du Colombier 	Fbr2	= 0x200,
42*5c47fe09SDavid du Colombier 
43*5c47fe09SDavid du Colombier 	/* CCCR */
44*5c47fe09SDavid du Colombier 	Ioenable	= 0x02,
45*5c47fe09SDavid du Colombier 	Ioready		= 0x03,
46*5c47fe09SDavid du Colombier 	Intenable	= 0x04,
47*5c47fe09SDavid du Colombier 	Intpend		= 0x05,
48*5c47fe09SDavid du Colombier 	Ioabort		= 0x06,
49*5c47fe09SDavid du Colombier 	Busifc		= 0x07,
50*5c47fe09SDavid du Colombier 	Capability	= 0x08,
51*5c47fe09SDavid du Colombier 	Blksize		= 0x10,
52*5c47fe09SDavid du Colombier 	Highspeed	= 0x13,
53*5c47fe09SDavid du Colombier 
54*5c47fe09SDavid du Colombier 	/* SDIOCommands */
55*5c47fe09SDavid du Colombier 	GO_IDLE_STATE		= 0,
56*5c47fe09SDavid du Colombier 	SEND_RELATIVE_ADDR	= 3,
57*5c47fe09SDavid du Colombier 	IO_SEND_OP_COND		= 5,
58*5c47fe09SDavid du Colombier 	SELECT_CARD		= 7,
59*5c47fe09SDavid du Colombier 	VOLTAGE_SWITCH 		= 11,
60*5c47fe09SDavid du Colombier 	IO_RW_DIRECT 		= 52,
61*5c47fe09SDavid du Colombier 	IO_RW_EXTENDED 		= 53,
62*5c47fe09SDavid du Colombier 
63*5c47fe09SDavid du Colombier 	/* SELECT_CARD args */
64*5c47fe09SDavid du Colombier 	Rcashift	= 16,
65*5c47fe09SDavid du Colombier 
66*5c47fe09SDavid du Colombier 	/* SEND_OP_COND args */
67*5c47fe09SDavid du Colombier 	Hcs	= 1<<30,	/* host supports SDHC & SDXC */
68*5c47fe09SDavid du Colombier 	V3_3	= 3<<20,	/* 3.2-3.4 volts */
69*5c47fe09SDavid du Colombier 	V2_8	= 3<<15,	/* 2.7-2.9 volts */
70*5c47fe09SDavid du Colombier 	V2_0	= 1<<8,		/* 2.0-2.1 volts */
71*5c47fe09SDavid du Colombier 	S18R	= 1<<24,	/* switch to 1.8V request */
72*5c47fe09SDavid du Colombier 
73*5c47fe09SDavid du Colombier 	/* Sonics Silicon Backplane (access to cores on chip) */
74*5c47fe09SDavid du Colombier 	Sbwsize	= 0x8000,
75*5c47fe09SDavid du Colombier 	Sb32bit	= 0x8000,
76*5c47fe09SDavid du Colombier 	Sbaddr	= 0x1000a,
77*5c47fe09SDavid du Colombier 		Enumbase	= 	0x18000000,
78*5c47fe09SDavid du Colombier 	Framectl= 0x1000d,
79*5c47fe09SDavid du Colombier 		Rfhalt		=	0x01,
80*5c47fe09SDavid du Colombier 		Wfhalt		=	0x02,
81*5c47fe09SDavid du Colombier 	Clkcsr	= 0x1000e,
82*5c47fe09SDavid du Colombier 		ForceALP	=	0x01,	/* active low-power clock */
83*5c47fe09SDavid du Colombier 		ForceHT		= 	0x02,	/* high throughput clock */
84*5c47fe09SDavid du Colombier 		ForceILP	=	0x04,	/* idle low-power clock */
85*5c47fe09SDavid du Colombier 		ReqALP		=	0x08,
86*5c47fe09SDavid du Colombier 		ReqHT		=	0x10,
87*5c47fe09SDavid du Colombier 		Nohwreq		=	0x20,
88*5c47fe09SDavid du Colombier 		ALPavail	=	0x40,
89*5c47fe09SDavid du Colombier 		HTavail		=	0x80,
90*5c47fe09SDavid du Colombier 	Pullups	= 0x1000f,
91*5c47fe09SDavid du Colombier 	Wfrmcnt	= 0x10019,
92*5c47fe09SDavid du Colombier 	Rfrmcnt	= 0x1001b,
93*5c47fe09SDavid du Colombier 
94*5c47fe09SDavid du Colombier 	/* core control regs */
95*5c47fe09SDavid du Colombier 	Ioctrl		= 0x408,
96*5c47fe09SDavid du Colombier 	Resetctrl	= 0x800,
97*5c47fe09SDavid du Colombier 
98*5c47fe09SDavid du Colombier 	/* socram regs */
99*5c47fe09SDavid du Colombier 	Coreinfo	= 0x00,
100*5c47fe09SDavid du Colombier 	Bankidx		= 0x10,
101*5c47fe09SDavid du Colombier 	Bankinfo	= 0x40,
102*5c47fe09SDavid du Colombier 	Bankpda		= 0x44,
103*5c47fe09SDavid du Colombier 
104*5c47fe09SDavid du Colombier 	/* armcr4 regs */
105*5c47fe09SDavid du Colombier 	Cr4Cap		= 0x04,
106*5c47fe09SDavid du Colombier 	Cr4Bankidx	= 0x40,
107*5c47fe09SDavid du Colombier 	Cr4Bankinfo	= 0x44,
108*5c47fe09SDavid du Colombier 	Cr4Cpuhalt	= 0x20,
109*5c47fe09SDavid du Colombier 
110*5c47fe09SDavid du Colombier 	/* chipcommon regs */
111*5c47fe09SDavid du Colombier 	Gpiopullup	= 0x58,
112*5c47fe09SDavid du Colombier 	Gpiopulldown	= 0x5c,
113*5c47fe09SDavid du Colombier 	Chipctladdr	= 0x650,
114*5c47fe09SDavid du Colombier 	Chipctldata	= 0x654,
115*5c47fe09SDavid du Colombier 
116*5c47fe09SDavid du Colombier 	/* sdio core regs */
117*5c47fe09SDavid du Colombier 	Intstatus	= 0x20,
118*5c47fe09SDavid du Colombier 		Fcstate		= 1<<4,
119*5c47fe09SDavid du Colombier 		Fcchange	= 1<<5,
120*5c47fe09SDavid du Colombier 		FrameInt	= 1<<6,
121*5c47fe09SDavid du Colombier 		MailboxInt	= 1<<7,
122*5c47fe09SDavid du Colombier 	Intmask		= 0x24,
123*5c47fe09SDavid du Colombier 	Sbmbox		= 0x40,
124*5c47fe09SDavid du Colombier 	Sbmboxdata	= 0x48,
125*5c47fe09SDavid du Colombier 	Hostmboxdata= 0x4c,
126*5c47fe09SDavid du Colombier 		Fwready		= 0x80,
127*5c47fe09SDavid du Colombier 
128*5c47fe09SDavid du Colombier 	/* wifi control commands */
129*5c47fe09SDavid du Colombier 	GetVar	= 262,
130*5c47fe09SDavid du Colombier 	SetVar	= 263,
131*5c47fe09SDavid du Colombier 
132*5c47fe09SDavid du Colombier 	/* status */
133*5c47fe09SDavid du Colombier 	Disconnected=	0,
134*5c47fe09SDavid du Colombier 	Connecting,
135*5c47fe09SDavid du Colombier 	Connected,
136*5c47fe09SDavid du Colombier };
137*5c47fe09SDavid du Colombier 
138*5c47fe09SDavid du Colombier typedef struct Ctlr Ctlr;
139*5c47fe09SDavid du Colombier 
140*5c47fe09SDavid du Colombier enum{
141*5c47fe09SDavid du Colombier 	Wpa		= 1,
142*5c47fe09SDavid du Colombier 	Wep		= 2,
143*5c47fe09SDavid du Colombier 	Wpa2		= 3,
144*5c47fe09SDavid du Colombier 	WNameLen	= 32,
145*5c47fe09SDavid du Colombier 	WNKeys		= 4,
146*5c47fe09SDavid du Colombier 	WKeyLen		= 32,
147*5c47fe09SDavid du Colombier 	WMinKeyLen	= 5,
148*5c47fe09SDavid du Colombier 	WMaxKeyLen	= 13,
149*5c47fe09SDavid du Colombier };
150*5c47fe09SDavid du Colombier 
151*5c47fe09SDavid du Colombier typedef struct WKey WKey;
152*5c47fe09SDavid du Colombier struct WKey
153*5c47fe09SDavid du Colombier {
154*5c47fe09SDavid du Colombier 	ushort	len;
155*5c47fe09SDavid du Colombier 	char	dat[WKeyLen];
156*5c47fe09SDavid du Colombier };
157*5c47fe09SDavid du Colombier 
158*5c47fe09SDavid du Colombier struct Ctlr {
159*5c47fe09SDavid du Colombier 	Ether*	edev;
160*5c47fe09SDavid du Colombier 	QLock	cmdlock;
161*5c47fe09SDavid du Colombier 	QLock	pktlock;
162*5c47fe09SDavid du Colombier 	QLock	tlock;
163*5c47fe09SDavid du Colombier 	QLock	alock;
164*5c47fe09SDavid du Colombier 	Lock	txwinlock;
165*5c47fe09SDavid du Colombier 	Rendez	cmdr;
166*5c47fe09SDavid du Colombier 	Rendez	joinr;
167*5c47fe09SDavid du Colombier 	int	joinstatus;
168*5c47fe09SDavid du Colombier 	int	cryptotype;
169*5c47fe09SDavid du Colombier 	int	chanid;
170*5c47fe09SDavid du Colombier 	char	essid[WNameLen + 1];
171*5c47fe09SDavid du Colombier 	WKey	keys[WNKeys];
172*5c47fe09SDavid du Colombier 	Block	*rsp;
173*5c47fe09SDavid du Colombier 	Block	*scanb;
174*5c47fe09SDavid du Colombier 	int	scansecs;
175*5c47fe09SDavid du Colombier 	int	status;
176*5c47fe09SDavid du Colombier 	int	chipid;
177*5c47fe09SDavid du Colombier 	int	chiprev;
178*5c47fe09SDavid du Colombier 	int	armcore;
179*5c47fe09SDavid du Colombier 	char	*regufile;
180*5c47fe09SDavid du Colombier 	union {
181*5c47fe09SDavid du Colombier 		u32int i;
182*5c47fe09SDavid du Colombier 		uchar c[4];
183*5c47fe09SDavid du Colombier 	} resetvec;
184*5c47fe09SDavid du Colombier 	ulong	chipcommon;
185*5c47fe09SDavid du Colombier 	ulong	armctl;
186*5c47fe09SDavid du Colombier 	ulong	armregs;
187*5c47fe09SDavid du Colombier 	ulong	d11ctl;
188*5c47fe09SDavid du Colombier 	ulong	socramregs;
189*5c47fe09SDavid du Colombier 	ulong	socramctl;
190*5c47fe09SDavid du Colombier 	ulong	sdregs;
191*5c47fe09SDavid du Colombier 	int	sdiorev;
192*5c47fe09SDavid du Colombier 	int	socramrev;
193*5c47fe09SDavid du Colombier 	ulong	socramsize;
194*5c47fe09SDavid du Colombier 	ulong	rambase;
195*5c47fe09SDavid du Colombier 	short	reqid;
196*5c47fe09SDavid du Colombier 	uchar	fcmask;
197*5c47fe09SDavid du Colombier 	uchar	txwindow;
198*5c47fe09SDavid du Colombier 	uchar	txseq;
199*5c47fe09SDavid du Colombier 	uchar	rxseq;
200*5c47fe09SDavid du Colombier };
201*5c47fe09SDavid du Colombier 
202*5c47fe09SDavid du Colombier enum{
203*5c47fe09SDavid du Colombier 	CMauth,
204*5c47fe09SDavid du Colombier 	CMchannel,
205*5c47fe09SDavid du Colombier 	CMcrypt,
206*5c47fe09SDavid du Colombier 	CMessid,
207*5c47fe09SDavid du Colombier 	CMkey1,
208*5c47fe09SDavid du Colombier 	CMkey2,
209*5c47fe09SDavid du Colombier 	CMkey3,
210*5c47fe09SDavid du Colombier 	CMkey4,
211*5c47fe09SDavid du Colombier 	CMrxkey,
212*5c47fe09SDavid du Colombier 	CMrxkey0,
213*5c47fe09SDavid du Colombier 	CMrxkey1,
214*5c47fe09SDavid du Colombier 	CMrxkey2,
215*5c47fe09SDavid du Colombier 	CMrxkey3,
216*5c47fe09SDavid du Colombier 	CMtxkey,
217*5c47fe09SDavid du Colombier 	CMdebug,
218*5c47fe09SDavid du Colombier 	CMjoin,
219*5c47fe09SDavid du Colombier };
220*5c47fe09SDavid du Colombier 
221*5c47fe09SDavid du Colombier static Cmdtab cmds[] = {
222*5c47fe09SDavid du Colombier 	{CMauth,	"auth", 2},
223*5c47fe09SDavid du Colombier 	{CMchannel,	"channel", 2},
224*5c47fe09SDavid du Colombier 	{CMcrypt,	"crypt", 2},
225*5c47fe09SDavid du Colombier 	{CMessid,	"essid", 2},
226*5c47fe09SDavid du Colombier 	{CMkey1,	"key1",	2},
227*5c47fe09SDavid du Colombier 	{CMkey2,	"key1",	2},
228*5c47fe09SDavid du Colombier 	{CMkey3,	"key1",	2},
229*5c47fe09SDavid du Colombier 	{CMkey4,	"key1",	2},
230*5c47fe09SDavid du Colombier 	{CMrxkey,	"rxkey", 3},
231*5c47fe09SDavid du Colombier 	{CMrxkey0,	"rxkey0", 3},
232*5c47fe09SDavid du Colombier 	{CMrxkey1,	"rxkey1", 3},
233*5c47fe09SDavid du Colombier 	{CMrxkey2,	"rxkey2", 3},
234*5c47fe09SDavid du Colombier 	{CMrxkey3,	"rxkey3", 3},
235*5c47fe09SDavid du Colombier 	{CMtxkey,	"txkey", 3},
236*5c47fe09SDavid du Colombier 	{CMdebug,	"debug", 2},
237*5c47fe09SDavid du Colombier 	{CMjoin,	"join", 5},
238*5c47fe09SDavid du Colombier };
239*5c47fe09SDavid du Colombier 
240*5c47fe09SDavid du Colombier typedef struct Sdpcm Sdpcm;
241*5c47fe09SDavid du Colombier typedef struct Cmd Cmd;
242*5c47fe09SDavid du Colombier struct Sdpcm {
243*5c47fe09SDavid du Colombier 	uchar	len[2];
244*5c47fe09SDavid du Colombier 	uchar	lenck[2];
245*5c47fe09SDavid du Colombier 	uchar	seq;
246*5c47fe09SDavid du Colombier 	uchar	chanflg;
247*5c47fe09SDavid du Colombier 	uchar	nextlen;
248*5c47fe09SDavid du Colombier 	uchar	doffset;
249*5c47fe09SDavid du Colombier 	uchar	fcmask;
250*5c47fe09SDavid du Colombier 	uchar	window;
251*5c47fe09SDavid du Colombier 	uchar	version;
252*5c47fe09SDavid du Colombier 	uchar	pad;
253*5c47fe09SDavid du Colombier };
254*5c47fe09SDavid du Colombier 
255*5c47fe09SDavid du Colombier struct Cmd {
256*5c47fe09SDavid du Colombier 	uchar	cmd[4];
257*5c47fe09SDavid du Colombier 	uchar	len[4];
258*5c47fe09SDavid du Colombier 	uchar	flags[2];
259*5c47fe09SDavid du Colombier 	uchar	id[2];
260*5c47fe09SDavid du Colombier 	uchar	status[4];
261*5c47fe09SDavid du Colombier };
262*5c47fe09SDavid du Colombier 
263*5c47fe09SDavid du Colombier static char config40181[] = "bcmdhd.cal.40181";
264*5c47fe09SDavid du Colombier static char config40183[] = "bcmdhd.cal.40183.26MHz";
265*5c47fe09SDavid du Colombier 
266*5c47fe09SDavid du Colombier struct {
267*5c47fe09SDavid du Colombier 	int chipid;
268*5c47fe09SDavid du Colombier 	int chiprev;
269*5c47fe09SDavid du Colombier 	char *fwfile;
270*5c47fe09SDavid du Colombier 	char *cfgfile;
271*5c47fe09SDavid du Colombier 	char *regufile;
272*5c47fe09SDavid du Colombier } firmware[] = {
273*5c47fe09SDavid du Colombier 	{ 0x4330, 3,	"fw_bcm40183b1.bin", config40183, 0 },
274*5c47fe09SDavid du Colombier 	{ 0x4330, 4,	"fw_bcm40183b2.bin", config40183, 0 },
275*5c47fe09SDavid du Colombier 	{ 43362, 0,	"fw_bcm40181a0.bin", config40181, 0 },
276*5c47fe09SDavid du Colombier 	{ 43362, 1,	"fw_bcm40181a2.bin", config40181, 0 },
277*5c47fe09SDavid du Colombier 	{ 43430, 1,	"brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt", 0 },
278*5c47fe09SDavid du Colombier 	{ 43430, 2,	"brcmfmac43436-sdio.bin", "brcmfmac43436-sdio.txt",  "brcmfmac43436-sdio.clm_blob" },
279*5c47fe09SDavid du Colombier 	{ 0x4345, 6, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt", "brcmfmac43455-sdio.clm_blob" },
280*5c47fe09SDavid du Colombier };
281*5c47fe09SDavid du Colombier 
282*5c47fe09SDavid du Colombier static QLock sdiolock;
283*5c47fe09SDavid du Colombier static int iodebug;
284*5c47fe09SDavid du Colombier 
285*5c47fe09SDavid du Colombier static void etherbcmintr(void *);
286*5c47fe09SDavid du Colombier static void bcmevent(Ctlr*, uchar*, int);
287*5c47fe09SDavid du Colombier static void wlscanresult(Ether*, uchar*, int);
288*5c47fe09SDavid du Colombier static void wlsetvar(Ctlr*, char*, void*, int);
289*5c47fe09SDavid du Colombier 
290*5c47fe09SDavid du Colombier static uchar*
put2(uchar * p,short v)291*5c47fe09SDavid du Colombier put2(uchar *p, short v)
292*5c47fe09SDavid du Colombier {
293*5c47fe09SDavid du Colombier 	p[0] = v;
294*5c47fe09SDavid du Colombier 	p[1] = v >> 8;
295*5c47fe09SDavid du Colombier 	return p + 2;
296*5c47fe09SDavid du Colombier }
297*5c47fe09SDavid du Colombier 
298*5c47fe09SDavid du Colombier static uchar*
put4(uchar * p,long v)299*5c47fe09SDavid du Colombier put4(uchar *p, long v)
300*5c47fe09SDavid du Colombier {
301*5c47fe09SDavid du Colombier 	p[0] = v;
302*5c47fe09SDavid du Colombier 	p[1] = v >> 8;
303*5c47fe09SDavid du Colombier 	p[2] = v >> 16;
304*5c47fe09SDavid du Colombier 	p[3] = v >> 24;
305*5c47fe09SDavid du Colombier 	return p + 4;
306*5c47fe09SDavid du Colombier }
307*5c47fe09SDavid du Colombier 
308*5c47fe09SDavid du Colombier static ushort
get2(uchar * p)309*5c47fe09SDavid du Colombier get2(uchar *p)
310*5c47fe09SDavid du Colombier {
311*5c47fe09SDavid du Colombier 	return p[0] | p[1]<<8;
312*5c47fe09SDavid du Colombier }
313*5c47fe09SDavid du Colombier 
314*5c47fe09SDavid du Colombier static ulong
get4(uchar * p)315*5c47fe09SDavid du Colombier get4(uchar *p)
316*5c47fe09SDavid du Colombier {
317*5c47fe09SDavid du Colombier 	return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
318*5c47fe09SDavid du Colombier }
319*5c47fe09SDavid du Colombier 
320*5c47fe09SDavid du Colombier static void
dump(char * s,void * a,int n)321*5c47fe09SDavid du Colombier dump(char *s, void *a, int n)
322*5c47fe09SDavid du Colombier {
323*5c47fe09SDavid du Colombier 	int i;
324*5c47fe09SDavid du Colombier 	uchar *p;
325*5c47fe09SDavid du Colombier 
326*5c47fe09SDavid du Colombier 	p = a;
327*5c47fe09SDavid du Colombier 	print("%s:", s);
328*5c47fe09SDavid du Colombier 	for(i = 0; i < n; i++)
329*5c47fe09SDavid du Colombier 		print("%c%2.2x", i&15? ' ' : '\n', *p++);
330*5c47fe09SDavid du Colombier 	print("\n");
331*5c47fe09SDavid du Colombier }
332*5c47fe09SDavid du Colombier 
333*5c47fe09SDavid du Colombier /*
334*5c47fe09SDavid du Colombier  * SDIO communication with dongle
335*5c47fe09SDavid du Colombier  */
336*5c47fe09SDavid du Colombier static ulong
sdiocmd_locked(int cmd,ulong arg)337*5c47fe09SDavid du Colombier sdiocmd_locked(int cmd, ulong arg)
338*5c47fe09SDavid du Colombier {
339*5c47fe09SDavid du Colombier 	u32int resp[4];
340*5c47fe09SDavid du Colombier 
341*5c47fe09SDavid du Colombier 	sdio.cmd(cmd, arg, resp);
342*5c47fe09SDavid du Colombier 	return resp[0];
343*5c47fe09SDavid du Colombier }
344*5c47fe09SDavid du Colombier 
345*5c47fe09SDavid du Colombier static ulong
sdiocmd(int cmd,ulong arg)346*5c47fe09SDavid du Colombier sdiocmd(int cmd, ulong arg)
347*5c47fe09SDavid du Colombier {
348*5c47fe09SDavid du Colombier 	ulong r;
349*5c47fe09SDavid du Colombier 
350*5c47fe09SDavid du Colombier 	qlock(&sdiolock);
351*5c47fe09SDavid du Colombier 	if(waserror()){
352*5c47fe09SDavid du Colombier 		if(SDIODEBUG) print("sdiocmd error: cmd %d arg %lux\n", cmd, arg);
353*5c47fe09SDavid du Colombier 		qunlock(&sdiolock);
354*5c47fe09SDavid du Colombier 		nexterror();
355*5c47fe09SDavid du Colombier 	}
356*5c47fe09SDavid du Colombier 	r = sdiocmd_locked(cmd, arg);
357*5c47fe09SDavid du Colombier 	qunlock(&sdiolock);
358*5c47fe09SDavid du Colombier 	poperror();
359*5c47fe09SDavid du Colombier 	return r;
360*5c47fe09SDavid du Colombier 
361*5c47fe09SDavid du Colombier }
362*5c47fe09SDavid du Colombier 
363*5c47fe09SDavid du Colombier static ulong
trysdiocmd(int cmd,ulong arg)364*5c47fe09SDavid du Colombier trysdiocmd(int cmd, ulong arg)
365*5c47fe09SDavid du Colombier {
366*5c47fe09SDavid du Colombier 	ulong r;
367*5c47fe09SDavid du Colombier 
368*5c47fe09SDavid du Colombier 	if(waserror())
369*5c47fe09SDavid du Colombier 		return 0;
370*5c47fe09SDavid du Colombier 	r = sdiocmd(cmd, arg);
371*5c47fe09SDavid du Colombier 	poperror();
372*5c47fe09SDavid du Colombier 	return r;
373*5c47fe09SDavid du Colombier }
374*5c47fe09SDavid du Colombier 
375*5c47fe09SDavid du Colombier static int
sdiord(int fn,int addr)376*5c47fe09SDavid du Colombier sdiord(int fn, int addr)
377*5c47fe09SDavid du Colombier {
378*5c47fe09SDavid du Colombier 	int r;
379*5c47fe09SDavid du Colombier 
380*5c47fe09SDavid du Colombier 	r = sdiocmd(IO_RW_DIRECT, (0<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9));
381*5c47fe09SDavid du Colombier 	if(r & 0xCF00){
382*5c47fe09SDavid du Colombier 		print("ether4330: sdiord(%x, %x) fail: %2.2ux %2.2ux\n", fn, addr, (r>>8)&0xFF, r&0xFF);
383*5c47fe09SDavid du Colombier 		error(Eio);
384*5c47fe09SDavid du Colombier 	}
385*5c47fe09SDavid du Colombier 	return r & 0xFF;
386*5c47fe09SDavid du Colombier }
387*5c47fe09SDavid du Colombier 
388*5c47fe09SDavid du Colombier static void
sdiowr(int fn,int addr,int data)389*5c47fe09SDavid du Colombier sdiowr(int fn, int addr, int data)
390*5c47fe09SDavid du Colombier {
391*5c47fe09SDavid du Colombier 	int r;
392*5c47fe09SDavid du Colombier 	int retry;
393*5c47fe09SDavid du Colombier 
394*5c47fe09SDavid du Colombier 	r = 0;
395*5c47fe09SDavid du Colombier 	for(retry = 0; retry < 10; retry++){
396*5c47fe09SDavid du Colombier 		r = sdiocmd(IO_RW_DIRECT, (1<<31)|((fn&7)<<28)|((addr&0x1FFFF)<<9)|(data&0xFF));
397*5c47fe09SDavid du Colombier 		if((r & 0xCF00) == 0)
398*5c47fe09SDavid du Colombier 			return;
399*5c47fe09SDavid du Colombier 	}
400*5c47fe09SDavid du Colombier 	print("ether4330: sdiowr(%x, %x, %x) fail: %2.2ux %2.2ux\n", fn, addr, data, (r>>8)&0xFF, r&0xFF);
401*5c47fe09SDavid du Colombier 	error(Eio);
402*5c47fe09SDavid du Colombier }
403*5c47fe09SDavid du Colombier 
404*5c47fe09SDavid du Colombier static void
sdiorwext(int fn,int write,void * a,int len,int addr,int incr)405*5c47fe09SDavid du Colombier sdiorwext(int fn, int write, void *a, int len, int addr, int incr)
406*5c47fe09SDavid du Colombier {
407*5c47fe09SDavid du Colombier 	int bsize, blk, bcount, m;
408*5c47fe09SDavid du Colombier 
409*5c47fe09SDavid du Colombier 	bsize = fn == Fn2? 512 : 64;
410*5c47fe09SDavid du Colombier 	while(len > 0){
411*5c47fe09SDavid du Colombier 		if(len >= 511*bsize){
412*5c47fe09SDavid du Colombier 			blk = 1;
413*5c47fe09SDavid du Colombier 			bcount = 511;
414*5c47fe09SDavid du Colombier 			m = bcount*bsize;
415*5c47fe09SDavid du Colombier 		}else if(len > bsize){
416*5c47fe09SDavid du Colombier 			blk = 1;
417*5c47fe09SDavid du Colombier 			bcount = len/bsize;
418*5c47fe09SDavid du Colombier 			m = bcount*bsize;
419*5c47fe09SDavid du Colombier 		}else{
420*5c47fe09SDavid du Colombier 			blk = 0;
421*5c47fe09SDavid du Colombier 			bcount = len;
422*5c47fe09SDavid du Colombier 			m = bcount;
423*5c47fe09SDavid du Colombier 		}
424*5c47fe09SDavid du Colombier 		qlock(&sdiolock);
425*5c47fe09SDavid du Colombier 		if(waserror()){
426*5c47fe09SDavid du Colombier 			print("ether4330: sdiorwext fail: %s\n", up->errstr);
427*5c47fe09SDavid du Colombier 			qunlock(&sdiolock);
428*5c47fe09SDavid du Colombier 			nexterror();
429*5c47fe09SDavid du Colombier 		}
430*5c47fe09SDavid du Colombier 		if(blk)
431*5c47fe09SDavid du Colombier 			sdio.iosetup(write, a, bsize, bcount);
432*5c47fe09SDavid du Colombier 		else
433*5c47fe09SDavid du Colombier 			sdio.iosetup(write, a, bcount, 1);
434*5c47fe09SDavid du Colombier 		sdiocmd_locked(IO_RW_EXTENDED,
435*5c47fe09SDavid du Colombier 			write<<31 | (fn&7)<<28 | blk<<27 | incr<<26 | (addr&0x1FFFF)<<9 | (bcount&0x1FF));
436*5c47fe09SDavid du Colombier 		sdio.io(write, a, m);
437*5c47fe09SDavid du Colombier 		qunlock(&sdiolock);
438*5c47fe09SDavid du Colombier 		poperror();
439*5c47fe09SDavid du Colombier 		len -= m;
440*5c47fe09SDavid du Colombier 		a = (char*)a + m;
441*5c47fe09SDavid du Colombier 		if(incr)
442*5c47fe09SDavid du Colombier 			addr += m;
443*5c47fe09SDavid du Colombier 	}
444*5c47fe09SDavid du Colombier }
445*5c47fe09SDavid du Colombier 
446*5c47fe09SDavid du Colombier static void
sdioset(int fn,int addr,int bits)447*5c47fe09SDavid du Colombier sdioset(int fn, int addr, int bits)
448*5c47fe09SDavid du Colombier {
449*5c47fe09SDavid du Colombier 	sdiowr(fn, addr, sdiord(fn, addr) | bits);
450*5c47fe09SDavid du Colombier }
451*5c47fe09SDavid du Colombier 
452*5c47fe09SDavid du Colombier static void
sdioinit(void)453*5c47fe09SDavid du Colombier sdioinit(void)
454*5c47fe09SDavid du Colombier {
455*5c47fe09SDavid du Colombier 	ulong ocr, rca;
456*5c47fe09SDavid du Colombier 	int i;
457*5c47fe09SDavid du Colombier 
458*5c47fe09SDavid du Colombier 	/* disconnect emmc from SD card (connect sdhost instead) */
459*5c47fe09SDavid du Colombier 	for(i = 48; i <= 53; i++)
460*5c47fe09SDavid du Colombier 		gpiosel(i, Alt0);
461*5c47fe09SDavid du Colombier 	/* connect emmc to wifi */
462*5c47fe09SDavid du Colombier 	for(i = 34; i <= 39; i++){
463*5c47fe09SDavid du Colombier 		gpiosel(i, Alt3);
464*5c47fe09SDavid du Colombier 		if(i == 34)
465*5c47fe09SDavid du Colombier 			gpiopulloff(i);
466*5c47fe09SDavid du Colombier 		else
467*5c47fe09SDavid du Colombier 			gpiopullup(i);
468*5c47fe09SDavid du Colombier 	}
469*5c47fe09SDavid du Colombier 	sdio.init();
470*5c47fe09SDavid du Colombier 	sdio.enable();
471*5c47fe09SDavid du Colombier 	sdiocmd(GO_IDLE_STATE, 0);
472*5c47fe09SDavid du Colombier 	ocr = trysdiocmd(IO_SEND_OP_COND, 0);
473*5c47fe09SDavid du Colombier 	i = 0;
474*5c47fe09SDavid du Colombier 	while((ocr & (1<<31)) == 0){
475*5c47fe09SDavid du Colombier 		if(++i > 5){
476*5c47fe09SDavid du Colombier 			print("ether4330: no response to sdio access: ocr = %lux\n", ocr);
477*5c47fe09SDavid du Colombier 			error(Eio);
478*5c47fe09SDavid du Colombier 		}
479*5c47fe09SDavid du Colombier 		ocr = trysdiocmd(IO_SEND_OP_COND, V3_3);
480*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 100);
481*5c47fe09SDavid du Colombier 	}
482*5c47fe09SDavid du Colombier 	rca = sdiocmd(SEND_RELATIVE_ADDR, 0) >> Rcashift;
483*5c47fe09SDavid du Colombier 	sdiocmd(SELECT_CARD, rca << Rcashift);
484*5c47fe09SDavid du Colombier 	sdioset(Fn0, Highspeed, 2);
485*5c47fe09SDavid du Colombier 	sdioset(Fn0, Busifc, 2);	/* bus width 4 */
486*5c47fe09SDavid du Colombier 	sdiowr(Fn0, Fbr1+Blksize, 64);
487*5c47fe09SDavid du Colombier 	sdiowr(Fn0, Fbr1+Blksize+1, 64>>8);
488*5c47fe09SDavid du Colombier 	sdiowr(Fn0, Fbr2+Blksize, 512);
489*5c47fe09SDavid du Colombier 	sdiowr(Fn0, Fbr2+Blksize+1, 512>>8);
490*5c47fe09SDavid du Colombier 	sdioset(Fn0, Ioenable, 1<<Fn1);
491*5c47fe09SDavid du Colombier 	sdiowr(Fn0, Intenable, 0);
492*5c47fe09SDavid du Colombier 	for(i = 0; !(sdiord(Fn0, Ioready) & 1<<Fn1); i++){
493*5c47fe09SDavid du Colombier 		if(i == 10){
494*5c47fe09SDavid du Colombier 			print("ether4330: can't enable SDIO function\n");
495*5c47fe09SDavid du Colombier 			error(Eio);
496*5c47fe09SDavid du Colombier 		}
497*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 100);
498*5c47fe09SDavid du Colombier 	}
499*5c47fe09SDavid du Colombier }
500*5c47fe09SDavid du Colombier 
501*5c47fe09SDavid du Colombier static void
sdioreset(void)502*5c47fe09SDavid du Colombier sdioreset(void)
503*5c47fe09SDavid du Colombier {
504*5c47fe09SDavid du Colombier 	sdiowr(Fn0, Ioabort, 1<<3);	/* reset */
505*5c47fe09SDavid du Colombier }
506*5c47fe09SDavid du Colombier 
507*5c47fe09SDavid du Colombier static void
sdioabort(int fn)508*5c47fe09SDavid du Colombier sdioabort(int fn)
509*5c47fe09SDavid du Colombier {
510*5c47fe09SDavid du Colombier 	sdiowr(Fn0, Ioabort, fn);
511*5c47fe09SDavid du Colombier }
512*5c47fe09SDavid du Colombier 
513*5c47fe09SDavid du Colombier /*
514*5c47fe09SDavid du Colombier  * Chip register and memory access via SDIO
515*5c47fe09SDavid du Colombier  */
516*5c47fe09SDavid du Colombier 
517*5c47fe09SDavid du Colombier static void
cfgw(ulong off,int val)518*5c47fe09SDavid du Colombier cfgw(ulong off, int val)
519*5c47fe09SDavid du Colombier {
520*5c47fe09SDavid du Colombier 	sdiowr(Fn1, off, val);
521*5c47fe09SDavid du Colombier }
522*5c47fe09SDavid du Colombier 
523*5c47fe09SDavid du Colombier static int
cfgr(ulong off)524*5c47fe09SDavid du Colombier cfgr(ulong off)
525*5c47fe09SDavid du Colombier {
526*5c47fe09SDavid du Colombier 	return sdiord(Fn1, off);
527*5c47fe09SDavid du Colombier }
528*5c47fe09SDavid du Colombier 
529*5c47fe09SDavid du Colombier static ulong
cfgreadl(int fn,ulong off)530*5c47fe09SDavid du Colombier cfgreadl(int fn, ulong off)
531*5c47fe09SDavid du Colombier {
532*5c47fe09SDavid du Colombier 	uchar cbuf[2*CACHELINESZ];
533*5c47fe09SDavid du Colombier 	uchar *p;
534*5c47fe09SDavid du Colombier 
535*5c47fe09SDavid du Colombier 	p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ);
536*5c47fe09SDavid du Colombier 	memset(p, 0, 4);
537*5c47fe09SDavid du Colombier 	sdiorwext(fn, 0, p, 4, off|Sb32bit, 1);
538*5c47fe09SDavid du Colombier 	if(SDIODEBUG) print("cfgreadl %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]);
539*5c47fe09SDavid du Colombier 	return p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
540*5c47fe09SDavid du Colombier }
541*5c47fe09SDavid du Colombier 
542*5c47fe09SDavid du Colombier static void
cfgwritel(int fn,ulong off,u32int data)543*5c47fe09SDavid du Colombier cfgwritel(int fn, ulong off, u32int data)
544*5c47fe09SDavid du Colombier {
545*5c47fe09SDavid du Colombier 	uchar cbuf[2*CACHELINESZ];
546*5c47fe09SDavid du Colombier 	uchar *p;
547*5c47fe09SDavid du Colombier 	int retry;
548*5c47fe09SDavid du Colombier 
549*5c47fe09SDavid du Colombier 	p = (uchar*)ROUND((uintptr)cbuf, CACHELINESZ);
550*5c47fe09SDavid du Colombier 	put4(p, data);
551*5c47fe09SDavid du Colombier 	if(SDIODEBUG) print("cfgwritel %lux: %2.2x %2.2x %2.2x %2.2x\n", off, p[0], p[1], p[2], p[3]);
552*5c47fe09SDavid du Colombier 	retry = 0;
553*5c47fe09SDavid du Colombier 	while(waserror()){
554*5c47fe09SDavid du Colombier 		print("ether4330: cfgwritel retry %lux %ux\n", off, data);
555*5c47fe09SDavid du Colombier 		sdioabort(fn);
556*5c47fe09SDavid du Colombier 		if(++retry == 3)
557*5c47fe09SDavid du Colombier 			nexterror();
558*5c47fe09SDavid du Colombier 	}
559*5c47fe09SDavid du Colombier 	sdiorwext(fn, 1, p, 4, off|Sb32bit, 1);
560*5c47fe09SDavid du Colombier 	poperror();
561*5c47fe09SDavid du Colombier }
562*5c47fe09SDavid du Colombier 
563*5c47fe09SDavid du Colombier static void
sbwindow(ulong addr)564*5c47fe09SDavid du Colombier sbwindow(ulong addr)
565*5c47fe09SDavid du Colombier {
566*5c47fe09SDavid du Colombier 	addr &= ~(Sbwsize-1);
567*5c47fe09SDavid du Colombier 	cfgw(Sbaddr, addr>>8);
568*5c47fe09SDavid du Colombier 	cfgw(Sbaddr+1, addr>>16);
569*5c47fe09SDavid du Colombier 	cfgw(Sbaddr+2, addr>>24);
570*5c47fe09SDavid du Colombier }
571*5c47fe09SDavid du Colombier 
572*5c47fe09SDavid du Colombier static void
sbrw(int fn,int write,uchar * buf,int len,ulong off)573*5c47fe09SDavid du Colombier sbrw(int fn, int write, uchar *buf, int len, ulong off)
574*5c47fe09SDavid du Colombier {
575*5c47fe09SDavid du Colombier 	int n;
576*5c47fe09SDavid du Colombier 	USED(fn);
577*5c47fe09SDavid du Colombier 
578*5c47fe09SDavid du Colombier 	if(waserror()){
579*5c47fe09SDavid du Colombier 		print("ether4330: sbrw err off %lux len %ud\n", off, len);
580*5c47fe09SDavid du Colombier 		nexterror();
581*5c47fe09SDavid du Colombier 	}
582*5c47fe09SDavid du Colombier 	if(write){
583*5c47fe09SDavid du Colombier 		if(len >= 4){
584*5c47fe09SDavid du Colombier 			n = len;
585*5c47fe09SDavid du Colombier 			n &= ~3;
586*5c47fe09SDavid du Colombier 			sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1);
587*5c47fe09SDavid du Colombier 			off += n;
588*5c47fe09SDavid du Colombier 			buf += n;
589*5c47fe09SDavid du Colombier 			len -= n;
590*5c47fe09SDavid du Colombier 		}
591*5c47fe09SDavid du Colombier 		while(len > 0){
592*5c47fe09SDavid du Colombier 			sdiowr(Fn1, off|Sb32bit, *buf);
593*5c47fe09SDavid du Colombier 			off++;
594*5c47fe09SDavid du Colombier 			buf++;
595*5c47fe09SDavid du Colombier 			len--;
596*5c47fe09SDavid du Colombier 		}
597*5c47fe09SDavid du Colombier 	}else{
598*5c47fe09SDavid du Colombier 		if(len >= 4){
599*5c47fe09SDavid du Colombier 			n = len;
600*5c47fe09SDavid du Colombier 			n &= ~3;
601*5c47fe09SDavid du Colombier 			sdiorwext(Fn1, write, buf, n, off|Sb32bit, 1);
602*5c47fe09SDavid du Colombier 			off += n;
603*5c47fe09SDavid du Colombier 			buf += n;
604*5c47fe09SDavid du Colombier 			len -= n;
605*5c47fe09SDavid du Colombier 		}
606*5c47fe09SDavid du Colombier 		while(len > 0){
607*5c47fe09SDavid du Colombier 			*buf = sdiord(Fn1, off|Sb32bit);
608*5c47fe09SDavid du Colombier 			off++;
609*5c47fe09SDavid du Colombier 			buf++;
610*5c47fe09SDavid du Colombier 			len--;
611*5c47fe09SDavid du Colombier 		}
612*5c47fe09SDavid du Colombier 	}
613*5c47fe09SDavid du Colombier 	poperror();
614*5c47fe09SDavid du Colombier }
615*5c47fe09SDavid du Colombier 
616*5c47fe09SDavid du Colombier static void
sbmem(int write,uchar * buf,int len,ulong off)617*5c47fe09SDavid du Colombier sbmem(int write, uchar *buf, int len, ulong off)
618*5c47fe09SDavid du Colombier {
619*5c47fe09SDavid du Colombier 	ulong n;
620*5c47fe09SDavid du Colombier 
621*5c47fe09SDavid du Colombier 	n = ROUNDUP(off, Sbwsize) - off;
622*5c47fe09SDavid du Colombier 	if(n == 0)
623*5c47fe09SDavid du Colombier 		n = Sbwsize;
624*5c47fe09SDavid du Colombier 	while(len > 0){
625*5c47fe09SDavid du Colombier 		if(n > len)
626*5c47fe09SDavid du Colombier 			n = len;
627*5c47fe09SDavid du Colombier 		sbwindow(off);
628*5c47fe09SDavid du Colombier 		sbrw(Fn1, write, buf, n, off & (Sbwsize-1));
629*5c47fe09SDavid du Colombier 		off += n;
630*5c47fe09SDavid du Colombier 		buf += n;
631*5c47fe09SDavid du Colombier 		len -= n;
632*5c47fe09SDavid du Colombier 		n = Sbwsize;
633*5c47fe09SDavid du Colombier 	}
634*5c47fe09SDavid du Colombier }
635*5c47fe09SDavid du Colombier 
636*5c47fe09SDavid du Colombier static void
packetrw(int write,uchar * buf,int len)637*5c47fe09SDavid du Colombier packetrw(int write, uchar *buf, int len)
638*5c47fe09SDavid du Colombier {
639*5c47fe09SDavid du Colombier 	int n;
640*5c47fe09SDavid du Colombier 	int retry;
641*5c47fe09SDavid du Colombier 
642*5c47fe09SDavid du Colombier 	n = 2048;
643*5c47fe09SDavid du Colombier 	while(len > 0){
644*5c47fe09SDavid du Colombier 		if(n > len)
645*5c47fe09SDavid du Colombier 			n = ROUND(len, 4);
646*5c47fe09SDavid du Colombier 		retry = 0;
647*5c47fe09SDavid du Colombier 		while(waserror()){
648*5c47fe09SDavid du Colombier 			sdioabort(Fn2);
649*5c47fe09SDavid du Colombier 			if(++retry == 3)
650*5c47fe09SDavid du Colombier 				nexterror();
651*5c47fe09SDavid du Colombier 		}
652*5c47fe09SDavid du Colombier 		sdiorwext(Fn2, write, buf, n, Enumbase, 0);
653*5c47fe09SDavid du Colombier 		poperror();
654*5c47fe09SDavid du Colombier 		buf += n;
655*5c47fe09SDavid du Colombier 		len -= n;
656*5c47fe09SDavid du Colombier 	}
657*5c47fe09SDavid du Colombier }
658*5c47fe09SDavid du Colombier 
659*5c47fe09SDavid du Colombier /*
660*5c47fe09SDavid du Colombier  * Configuration and control of chip cores via Silicon Backplane
661*5c47fe09SDavid du Colombier  */
662*5c47fe09SDavid du Colombier 
663*5c47fe09SDavid du Colombier static void
sbdisable(ulong regs,int pre,int ioctl)664*5c47fe09SDavid du Colombier sbdisable(ulong regs, int pre, int ioctl)
665*5c47fe09SDavid du Colombier {
666*5c47fe09SDavid du Colombier 	sbwindow(regs);
667*5c47fe09SDavid du Colombier 	if((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){
668*5c47fe09SDavid du Colombier 		cfgwritel(Fn1, regs + Ioctrl, 3|ioctl);
669*5c47fe09SDavid du Colombier 		cfgreadl(Fn1, regs + Ioctrl);
670*5c47fe09SDavid du Colombier 		return;
671*5c47fe09SDavid du Colombier 	}
672*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, regs + Ioctrl, 3|pre);
673*5c47fe09SDavid du Colombier 	cfgreadl(Fn1, regs + Ioctrl);
674*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, regs + Resetctrl, 1);
675*5c47fe09SDavid du Colombier 	microdelay(10);
676*5c47fe09SDavid du Colombier 	while((cfgreadl(Fn1, regs + Resetctrl) & 1) == 0)
677*5c47fe09SDavid du Colombier 		;
678*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, regs + Ioctrl, 3|ioctl);
679*5c47fe09SDavid du Colombier 	cfgreadl(Fn1, regs + Ioctrl);
680*5c47fe09SDavid du Colombier }
681*5c47fe09SDavid du Colombier 
682*5c47fe09SDavid du Colombier static void
sbreset(ulong regs,int pre,int ioctl)683*5c47fe09SDavid du Colombier sbreset(ulong regs, int pre, int ioctl)
684*5c47fe09SDavid du Colombier {
685*5c47fe09SDavid du Colombier 	sbdisable(regs, pre, ioctl);
686*5c47fe09SDavid du Colombier 	sbwindow(regs);
687*5c47fe09SDavid du Colombier 	if(SBDEBUG) print("sbreset %#p %#lux %#lux ->", regs,
688*5c47fe09SDavid du Colombier 		cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl));
689*5c47fe09SDavid du Colombier 	while((cfgreadl(Fn1, regs + Resetctrl) & 1) != 0){
690*5c47fe09SDavid du Colombier 		cfgwritel(Fn1, regs + Resetctrl, 0);
691*5c47fe09SDavid du Colombier 		microdelay(40);
692*5c47fe09SDavid du Colombier 	}
693*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, regs + Ioctrl, 1|ioctl);
694*5c47fe09SDavid du Colombier 	cfgreadl(Fn1, regs + Ioctrl);
695*5c47fe09SDavid du Colombier 	if(SBDEBUG) print("%#lux %#lux\n",
696*5c47fe09SDavid du Colombier 		cfgreadl(Fn1, regs+Ioctrl), cfgreadl(Fn1, regs+Resetctrl));
697*5c47fe09SDavid du Colombier }
698*5c47fe09SDavid du Colombier 
699*5c47fe09SDavid du Colombier static void
corescan(Ctlr * ctl,ulong r)700*5c47fe09SDavid du Colombier corescan(Ctlr *ctl, ulong r)
701*5c47fe09SDavid du Colombier {
702*5c47fe09SDavid du Colombier 	uchar *buf;
703*5c47fe09SDavid du Colombier 	int i, coreid, corerev;
704*5c47fe09SDavid du Colombier 	ulong addr;
705*5c47fe09SDavid du Colombier 
706*5c47fe09SDavid du Colombier 	buf = sdmalloc(Corescansz);
707*5c47fe09SDavid du Colombier 	if(buf == nil)
708*5c47fe09SDavid du Colombier 		error(Enomem);
709*5c47fe09SDavid du Colombier 	sbmem(0, buf, Corescansz, r);
710*5c47fe09SDavid du Colombier 	coreid = 0;
711*5c47fe09SDavid du Colombier 	corerev = 0;
712*5c47fe09SDavid du Colombier 	for(i = 0; i < Corescansz; i += 4){
713*5c47fe09SDavid du Colombier 		switch(buf[i]&0xF){
714*5c47fe09SDavid du Colombier 		case 0xF:	/* end */
715*5c47fe09SDavid du Colombier 			sdfree(buf);
716*5c47fe09SDavid du Colombier 			return;
717*5c47fe09SDavid du Colombier 		case 0x1:	/* core info */
718*5c47fe09SDavid du Colombier 			if((buf[i+4]&0xF) != 0x1)
719*5c47fe09SDavid du Colombier 				break;
720*5c47fe09SDavid du Colombier 			coreid = (buf[i+1] | buf[i+2]<<8) & 0xFFF;
721*5c47fe09SDavid du Colombier 			i += 4;
722*5c47fe09SDavid du Colombier 			corerev = buf[i+3];
723*5c47fe09SDavid du Colombier 			break;
724*5c47fe09SDavid du Colombier 		case 0x05:	/* address */
725*5c47fe09SDavid du Colombier 			addr = buf[i+1]<<8 | buf[i+2]<<16 | buf[i+3]<<24;
726*5c47fe09SDavid du Colombier 			addr &= ~0xFFF;
727*5c47fe09SDavid du Colombier 			if(SBDEBUG) print("core %x %s %#p\n", coreid, buf[i]&0xC0? "ctl" : "mem", addr);
728*5c47fe09SDavid du Colombier 			switch(coreid){
729*5c47fe09SDavid du Colombier 			case 0x800:
730*5c47fe09SDavid du Colombier 				if((buf[i] & 0xC0) == 0)
731*5c47fe09SDavid du Colombier 					ctl->chipcommon = addr;
732*5c47fe09SDavid du Colombier 				break;
733*5c47fe09SDavid du Colombier 			case ARMcm3:
734*5c47fe09SDavid du Colombier 			case ARM7tdmi:
735*5c47fe09SDavid du Colombier 			case ARMcr4:
736*5c47fe09SDavid du Colombier 				ctl->armcore = coreid;
737*5c47fe09SDavid du Colombier 				if(buf[i] & 0xC0){
738*5c47fe09SDavid du Colombier 					if(ctl->armctl == 0)
739*5c47fe09SDavid du Colombier 						ctl->armctl = addr;
740*5c47fe09SDavid du Colombier 				}else{
741*5c47fe09SDavid du Colombier 					if(ctl->armregs == 0)
742*5c47fe09SDavid du Colombier 						ctl->armregs = addr;
743*5c47fe09SDavid du Colombier 				}
744*5c47fe09SDavid du Colombier 				break;
745*5c47fe09SDavid du Colombier 			case 0x80E:
746*5c47fe09SDavid du Colombier 				if(buf[i] & 0xC0)
747*5c47fe09SDavid du Colombier 					ctl->socramctl = addr;
748*5c47fe09SDavid du Colombier 				else if(ctl->socramregs == 0)
749*5c47fe09SDavid du Colombier 					ctl->socramregs = addr;
750*5c47fe09SDavid du Colombier 				ctl->socramrev = corerev;
751*5c47fe09SDavid du Colombier 				break;
752*5c47fe09SDavid du Colombier 			case 0x829:
753*5c47fe09SDavid du Colombier 				if((buf[i] & 0xC0) == 0)
754*5c47fe09SDavid du Colombier 					ctl->sdregs = addr;
755*5c47fe09SDavid du Colombier 				ctl->sdiorev = corerev;
756*5c47fe09SDavid du Colombier 				break;
757*5c47fe09SDavid du Colombier 			case 0x812:
758*5c47fe09SDavid du Colombier 				if(buf[i] & 0xC0)
759*5c47fe09SDavid du Colombier 					ctl->d11ctl = addr;
760*5c47fe09SDavid du Colombier 				break;
761*5c47fe09SDavid du Colombier 			}
762*5c47fe09SDavid du Colombier 		}
763*5c47fe09SDavid du Colombier 	}
764*5c47fe09SDavid du Colombier 	sdfree(buf);
765*5c47fe09SDavid du Colombier }
766*5c47fe09SDavid du Colombier 
767*5c47fe09SDavid du Colombier static void
ramscan(Ctlr * ctl)768*5c47fe09SDavid du Colombier ramscan(Ctlr *ctl)
769*5c47fe09SDavid du Colombier {
770*5c47fe09SDavid du Colombier 	ulong r, n, size;
771*5c47fe09SDavid du Colombier 	int banks, i;
772*5c47fe09SDavid du Colombier 
773*5c47fe09SDavid du Colombier 	if(ctl->armcore == ARMcr4){
774*5c47fe09SDavid du Colombier 		r = ctl->armregs;
775*5c47fe09SDavid du Colombier 		sbwindow(r);
776*5c47fe09SDavid du Colombier 		n = cfgreadl(Fn1, r + Cr4Cap);
777*5c47fe09SDavid du Colombier 		if(SBDEBUG) print("cr4 banks %lux\n", n);
778*5c47fe09SDavid du Colombier 		banks = ((n>>4) & 0xF) + (n & 0xF);
779*5c47fe09SDavid du Colombier 		size = 0;
780*5c47fe09SDavid du Colombier 		for(i = 0; i < banks; i++){
781*5c47fe09SDavid du Colombier 			cfgwritel(Fn1, r + Cr4Bankidx, i);
782*5c47fe09SDavid du Colombier 			n = cfgreadl(Fn1, r + Cr4Bankinfo);
783*5c47fe09SDavid du Colombier 			if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1));
784*5c47fe09SDavid du Colombier 			size += 8192 * ((n & 0x3F) + 1);
785*5c47fe09SDavid du Colombier 		}
786*5c47fe09SDavid du Colombier 		ctl->socramsize = size;
787*5c47fe09SDavid du Colombier 		ctl->rambase = 0x198000;
788*5c47fe09SDavid du Colombier 		return;
789*5c47fe09SDavid du Colombier 	}
790*5c47fe09SDavid du Colombier 	if(ctl->socramrev <= 7 || ctl->socramrev == 12){
791*5c47fe09SDavid du Colombier 		print("ether4330: SOCRAM rev %d not supported\n", ctl->socramrev);
792*5c47fe09SDavid du Colombier 		error(Eio);
793*5c47fe09SDavid du Colombier 	}
794*5c47fe09SDavid du Colombier 	sbreset(ctl->socramctl, 0, 0);
795*5c47fe09SDavid du Colombier 	r = ctl->socramregs;
796*5c47fe09SDavid du Colombier 	sbwindow(r);
797*5c47fe09SDavid du Colombier 	n = cfgreadl(Fn1, r + Coreinfo);
798*5c47fe09SDavid du Colombier 	if(SBDEBUG) print("socramrev %d coreinfo %lux\n", ctl->socramrev, n);
799*5c47fe09SDavid du Colombier 	banks = (n>>4) & 0xF;
800*5c47fe09SDavid du Colombier 	size = 0;
801*5c47fe09SDavid du Colombier 	for(i = 0; i < banks; i++){
802*5c47fe09SDavid du Colombier 		cfgwritel(Fn1, r + Bankidx, i);
803*5c47fe09SDavid du Colombier 		n = cfgreadl(Fn1, r + Bankinfo);
804*5c47fe09SDavid du Colombier 		if(SBDEBUG) print("bank %d reg %lux size %lud\n", i, n, 8192 * ((n & 0x3F) + 1));
805*5c47fe09SDavid du Colombier 		size += 8192 * ((n & 0x3F) + 1);
806*5c47fe09SDavid du Colombier 	}
807*5c47fe09SDavid du Colombier 	ctl->socramsize = size;
808*5c47fe09SDavid du Colombier 	ctl->rambase = 0;
809*5c47fe09SDavid du Colombier 	if(ctl->chipid == 43430){
810*5c47fe09SDavid du Colombier 		cfgwritel(Fn1, r + Bankidx, 3);
811*5c47fe09SDavid du Colombier 		cfgwritel(Fn1, r + Bankpda, 0);
812*5c47fe09SDavid du Colombier 	}
813*5c47fe09SDavid du Colombier }
814*5c47fe09SDavid du Colombier 
815*5c47fe09SDavid du Colombier static void
sbinit(Ctlr * ctl)816*5c47fe09SDavid du Colombier sbinit(Ctlr *ctl)
817*5c47fe09SDavid du Colombier {
818*5c47fe09SDavid du Colombier 	ulong r;
819*5c47fe09SDavid du Colombier 	int chipid;
820*5c47fe09SDavid du Colombier 	char buf[16];
821*5c47fe09SDavid du Colombier 
822*5c47fe09SDavid du Colombier 	sbwindow(Enumbase);
823*5c47fe09SDavid du Colombier 	r = cfgreadl(Fn1, Enumbase);
824*5c47fe09SDavid du Colombier 	chipid = r & 0xFFFF;
825*5c47fe09SDavid du Colombier 	sprint(buf, chipid > 43000 ? "%d" : "%#x", chipid);
826*5c47fe09SDavid du Colombier 	print("ether4330: chip %s rev %ld type %ld\n", buf, (r>>16)&0xF, (r>>28)&0xF);
827*5c47fe09SDavid du Colombier 	switch(chipid){
828*5c47fe09SDavid du Colombier 		case 0x4330:
829*5c47fe09SDavid du Colombier 		case 43362:
830*5c47fe09SDavid du Colombier 		case 43430:
831*5c47fe09SDavid du Colombier 		case 0x4345:
832*5c47fe09SDavid du Colombier 			ctl->chipid = chipid;
833*5c47fe09SDavid du Colombier 			ctl->chiprev = (r>>16)&0xF;
834*5c47fe09SDavid du Colombier 			break;
835*5c47fe09SDavid du Colombier 		default:
836*5c47fe09SDavid du Colombier 			print("ether4330: chipid %#x (%d) not supported\n", chipid, chipid);
837*5c47fe09SDavid du Colombier 			error(Eio);
838*5c47fe09SDavid du Colombier 	}
839*5c47fe09SDavid du Colombier 	r = cfgreadl(Fn1, Enumbase + 63*4);
840*5c47fe09SDavid du Colombier 	corescan(ctl, r);
841*5c47fe09SDavid du Colombier 	if(ctl->armctl == 0 || ctl->d11ctl == 0 ||
842*5c47fe09SDavid du Colombier 	   (ctl->armcore == ARMcm3 && (ctl->socramctl == 0 || ctl->socramregs == 0)))
843*5c47fe09SDavid du Colombier 		error("corescan didn't find essential cores\n");
844*5c47fe09SDavid du Colombier 	if(ctl->armcore == ARMcr4)
845*5c47fe09SDavid du Colombier 		sbreset(ctl->armctl, Cr4Cpuhalt, Cr4Cpuhalt);
846*5c47fe09SDavid du Colombier 	else
847*5c47fe09SDavid du Colombier 		sbdisable(ctl->armctl, 0, 0);
848*5c47fe09SDavid du Colombier 	sbreset(ctl->d11ctl, 8|4, 4);
849*5c47fe09SDavid du Colombier 	ramscan(ctl);
850*5c47fe09SDavid du Colombier 	if(SBDEBUG) print("ARM %#p D11 %#p SOCRAM %#p,%#p %lud bytes @ %#p\n",
851*5c47fe09SDavid du Colombier 		ctl->armctl, ctl->d11ctl, ctl->socramctl, ctl->socramregs, ctl->socramsize, ctl->rambase);
852*5c47fe09SDavid du Colombier 	cfgw(Clkcsr, 0);
853*5c47fe09SDavid du Colombier 	microdelay(10);
854*5c47fe09SDavid du Colombier 	if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
855*5c47fe09SDavid du Colombier 	cfgw(Clkcsr, Nohwreq | ReqALP);
856*5c47fe09SDavid du Colombier 	while((cfgr(Clkcsr) & (HTavail|ALPavail)) == 0)
857*5c47fe09SDavid du Colombier 		microdelay(10);
858*5c47fe09SDavid du Colombier 	cfgw(Clkcsr, Nohwreq | ForceALP);
859*5c47fe09SDavid du Colombier 	microdelay(65);
860*5c47fe09SDavid du Colombier 	if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
861*5c47fe09SDavid du Colombier 	cfgw(Pullups, 0);
862*5c47fe09SDavid du Colombier 	sbwindow(ctl->chipcommon);
863*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, ctl->chipcommon + Gpiopullup, 0);
864*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, ctl->chipcommon + Gpiopulldown, 0);
865*5c47fe09SDavid du Colombier 	if(ctl->chipid != 0x4330 && ctl->chipid != 43362)
866*5c47fe09SDavid du Colombier 		return;
867*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, ctl->chipcommon + Chipctladdr, 1);
868*5c47fe09SDavid du Colombier 	if(cfgreadl(Fn1, ctl->chipcommon + Chipctladdr) != 1)
869*5c47fe09SDavid du Colombier 		print("ether4330: can't set Chipctladdr\n");
870*5c47fe09SDavid du Colombier 	else{
871*5c47fe09SDavid du Colombier 		r = cfgreadl(Fn1, ctl->chipcommon + Chipctldata);
872*5c47fe09SDavid du Colombier 		if(SBDEBUG) print("chipcommon PMU (%lux) %lux", cfgreadl(Fn1, ctl->chipcommon + Chipctladdr), r);
873*5c47fe09SDavid du Colombier 		/* set SDIO drive strength >= 6mA */
874*5c47fe09SDavid du Colombier 		r &= ~0x3800;
875*5c47fe09SDavid du Colombier 		if(ctl->chipid == 0x4330)
876*5c47fe09SDavid du Colombier 			r |= 3<<11;
877*5c47fe09SDavid du Colombier 		else
878*5c47fe09SDavid du Colombier 			r |= 7<<11;
879*5c47fe09SDavid du Colombier 		cfgwritel(Fn1, ctl->chipcommon + Chipctldata, r);
880*5c47fe09SDavid du Colombier 		if(SBDEBUG) print("-> %lux (= %lux)\n", r, cfgreadl(Fn1, ctl->chipcommon + Chipctldata));
881*5c47fe09SDavid du Colombier 	}
882*5c47fe09SDavid du Colombier }
883*5c47fe09SDavid du Colombier 
884*5c47fe09SDavid du Colombier static void
sbenable(Ctlr * ctl)885*5c47fe09SDavid du Colombier sbenable(Ctlr *ctl)
886*5c47fe09SDavid du Colombier {
887*5c47fe09SDavid du Colombier 	int i;
888*5c47fe09SDavid du Colombier 
889*5c47fe09SDavid du Colombier 	if(SBDEBUG) print("enabling HT clock...");
890*5c47fe09SDavid du Colombier 	cfgw(Clkcsr, 0);
891*5c47fe09SDavid du Colombier 	delay(1);
892*5c47fe09SDavid du Colombier 	cfgw(Clkcsr, ReqHT);
893*5c47fe09SDavid du Colombier 	for(i = 0; (cfgr(Clkcsr) & HTavail) == 0; i++){
894*5c47fe09SDavid du Colombier 		if(i == 50){
895*5c47fe09SDavid du Colombier 			print("ether4330: can't enable HT clock: csr %x\n", cfgr(Clkcsr));
896*5c47fe09SDavid du Colombier 			error(Eio);
897*5c47fe09SDavid du Colombier 		}
898*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 100);
899*5c47fe09SDavid du Colombier 	}
900*5c47fe09SDavid du Colombier 	cfgw(Clkcsr, cfgr(Clkcsr) | ForceHT);
901*5c47fe09SDavid du Colombier 	delay(10);
902*5c47fe09SDavid du Colombier 	if(SBDEBUG) print("chipclk: %x\n", cfgr(Clkcsr));
903*5c47fe09SDavid du Colombier 	sbwindow(ctl->sdregs);
904*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, ctl->sdregs + Sbmboxdata, 4 << 16);	/* protocol version */
905*5c47fe09SDavid du Colombier 	cfgwritel(Fn1, ctl->sdregs + Intmask, FrameInt | MailboxInt | Fcchange);
906*5c47fe09SDavid du Colombier 	sdioset(Fn0, Ioenable, 1<<Fn2);
907*5c47fe09SDavid du Colombier 	for(i = 0; !(sdiord(Fn0, Ioready) & 1<<Fn2); i++){
908*5c47fe09SDavid du Colombier 		if(i == 10){
909*5c47fe09SDavid du Colombier 			print("ether4330: can't enable SDIO function 2 - ioready %x\n", sdiord(Fn0, Ioready));
910*5c47fe09SDavid du Colombier 			error(Eio);
911*5c47fe09SDavid du Colombier 		}
912*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 100);
913*5c47fe09SDavid du Colombier 	}
914*5c47fe09SDavid du Colombier 	sdiowr(Fn0, Intenable, (1<<Fn1) | (1<<Fn2) | 1);
915*5c47fe09SDavid du Colombier }
916*5c47fe09SDavid du Colombier 
917*5c47fe09SDavid du Colombier 
918*5c47fe09SDavid du Colombier /*
919*5c47fe09SDavid du Colombier  * Firmware and config file uploading
920*5c47fe09SDavid du Colombier  */
921*5c47fe09SDavid du Colombier 
922*5c47fe09SDavid du Colombier /*
923*5c47fe09SDavid du Colombier  * Condense config file contents (in buffer buf with length n)
924*5c47fe09SDavid du Colombier  * to 'var=value\0' list for firmware:
925*5c47fe09SDavid du Colombier  *	- remove comments (starting with '#') and blank lines
926*5c47fe09SDavid du Colombier  *	- remove carriage returns
927*5c47fe09SDavid du Colombier  *	- convert newlines to nulls
928*5c47fe09SDavid du Colombier  *	- mark end with two nulls
929*5c47fe09SDavid du Colombier  *	- pad with nulls to multiple of 4 bytes total length
930*5c47fe09SDavid du Colombier  */
931*5c47fe09SDavid du Colombier static int
condense(uchar * buf,int n)932*5c47fe09SDavid du Colombier condense(uchar *buf, int n)
933*5c47fe09SDavid du Colombier {
934*5c47fe09SDavid du Colombier 	uchar *p, *ep, *lp, *op;
935*5c47fe09SDavid du Colombier 	int c, skipping;
936*5c47fe09SDavid du Colombier 
937*5c47fe09SDavid du Colombier 	skipping = 0;	/* true if in a comment */
938*5c47fe09SDavid du Colombier 	ep = buf + n;	/* end of input */
939*5c47fe09SDavid du Colombier 	op = buf;	/* end of output */
940*5c47fe09SDavid du Colombier 	lp = buf;	/* start of current output line */
941*5c47fe09SDavid du Colombier 	for(p = buf; p < ep; p++){
942*5c47fe09SDavid du Colombier 		switch(c = *p){
943*5c47fe09SDavid du Colombier 		case '#':
944*5c47fe09SDavid du Colombier 			skipping = 1;
945*5c47fe09SDavid du Colombier 			break;
946*5c47fe09SDavid du Colombier 		case '\0':
947*5c47fe09SDavid du Colombier 		case '\n':
948*5c47fe09SDavid du Colombier 			skipping = 0;
949*5c47fe09SDavid du Colombier 			if(op != lp){
950*5c47fe09SDavid du Colombier 				*op++ = '\0';
951*5c47fe09SDavid du Colombier 				lp = op;
952*5c47fe09SDavid du Colombier 			}
953*5c47fe09SDavid du Colombier 			break;
954*5c47fe09SDavid du Colombier 		case '\r':
955*5c47fe09SDavid du Colombier 			break;
956*5c47fe09SDavid du Colombier 		default:
957*5c47fe09SDavid du Colombier 			if(!skipping)
958*5c47fe09SDavid du Colombier 				*op++ = c;
959*5c47fe09SDavid du Colombier 			break;
960*5c47fe09SDavid du Colombier 		}
961*5c47fe09SDavid du Colombier 	}
962*5c47fe09SDavid du Colombier 	if(!skipping && op != lp)
963*5c47fe09SDavid du Colombier 		*op++ = '\0';
964*5c47fe09SDavid du Colombier 	*op++ = '\0';
965*5c47fe09SDavid du Colombier 	for(n = op - buf; n & 03; n++)
966*5c47fe09SDavid du Colombier 		*op++ = '\0';
967*5c47fe09SDavid du Colombier 	return n;
968*5c47fe09SDavid du Colombier }
969*5c47fe09SDavid du Colombier 
970*5c47fe09SDavid du Colombier /*
971*5c47fe09SDavid du Colombier  * Try to find firmware file in /boot or in /sys/lib/firmware.
972*5c47fe09SDavid du Colombier  * Throw an error if not found.
973*5c47fe09SDavid du Colombier  */
974*5c47fe09SDavid du Colombier static Chan*
findfirmware(char * file)975*5c47fe09SDavid du Colombier findfirmware(char *file)
976*5c47fe09SDavid du Colombier {
977*5c47fe09SDavid du Colombier 	char nbuf[64];
978*5c47fe09SDavid du Colombier 	Chan *c;
979*5c47fe09SDavid du Colombier 
980*5c47fe09SDavid du Colombier 	if(!waserror()){
981*5c47fe09SDavid du Colombier 		snprint(nbuf, sizeof nbuf, "/boot/%s", file);
982*5c47fe09SDavid du Colombier 		c = namec(nbuf, Aopen, OREAD, 0);
983*5c47fe09SDavid du Colombier 		poperror();
984*5c47fe09SDavid du Colombier 	}else if(!waserror()){
985*5c47fe09SDavid du Colombier 		snprint(nbuf, sizeof nbuf, "/sys/lib/firmware/%s", file);
986*5c47fe09SDavid du Colombier 		c = namec(nbuf, Aopen, OREAD, 0);
987*5c47fe09SDavid du Colombier 		poperror();
988*5c47fe09SDavid du Colombier 	}else{
989*5c47fe09SDavid du Colombier 		c = nil;
990*5c47fe09SDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "can't find %s in /boot or /sys/lib/firmware", file);
991*5c47fe09SDavid du Colombier 		error(up->genbuf);
992*5c47fe09SDavid du Colombier 	}
993*5c47fe09SDavid du Colombier 	return c;
994*5c47fe09SDavid du Colombier }
995*5c47fe09SDavid du Colombier 
996*5c47fe09SDavid du Colombier static int
upload(Ctlr * ctl,char * file,int isconfig)997*5c47fe09SDavid du Colombier upload(Ctlr *ctl, char *file, int isconfig)
998*5c47fe09SDavid du Colombier {
999*5c47fe09SDavid du Colombier 	Chan *c;
1000*5c47fe09SDavid du Colombier 	uchar *buf;
1001*5c47fe09SDavid du Colombier 	uchar *cbuf;
1002*5c47fe09SDavid du Colombier 	int off, n;
1003*5c47fe09SDavid du Colombier 
1004*5c47fe09SDavid du Colombier 	buf = cbuf = nil;
1005*5c47fe09SDavid du Colombier 	c = findfirmware(file);
1006*5c47fe09SDavid du Colombier 	if(waserror()){
1007*5c47fe09SDavid du Colombier 		cclose(c);
1008*5c47fe09SDavid du Colombier 		sdfree(buf);
1009*5c47fe09SDavid du Colombier 		sdfree(cbuf);
1010*5c47fe09SDavid du Colombier 		nexterror();
1011*5c47fe09SDavid du Colombier 	}
1012*5c47fe09SDavid du Colombier 	buf = sdmalloc(Uploadsz);
1013*5c47fe09SDavid du Colombier 	if(buf == nil)
1014*5c47fe09SDavid du Colombier 		error(Enomem);
1015*5c47fe09SDavid du Colombier 	if(Firmwarecmp){
1016*5c47fe09SDavid du Colombier 		cbuf = sdmalloc(Uploadsz);
1017*5c47fe09SDavid du Colombier 		if(cbuf == nil)
1018*5c47fe09SDavid du Colombier 			error(Enomem);
1019*5c47fe09SDavid du Colombier 	}
1020*5c47fe09SDavid du Colombier 	off = 0;
1021*5c47fe09SDavid du Colombier 	for(;;){
1022*5c47fe09SDavid du Colombier 		n = devtab[c->type]->read(c, buf, Uploadsz, off);
1023*5c47fe09SDavid du Colombier 		if(n <= 0)
1024*5c47fe09SDavid du Colombier 			break;
1025*5c47fe09SDavid du Colombier 		if(isconfig){
1026*5c47fe09SDavid du Colombier 			n = condense(buf, n);
1027*5c47fe09SDavid du Colombier 			off = ctl->socramsize - n - 4;
1028*5c47fe09SDavid du Colombier 		}else if(off == 0)
1029*5c47fe09SDavid du Colombier 			memmove(ctl->resetvec.c, buf, sizeof(ctl->resetvec.c));
1030*5c47fe09SDavid du Colombier 		while(n&3)
1031*5c47fe09SDavid du Colombier 			buf[n++] = 0;
1032*5c47fe09SDavid du Colombier 		sbmem(1, buf, n, ctl->rambase + off);
1033*5c47fe09SDavid du Colombier 		if(isconfig)
1034*5c47fe09SDavid du Colombier 			break;
1035*5c47fe09SDavid du Colombier 		off += n;
1036*5c47fe09SDavid du Colombier 	}
1037*5c47fe09SDavid du Colombier 	if(Firmwarecmp){
1038*5c47fe09SDavid du Colombier 		if(FWDEBUG) print("compare...");
1039*5c47fe09SDavid du Colombier 		if(!isconfig)
1040*5c47fe09SDavid du Colombier 			off = 0;
1041*5c47fe09SDavid du Colombier 		for(;;){
1042*5c47fe09SDavid du Colombier 			if(!isconfig){
1043*5c47fe09SDavid du Colombier 				n = devtab[c->type]->read(c, buf, Uploadsz, off);
1044*5c47fe09SDavid du Colombier 				if(n <= 0)
1045*5c47fe09SDavid du Colombier 					break;
1046*5c47fe09SDavid du Colombier 			while(n&3)
1047*5c47fe09SDavid du Colombier 				buf[n++] = 0;
1048*5c47fe09SDavid du Colombier 			}
1049*5c47fe09SDavid du Colombier 			sbmem(0, cbuf, n, ctl->rambase + off);
1050*5c47fe09SDavid du Colombier 			if(memcmp(buf, cbuf, n) != 0){
1051*5c47fe09SDavid du Colombier 				print("ether4330: firmware load failed offset %d\n", off);
1052*5c47fe09SDavid du Colombier 				error(Eio);
1053*5c47fe09SDavid du Colombier 			}
1054*5c47fe09SDavid du Colombier 			if(isconfig)
1055*5c47fe09SDavid du Colombier 				break;
1056*5c47fe09SDavid du Colombier 			off += n;
1057*5c47fe09SDavid du Colombier 		}
1058*5c47fe09SDavid du Colombier 	}
1059*5c47fe09SDavid du Colombier 	if(FWDEBUG) print("\n");
1060*5c47fe09SDavid du Colombier 	poperror();
1061*5c47fe09SDavid du Colombier 	cclose(c);
1062*5c47fe09SDavid du Colombier 	sdfree(buf);
1063*5c47fe09SDavid du Colombier 	sdfree(cbuf);
1064*5c47fe09SDavid du Colombier 	return n;
1065*5c47fe09SDavid du Colombier }
1066*5c47fe09SDavid du Colombier 
1067*5c47fe09SDavid du Colombier /*
1068*5c47fe09SDavid du Colombier  * Upload regulatory file (.clm) to firmware.
1069*5c47fe09SDavid du Colombier  * Packet format is
1070*5c47fe09SDavid du Colombier  *	[2]flag [2]type [4]len [4]crc [len]data
1071*5c47fe09SDavid du Colombier  */
1072*5c47fe09SDavid du Colombier static void
reguload(Ctlr * ctl,char * file)1073*5c47fe09SDavid du Colombier reguload(Ctlr *ctl, char *file)
1074*5c47fe09SDavid du Colombier {
1075*5c47fe09SDavid du Colombier 	Chan *c;
1076*5c47fe09SDavid du Colombier 	uchar *buf;
1077*5c47fe09SDavid du Colombier 	int off, n, flag;
1078*5c47fe09SDavid du Colombier 	enum {
1079*5c47fe09SDavid du Colombier 		Reguhdr = 2+2+4+4,
1080*5c47fe09SDavid du Colombier 		Regusz	= 1400,
1081*5c47fe09SDavid du Colombier 		Regutyp	= 2,
1082*5c47fe09SDavid du Colombier 		Flagclm	= 1<<12,
1083*5c47fe09SDavid du Colombier 		Firstpkt= 1<<1,
1084*5c47fe09SDavid du Colombier 		Lastpkt	= 1<<2,
1085*5c47fe09SDavid du Colombier 	};
1086*5c47fe09SDavid du Colombier 
1087*5c47fe09SDavid du Colombier 	buf = nil;
1088*5c47fe09SDavid du Colombier 	c = findfirmware(file);
1089*5c47fe09SDavid du Colombier 	if(waserror()){
1090*5c47fe09SDavid du Colombier 		cclose(c);
1091*5c47fe09SDavid du Colombier 		free(buf);
1092*5c47fe09SDavid du Colombier 		nexterror();
1093*5c47fe09SDavid du Colombier 	}
1094*5c47fe09SDavid du Colombier 	buf = malloc(Reguhdr+Regusz+1);
1095*5c47fe09SDavid du Colombier 	if(buf == nil)
1096*5c47fe09SDavid du Colombier 		error(Enomem);
1097*5c47fe09SDavid du Colombier 	put2(buf+2, Regutyp);
1098*5c47fe09SDavid du Colombier 	put2(buf+8, 0);
1099*5c47fe09SDavid du Colombier 	off = 0;
1100*5c47fe09SDavid du Colombier 	flag = Flagclm | Firstpkt;
1101*5c47fe09SDavid du Colombier 	while((flag&Lastpkt) == 0){
1102*5c47fe09SDavid du Colombier 		n = devtab[c->type]->read(c, buf+Reguhdr, Regusz+1, off);
1103*5c47fe09SDavid du Colombier 		if(n <= 0)
1104*5c47fe09SDavid du Colombier 			break;
1105*5c47fe09SDavid du Colombier 		if(n == Regusz+1)
1106*5c47fe09SDavid du Colombier 			--n;
1107*5c47fe09SDavid du Colombier 		else{
1108*5c47fe09SDavid du Colombier 			while(n&7)
1109*5c47fe09SDavid du Colombier 				buf[Reguhdr+n++] = 0;
1110*5c47fe09SDavid du Colombier 			flag |= Lastpkt;
1111*5c47fe09SDavid du Colombier 		}
1112*5c47fe09SDavid du Colombier 		put2(buf+0, flag);
1113*5c47fe09SDavid du Colombier 		put4(buf+4, n);
1114*5c47fe09SDavid du Colombier 		wlsetvar(ctl, "clmload", buf, Reguhdr + n);
1115*5c47fe09SDavid du Colombier 		off += n;
1116*5c47fe09SDavid du Colombier 		flag &= ~Firstpkt;
1117*5c47fe09SDavid du Colombier 	}
1118*5c47fe09SDavid du Colombier 	poperror();
1119*5c47fe09SDavid du Colombier 	cclose(c);
1120*5c47fe09SDavid du Colombier 	free(buf);
1121*5c47fe09SDavid du Colombier }
1122*5c47fe09SDavid du Colombier 
1123*5c47fe09SDavid du Colombier static void
fwload(Ctlr * ctl)1124*5c47fe09SDavid du Colombier fwload(Ctlr *ctl)
1125*5c47fe09SDavid du Colombier {
1126*5c47fe09SDavid du Colombier 	uchar buf[4];
1127*5c47fe09SDavid du Colombier 	uint i, n;
1128*5c47fe09SDavid du Colombier 
1129*5c47fe09SDavid du Colombier 	i = 0;
1130*5c47fe09SDavid du Colombier 	while(firmware[i].chipid != ctl->chipid ||
1131*5c47fe09SDavid du Colombier 		   firmware[i].chiprev != ctl->chiprev){
1132*5c47fe09SDavid du Colombier 		if(++i == nelem(firmware)){
1133*5c47fe09SDavid du Colombier 			print("ether4330: no firmware for chipid %x (%d) chiprev %d\n",
1134*5c47fe09SDavid du Colombier 				ctl->chipid, ctl->chipid, ctl->chiprev);
1135*5c47fe09SDavid du Colombier 			error("no firmware");
1136*5c47fe09SDavid du Colombier 		}
1137*5c47fe09SDavid du Colombier 	}
1138*5c47fe09SDavid du Colombier 	ctl->regufile = firmware[i].regufile;
1139*5c47fe09SDavid du Colombier 	cfgw(Clkcsr, ReqALP);
1140*5c47fe09SDavid du Colombier 	while((cfgr(Clkcsr) & ALPavail) == 0)
1141*5c47fe09SDavid du Colombier 		microdelay(10);
1142*5c47fe09SDavid du Colombier 	memset(buf, 0, 4);
1143*5c47fe09SDavid du Colombier 	sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4);
1144*5c47fe09SDavid du Colombier 	if(FWDEBUG) print("firmware load...");
1145*5c47fe09SDavid du Colombier 	upload(ctl, firmware[i].fwfile, 0);
1146*5c47fe09SDavid du Colombier 	if(FWDEBUG) print("config load...");
1147*5c47fe09SDavid du Colombier 	n = upload(ctl, firmware[i].cfgfile, 1);
1148*5c47fe09SDavid du Colombier 	n /= 4;
1149*5c47fe09SDavid du Colombier 	n = (n & 0xFFFF) | (~n << 16);
1150*5c47fe09SDavid du Colombier 	put4(buf, n);
1151*5c47fe09SDavid du Colombier 	sbmem(1, buf, 4, ctl->rambase + ctl->socramsize - 4);
1152*5c47fe09SDavid du Colombier 	if(ctl->armcore == ARMcr4){
1153*5c47fe09SDavid du Colombier 		sbwindow(ctl->sdregs);
1154*5c47fe09SDavid du Colombier 		cfgwritel(Fn1, ctl->sdregs + Intstatus, ~0);
1155*5c47fe09SDavid du Colombier 		if(ctl->resetvec.i != 0){
1156*5c47fe09SDavid du Colombier 			if(SBDEBUG) print("%ux\n", ctl->resetvec.i);
1157*5c47fe09SDavid du Colombier 			sbmem(1, ctl->resetvec.c, sizeof(ctl->resetvec.c), 0);
1158*5c47fe09SDavid du Colombier 		}
1159*5c47fe09SDavid du Colombier 		sbreset(ctl->armctl, Cr4Cpuhalt, 0);
1160*5c47fe09SDavid du Colombier 	}else
1161*5c47fe09SDavid du Colombier 		sbreset(ctl->armctl, 0, 0);
1162*5c47fe09SDavid du Colombier }
1163*5c47fe09SDavid du Colombier 
1164*5c47fe09SDavid du Colombier /*
1165*5c47fe09SDavid du Colombier  * Communication of data and control packets
1166*5c47fe09SDavid du Colombier  */
1167*5c47fe09SDavid du Colombier 
1168*5c47fe09SDavid du Colombier void
intwait(Ctlr * ctlr,int wait)1169*5c47fe09SDavid du Colombier intwait(Ctlr *ctlr, int wait)
1170*5c47fe09SDavid du Colombier {
1171*5c47fe09SDavid du Colombier 	ulong ints, mbox;
1172*5c47fe09SDavid du Colombier 	int i;
1173*5c47fe09SDavid du Colombier 
1174*5c47fe09SDavid du Colombier 	if(waserror())
1175*5c47fe09SDavid du Colombier 		return;
1176*5c47fe09SDavid du Colombier 	for(;;){
1177*5c47fe09SDavid du Colombier 		sdiocardintr(wait);
1178*5c47fe09SDavid du Colombier 		sbwindow(ctlr->sdregs);
1179*5c47fe09SDavid du Colombier 		i = sdiord(Fn0, Intpend);
1180*5c47fe09SDavid du Colombier 		if(i == 0){
1181*5c47fe09SDavid du Colombier 			tsleep(&up->sleep, return0, 0, 10);
1182*5c47fe09SDavid du Colombier 			continue;
1183*5c47fe09SDavid du Colombier 		}
1184*5c47fe09SDavid du Colombier 		ints = cfgreadl(Fn1, ctlr->sdregs + Intstatus);
1185*5c47fe09SDavid du Colombier 		cfgwritel(Fn1, ctlr->sdregs + Intstatus, ints);
1186*5c47fe09SDavid du Colombier 		if(0) print("INTS: (%x) %lux -> %lux\n", i, ints, cfgreadl(Fn1, ctlr->sdregs + Intstatus));
1187*5c47fe09SDavid du Colombier 		if(ints & MailboxInt){
1188*5c47fe09SDavid du Colombier 			mbox = cfgreadl(Fn1, ctlr->sdregs + Hostmboxdata);
1189*5c47fe09SDavid du Colombier 			cfgwritel(Fn1, ctlr->sdregs + Sbmbox, 2);	/* ack */
1190*5c47fe09SDavid du Colombier 			if(mbox & 0x8)
1191*5c47fe09SDavid du Colombier 				print("ether4330: firmware ready\n");
1192*5c47fe09SDavid du Colombier 		}
1193*5c47fe09SDavid du Colombier 		if(ints & FrameInt)
1194*5c47fe09SDavid du Colombier 			break;
1195*5c47fe09SDavid du Colombier 	}
1196*5c47fe09SDavid du Colombier 	poperror();
1197*5c47fe09SDavid du Colombier }
1198*5c47fe09SDavid du Colombier 
1199*5c47fe09SDavid du Colombier static Block*
wlreadpkt(Ctlr * ctl)1200*5c47fe09SDavid du Colombier wlreadpkt(Ctlr *ctl)
1201*5c47fe09SDavid du Colombier {
1202*5c47fe09SDavid du Colombier 	Block *b;
1203*5c47fe09SDavid du Colombier 	Sdpcm *p;
1204*5c47fe09SDavid du Colombier 	int len, lenck;
1205*5c47fe09SDavid du Colombier 
1206*5c47fe09SDavid du Colombier 	b = allocb(2048);
1207*5c47fe09SDavid du Colombier 	p = (Sdpcm*)b->wp;
1208*5c47fe09SDavid du Colombier 	qlock(&ctl->pktlock);
1209*5c47fe09SDavid du Colombier 	for(;;){
1210*5c47fe09SDavid du Colombier 		packetrw(0, b->wp, sizeof(*p));
1211*5c47fe09SDavid du Colombier 		len = p->len[0] | p->len[1]<<8;
1212*5c47fe09SDavid du Colombier 		if(len == 0){
1213*5c47fe09SDavid du Colombier 			freeb(b);
1214*5c47fe09SDavid du Colombier 			b = nil;
1215*5c47fe09SDavid du Colombier 			break;
1216*5c47fe09SDavid du Colombier 		}
1217*5c47fe09SDavid du Colombier 		lenck = p->lenck[0] | p->lenck[1]<<8;
1218*5c47fe09SDavid du Colombier 		if(lenck != (len ^ 0xFFFF) ||
1219*5c47fe09SDavid du Colombier 		   len < sizeof(*p) || len > 2048){
1220*5c47fe09SDavid du Colombier 			print("ether4330: wlreadpkt error len %.4x lenck %.4x\n", len, lenck);
1221*5c47fe09SDavid du Colombier 			cfgw(Framectl, Rfhalt);
1222*5c47fe09SDavid du Colombier 			while(cfgr(Rfrmcnt+1))
1223*5c47fe09SDavid du Colombier 				;
1224*5c47fe09SDavid du Colombier 			while(cfgr(Rfrmcnt))
1225*5c47fe09SDavid du Colombier 				;
1226*5c47fe09SDavid du Colombier 			continue;
1227*5c47fe09SDavid du Colombier 		}
1228*5c47fe09SDavid du Colombier 		if(len > sizeof(*p))
1229*5c47fe09SDavid du Colombier 			packetrw(0, b->wp + sizeof(*p), len - sizeof(*p));
1230*5c47fe09SDavid du Colombier 		b->wp += len;
1231*5c47fe09SDavid du Colombier 		break;
1232*5c47fe09SDavid du Colombier 	}
1233*5c47fe09SDavid du Colombier 	qunlock(&ctl->pktlock);
1234*5c47fe09SDavid du Colombier 	return b;
1235*5c47fe09SDavid du Colombier }
1236*5c47fe09SDavid du Colombier 
1237*5c47fe09SDavid du Colombier static void
txstart(Ether * edev)1238*5c47fe09SDavid du Colombier txstart(Ether *edev)
1239*5c47fe09SDavid du Colombier {
1240*5c47fe09SDavid du Colombier 	Ctlr *ctl;
1241*5c47fe09SDavid du Colombier 	Sdpcm *p;
1242*5c47fe09SDavid du Colombier 	Block *b;
1243*5c47fe09SDavid du Colombier 	int len, off;
1244*5c47fe09SDavid du Colombier 
1245*5c47fe09SDavid du Colombier 	ctl = edev->ctlr;
1246*5c47fe09SDavid du Colombier 	if(!canqlock(&ctl->tlock))
1247*5c47fe09SDavid du Colombier 		return;
1248*5c47fe09SDavid du Colombier 	if(waserror()){
1249*5c47fe09SDavid du Colombier 		qunlock(&ctl->tlock);
1250*5c47fe09SDavid du Colombier 		return;
1251*5c47fe09SDavid du Colombier 	}
1252*5c47fe09SDavid du Colombier 	for(;;){
1253*5c47fe09SDavid du Colombier 		lock(&ctl->txwinlock);
1254*5c47fe09SDavid du Colombier 		if(ctl->txseq == ctl->txwindow){
1255*5c47fe09SDavid du Colombier 			//print("f");
1256*5c47fe09SDavid du Colombier 			unlock(&ctl->txwinlock);
1257*5c47fe09SDavid du Colombier 			break;
1258*5c47fe09SDavid du Colombier 		}
1259*5c47fe09SDavid du Colombier 		if(ctl->fcmask & 1<<2){
1260*5c47fe09SDavid du Colombier 			//print("x");
1261*5c47fe09SDavid du Colombier 			unlock(&ctl->txwinlock);
1262*5c47fe09SDavid du Colombier 			break;
1263*5c47fe09SDavid du Colombier 		}
1264*5c47fe09SDavid du Colombier 		unlock(&ctl->txwinlock);
1265*5c47fe09SDavid du Colombier 		b = qget(edev->oq);
1266*5c47fe09SDavid du Colombier 		if(b == nil)
1267*5c47fe09SDavid du Colombier 			break;
1268*5c47fe09SDavid du Colombier 		off = ((uintptr)b->rp & 3) + sizeof(Sdpcm);
1269*5c47fe09SDavid du Colombier 		b = padblock(b, off + 4);
1270*5c47fe09SDavid du Colombier 		len = BLEN(b);
1271*5c47fe09SDavid du Colombier 		p = (Sdpcm*)b->rp;
1272*5c47fe09SDavid du Colombier 		memset(p, 0, off);	/* TODO: refactor dup code */
1273*5c47fe09SDavid du Colombier 		put2(p->len, len);
1274*5c47fe09SDavid du Colombier 		put2(p->lenck, ~len);
1275*5c47fe09SDavid du Colombier 		p->chanflg = 2;
1276*5c47fe09SDavid du Colombier 		p->seq = ctl->txseq;
1277*5c47fe09SDavid du Colombier 		p->doffset = off;
1278*5c47fe09SDavid du Colombier 		put4(b->rp + off, 0x20);	/* BDC header */
1279*5c47fe09SDavid du Colombier 		if(iodebug) dump("send", b->rp, len);
1280*5c47fe09SDavid du Colombier 		qlock(&ctl->pktlock);
1281*5c47fe09SDavid du Colombier 		if(waserror()){
1282*5c47fe09SDavid du Colombier 			if(iodebug) print("halt frame %x %x\n", cfgr(Wfrmcnt+1), cfgr(Wfrmcnt+1));
1283*5c47fe09SDavid du Colombier 			cfgw(Framectl, Wfhalt);
1284*5c47fe09SDavid du Colombier 			while(cfgr(Wfrmcnt+1))
1285*5c47fe09SDavid du Colombier 				;
1286*5c47fe09SDavid du Colombier 			while(cfgr(Wfrmcnt))
1287*5c47fe09SDavid du Colombier 				;
1288*5c47fe09SDavid du Colombier 			qunlock(&ctl->pktlock);
1289*5c47fe09SDavid du Colombier 			nexterror();
1290*5c47fe09SDavid du Colombier 		}
1291*5c47fe09SDavid du Colombier 		packetrw(1, b->rp, len);
1292*5c47fe09SDavid du Colombier 		ctl->txseq++;
1293*5c47fe09SDavid du Colombier 		poperror();
1294*5c47fe09SDavid du Colombier 		qunlock(&ctl->pktlock);
1295*5c47fe09SDavid du Colombier 		freeb(b);
1296*5c47fe09SDavid du Colombier 	}
1297*5c47fe09SDavid du Colombier 	poperror();
1298*5c47fe09SDavid du Colombier 	qunlock(&ctl->tlock);
1299*5c47fe09SDavid du Colombier }
1300*5c47fe09SDavid du Colombier 
1301*5c47fe09SDavid du Colombier static void
rproc(void * a)1302*5c47fe09SDavid du Colombier rproc(void *a)
1303*5c47fe09SDavid du Colombier {
1304*5c47fe09SDavid du Colombier 	Ether *edev;
1305*5c47fe09SDavid du Colombier 	Ctlr *ctl;
1306*5c47fe09SDavid du Colombier 	Block *b;
1307*5c47fe09SDavid du Colombier 	Sdpcm *p;
1308*5c47fe09SDavid du Colombier 	Cmd *q;
1309*5c47fe09SDavid du Colombier 	int flowstart;
1310*5c47fe09SDavid du Colombier 	int bdc;
1311*5c47fe09SDavid du Colombier 
1312*5c47fe09SDavid du Colombier 	edev = a;
1313*5c47fe09SDavid du Colombier 	ctl = edev->ctlr;
1314*5c47fe09SDavid du Colombier 	flowstart = 0;
1315*5c47fe09SDavid du Colombier 	for(;;){
1316*5c47fe09SDavid du Colombier 		if(flowstart){
1317*5c47fe09SDavid du Colombier 			//print("F");
1318*5c47fe09SDavid du Colombier 			flowstart = 0;
1319*5c47fe09SDavid du Colombier 			txstart(edev);
1320*5c47fe09SDavid du Colombier 		}
1321*5c47fe09SDavid du Colombier 		b = wlreadpkt(ctl);
1322*5c47fe09SDavid du Colombier 		if(b == nil){
1323*5c47fe09SDavid du Colombier 			intwait(ctl, 1);
1324*5c47fe09SDavid du Colombier 			continue;
1325*5c47fe09SDavid du Colombier 		}
1326*5c47fe09SDavid du Colombier 		p = (Sdpcm*)b->rp;
1327*5c47fe09SDavid du Colombier 		if(p->window != ctl->txwindow || p->fcmask != ctl->fcmask){
1328*5c47fe09SDavid du Colombier 			lock(&ctl->txwinlock);
1329*5c47fe09SDavid du Colombier 			if(p->window != ctl->txwindow){
1330*5c47fe09SDavid du Colombier 				if(ctl->txseq == ctl->txwindow)
1331*5c47fe09SDavid du Colombier 					flowstart = 1;
1332*5c47fe09SDavid du Colombier 				ctl->txwindow = p->window;
1333*5c47fe09SDavid du Colombier 			}
1334*5c47fe09SDavid du Colombier 			if(p->fcmask != ctl->fcmask){
1335*5c47fe09SDavid du Colombier 				if((p->fcmask & 1<<2) == 0)
1336*5c47fe09SDavid du Colombier 					flowstart = 1;
1337*5c47fe09SDavid du Colombier 				ctl->fcmask = p->fcmask;
1338*5c47fe09SDavid du Colombier 			}
1339*5c47fe09SDavid du Colombier 			unlock(&ctl->txwinlock);
1340*5c47fe09SDavid du Colombier 		}
1341*5c47fe09SDavid du Colombier 		switch(p->chanflg & 0xF){
1342*5c47fe09SDavid du Colombier 		case 0:
1343*5c47fe09SDavid du Colombier 			if(iodebug) dump("rsp", b->rp, BLEN(b));
1344*5c47fe09SDavid du Colombier 			if(BLEN(b) < sizeof(Sdpcm) + sizeof(Cmd))
1345*5c47fe09SDavid du Colombier 				break;
1346*5c47fe09SDavid du Colombier 			q = (Cmd*)(b->rp + sizeof(*p));
1347*5c47fe09SDavid du Colombier 			if((q->id[0] | q->id[1]<<8) != ctl->reqid)
1348*5c47fe09SDavid du Colombier 				break;
1349*5c47fe09SDavid du Colombier 			ctl->rsp = b;
1350*5c47fe09SDavid du Colombier 			wakeup(&ctl->cmdr);
1351*5c47fe09SDavid du Colombier 			continue;
1352*5c47fe09SDavid du Colombier 		case 1:
1353*5c47fe09SDavid du Colombier 			if(iodebug) dump("event", b->rp, BLEN(b));
1354*5c47fe09SDavid du Colombier 			if(BLEN(b) > p->doffset + 4){
1355*5c47fe09SDavid du Colombier 				bdc = 4 + (b->rp[p->doffset + 3] << 2);
1356*5c47fe09SDavid du Colombier 				if(BLEN(b) > p->doffset + bdc){
1357*5c47fe09SDavid du Colombier 					b->rp += p->doffset + bdc;	/* skip BDC header */
1358*5c47fe09SDavid du Colombier 					bcmevent(ctl, b->rp, BLEN(b));
1359*5c47fe09SDavid du Colombier 					break;
1360*5c47fe09SDavid du Colombier 				}
1361*5c47fe09SDavid du Colombier 			}
1362*5c47fe09SDavid du Colombier 			if(iodebug && BLEN(b) != p->doffset)
1363*5c47fe09SDavid du Colombier 				print("short event %ld %d\n", BLEN(b), p->doffset);
1364*5c47fe09SDavid du Colombier 			break;
1365*5c47fe09SDavid du Colombier 		case 2:
1366*5c47fe09SDavid du Colombier 			if(iodebug) dump("packet", b->rp, BLEN(b));
1367*5c47fe09SDavid du Colombier 			if(BLEN(b) > p->doffset + 4){
1368*5c47fe09SDavid du Colombier 				bdc = 4 + (b->rp[p->doffset + 3] << 2);
1369*5c47fe09SDavid du Colombier 				if(BLEN(b) >= p->doffset + bdc + ETHERHDRSIZE){
1370*5c47fe09SDavid du Colombier 					b->rp += p->doffset + bdc;	/* skip BDC header */
1371*5c47fe09SDavid du Colombier 					etheriq(edev, b, 1);
1372*5c47fe09SDavid du Colombier 					continue;
1373*5c47fe09SDavid du Colombier 				}
1374*5c47fe09SDavid du Colombier 			}
1375*5c47fe09SDavid du Colombier 			break;
1376*5c47fe09SDavid du Colombier 		default:
1377*5c47fe09SDavid du Colombier 			dump("ether4330: bad packet", b->rp, BLEN(b));
1378*5c47fe09SDavid du Colombier 			break;
1379*5c47fe09SDavid du Colombier 		}
1380*5c47fe09SDavid du Colombier 		freeb(b);
1381*5c47fe09SDavid du Colombier 	}
1382*5c47fe09SDavid du Colombier }
1383*5c47fe09SDavid du Colombier 
1384*5c47fe09SDavid du Colombier static void
linkdown(Ctlr * ctl)1385*5c47fe09SDavid du Colombier linkdown(Ctlr *ctl)
1386*5c47fe09SDavid du Colombier {
1387*5c47fe09SDavid du Colombier 	Ether *edev;
1388*5c47fe09SDavid du Colombier 	Netfile *f;
1389*5c47fe09SDavid du Colombier 	int i;
1390*5c47fe09SDavid du Colombier 
1391*5c47fe09SDavid du Colombier 	edev = ctl->edev;
1392*5c47fe09SDavid du Colombier 	if(edev == nil || ctl->status != Connected)
1393*5c47fe09SDavid du Colombier 		return;
1394*5c47fe09SDavid du Colombier 	ctl->status = Disconnected;
1395*5c47fe09SDavid du Colombier 	/* send eof to aux/wpa */
1396*5c47fe09SDavid du Colombier 	for(i = 0; i < edev->nfile; i++){
1397*5c47fe09SDavid du Colombier 		f = edev->f[i];
1398*5c47fe09SDavid du Colombier 		if(f == nil || f->in == nil || f->inuse == 0 || f->type != 0x888e)
1399*5c47fe09SDavid du Colombier 			continue;
1400*5c47fe09SDavid du Colombier 		qwrite(f->in, 0, 0);
1401*5c47fe09SDavid du Colombier 	}
1402*5c47fe09SDavid du Colombier }
1403*5c47fe09SDavid du Colombier 
1404*5c47fe09SDavid du Colombier /*
1405*5c47fe09SDavid du Colombier  * Command interface between host and firmware
1406*5c47fe09SDavid du Colombier  */
1407*5c47fe09SDavid du Colombier 
1408*5c47fe09SDavid du Colombier static char *eventnames[] = {
1409*5c47fe09SDavid du Colombier 	[0] = "set ssid",
1410*5c47fe09SDavid du Colombier 	[1] = "join",
1411*5c47fe09SDavid du Colombier 	[2] = "start",
1412*5c47fe09SDavid du Colombier 	[3] = "auth",
1413*5c47fe09SDavid du Colombier 	[4] = "auth ind",
1414*5c47fe09SDavid du Colombier 	[5] = "deauth",
1415*5c47fe09SDavid du Colombier 	[6] = "deauth ind",
1416*5c47fe09SDavid du Colombier 	[7] = "assoc",
1417*5c47fe09SDavid du Colombier 	[8] = "assoc ind",
1418*5c47fe09SDavid du Colombier 	[9] = "reassoc",
1419*5c47fe09SDavid du Colombier 	[10] = "reassoc ind",
1420*5c47fe09SDavid du Colombier 	[11] = "disassoc",
1421*5c47fe09SDavid du Colombier 	[12] = "disassoc ind",
1422*5c47fe09SDavid du Colombier 	[13] = "quiet start",
1423*5c47fe09SDavid du Colombier 	[14] = "quiet end",
1424*5c47fe09SDavid du Colombier 	[15] = "beacon rx",
1425*5c47fe09SDavid du Colombier 	[16] = "link",
1426*5c47fe09SDavid du Colombier 	[17] = "mic error",
1427*5c47fe09SDavid du Colombier 	[18] = "ndis link",
1428*5c47fe09SDavid du Colombier 	[19] = "roam",
1429*5c47fe09SDavid du Colombier 	[20] = "txfail",
1430*5c47fe09SDavid du Colombier 	[21] = "pmkid cache",
1431*5c47fe09SDavid du Colombier 	[22] = "retrograde tsf",
1432*5c47fe09SDavid du Colombier 	[23] = "prune",
1433*5c47fe09SDavid du Colombier 	[24] = "autoauth",
1434*5c47fe09SDavid du Colombier 	[25] = "eapol msg",
1435*5c47fe09SDavid du Colombier 	[26] = "scan complete",
1436*5c47fe09SDavid du Colombier 	[27] = "addts ind",
1437*5c47fe09SDavid du Colombier 	[28] = "delts ind",
1438*5c47fe09SDavid du Colombier 	[29] = "bcnsent ind",
1439*5c47fe09SDavid du Colombier 	[30] = "bcnrx msg",
1440*5c47fe09SDavid du Colombier 	[31] = "bcnlost msg",
1441*5c47fe09SDavid du Colombier 	[32] = "roam prep",
1442*5c47fe09SDavid du Colombier 	[33] = "pfn net found",
1443*5c47fe09SDavid du Colombier 	[34] = "pfn net lost",
1444*5c47fe09SDavid du Colombier 	[35] = "reset complete",
1445*5c47fe09SDavid du Colombier 	[36] = "join start",
1446*5c47fe09SDavid du Colombier 	[37] = "roam start",
1447*5c47fe09SDavid du Colombier 	[38] = "assoc start",
1448*5c47fe09SDavid du Colombier 	[39] = "ibss assoc",
1449*5c47fe09SDavid du Colombier 	[40] = "radio",
1450*5c47fe09SDavid du Colombier 	[41] = "psm watchdog",
1451*5c47fe09SDavid du Colombier 	[44] = "probreq msg",
1452*5c47fe09SDavid du Colombier 	[45] = "scan confirm ind",
1453*5c47fe09SDavid du Colombier 	[46] = "psk sup",
1454*5c47fe09SDavid du Colombier 	[47] = "country code changed",
1455*5c47fe09SDavid du Colombier 	[48] = "exceeded medium time",
1456*5c47fe09SDavid du Colombier 	[49] = "icv error",
1457*5c47fe09SDavid du Colombier 	[50] = "unicast decode error",
1458*5c47fe09SDavid du Colombier 	[51] = "multicast decode error",
1459*5c47fe09SDavid du Colombier 	[52] = "trace",
1460*5c47fe09SDavid du Colombier 	[53] = "bta hci event",
1461*5c47fe09SDavid du Colombier 	[54] = "if",
1462*5c47fe09SDavid du Colombier 	[55] = "p2p disc listen complete",
1463*5c47fe09SDavid du Colombier 	[56] = "rssi",
1464*5c47fe09SDavid du Colombier 	[57] = "pfn scan complete",
1465*5c47fe09SDavid du Colombier 	[58] = "extlog msg",
1466*5c47fe09SDavid du Colombier 	[59] = "action frame",
1467*5c47fe09SDavid du Colombier 	[60] = "action frame complete",
1468*5c47fe09SDavid du Colombier 	[61] = "pre assoc ind",
1469*5c47fe09SDavid du Colombier 	[62] = "pre reassoc ind",
1470*5c47fe09SDavid du Colombier 	[63] = "channel adopted",
1471*5c47fe09SDavid du Colombier 	[64] = "ap started",
1472*5c47fe09SDavid du Colombier 	[65] = "dfs ap stop",
1473*5c47fe09SDavid du Colombier 	[66] = "dfs ap resume",
1474*5c47fe09SDavid du Colombier 	[67] = "wai sta event",
1475*5c47fe09SDavid du Colombier 	[68] = "wai msg",
1476*5c47fe09SDavid du Colombier 	[69] = "escan result",
1477*5c47fe09SDavid du Colombier 	[70] = "action frame off chan complete",
1478*5c47fe09SDavid du Colombier 	[71] = "probresp msg",
1479*5c47fe09SDavid du Colombier 	[72] = "p2p probreq msg",
1480*5c47fe09SDavid du Colombier 	[73] = "dcs request",
1481*5c47fe09SDavid du Colombier 	[74] = "fifo credit map",
1482*5c47fe09SDavid du Colombier 	[75] = "action frame rx",
1483*5c47fe09SDavid du Colombier 	[76] = "wake event",
1484*5c47fe09SDavid du Colombier 	[77] = "rm complete",
1485*5c47fe09SDavid du Colombier 	[78] = "htsfsync",
1486*5c47fe09SDavid du Colombier 	[79] = "overlay req",
1487*5c47fe09SDavid du Colombier 	[80] = "csa complete ind",
1488*5c47fe09SDavid du Colombier 	[81] = "excess pm wake event",
1489*5c47fe09SDavid du Colombier 	[82] = "pfn scan none",
1490*5c47fe09SDavid du Colombier 	[83] = "pfn scan allgone",
1491*5c47fe09SDavid du Colombier 	[84] = "gtk plumbed",
1492*5c47fe09SDavid du Colombier 	[85] = "assoc ind ndis",
1493*5c47fe09SDavid du Colombier 	[86] = "reassoc ind ndis",
1494*5c47fe09SDavid du Colombier 	[87] = "assoc req ie",
1495*5c47fe09SDavid du Colombier 	[88] = "assoc resp ie",
1496*5c47fe09SDavid du Colombier 	[89] = "assoc recreated",
1497*5c47fe09SDavid du Colombier 	[90] = "action frame rx ndis",
1498*5c47fe09SDavid du Colombier 	[91] = "auth req",
1499*5c47fe09SDavid du Colombier 	[92] = "tdls peer event",
1500*5c47fe09SDavid du Colombier 	[127] = "bcmc credit support"
1501*5c47fe09SDavid du Colombier };
1502*5c47fe09SDavid du Colombier 
1503*5c47fe09SDavid du Colombier static char*
evstring(uint event)1504*5c47fe09SDavid du Colombier evstring(uint event)
1505*5c47fe09SDavid du Colombier {
1506*5c47fe09SDavid du Colombier 	static char buf[12];
1507*5c47fe09SDavid du Colombier 
1508*5c47fe09SDavid du Colombier 	if(event >= nelem(eventnames) || eventnames[event] == 0){
1509*5c47fe09SDavid du Colombier 		/* not reentrant but only called from one kproc */
1510*5c47fe09SDavid du Colombier 		snprint(buf, sizeof buf, "%d", event);
1511*5c47fe09SDavid du Colombier 		return buf;
1512*5c47fe09SDavid du Colombier 	}
1513*5c47fe09SDavid du Colombier 	return eventnames[event];
1514*5c47fe09SDavid du Colombier }
1515*5c47fe09SDavid du Colombier 
1516*5c47fe09SDavid du Colombier static void
bcmevent(Ctlr * ctl,uchar * p,int len)1517*5c47fe09SDavid du Colombier bcmevent(Ctlr *ctl, uchar *p, int len)
1518*5c47fe09SDavid du Colombier {
1519*5c47fe09SDavid du Colombier 	int flags;
1520*5c47fe09SDavid du Colombier 	long event, status, reason;
1521*5c47fe09SDavid du Colombier 
1522*5c47fe09SDavid du Colombier 	if(len < ETHERHDRSIZE + 10 + 46)
1523*5c47fe09SDavid du Colombier 		return;
1524*5c47fe09SDavid du Colombier 	p += ETHERHDRSIZE + 10;			/* skip bcm_ether header */
1525*5c47fe09SDavid du Colombier 	len -= ETHERHDRSIZE + 10;
1526*5c47fe09SDavid du Colombier 	flags = nhgets(p + 2);
1527*5c47fe09SDavid du Colombier 	event = nhgets(p + 6);
1528*5c47fe09SDavid du Colombier 	status = nhgetl(p + 8);
1529*5c47fe09SDavid du Colombier 	reason = nhgetl(p + 12);
1530*5c47fe09SDavid du Colombier 	if(EVENTDEBUG)
1531*5c47fe09SDavid du Colombier 		print("ether4330: [%s] status %ld flags %#x reason %ld\n",
1532*5c47fe09SDavid du Colombier 			evstring(event), status, flags, reason);
1533*5c47fe09SDavid du Colombier 	switch(event){
1534*5c47fe09SDavid du Colombier 	case 19:	/* E_ROAM */
1535*5c47fe09SDavid du Colombier 		if(status == 0)
1536*5c47fe09SDavid du Colombier 			break;
1537*5c47fe09SDavid du Colombier 	/* fall through */
1538*5c47fe09SDavid du Colombier 	case 0:		/* E_SET_SSID */
1539*5c47fe09SDavid du Colombier 		ctl->joinstatus = 1 + status;
1540*5c47fe09SDavid du Colombier 		wakeup(&ctl->joinr);
1541*5c47fe09SDavid du Colombier 		break;
1542*5c47fe09SDavid du Colombier 	case 16:	/* E_LINK */
1543*5c47fe09SDavid du Colombier 		if(flags&1)	/* link up */
1544*5c47fe09SDavid du Colombier 			break;
1545*5c47fe09SDavid du Colombier 	/* fall through */
1546*5c47fe09SDavid du Colombier 	case 5:		/* E_DEAUTH */
1547*5c47fe09SDavid du Colombier 	case 6:		/* E_DEAUTH_IND */
1548*5c47fe09SDavid du Colombier 	case 12:	/* E_DISASSOC_IND */
1549*5c47fe09SDavid du Colombier 		linkdown(ctl);
1550*5c47fe09SDavid du Colombier 		break;
1551*5c47fe09SDavid du Colombier 	case 26:	/* E_SCAN_COMPLETE */
1552*5c47fe09SDavid du Colombier 		break;
1553*5c47fe09SDavid du Colombier 	case 69:	/* E_ESCAN_RESULT */
1554*5c47fe09SDavid du Colombier 		wlscanresult(ctl->edev, p + 48, len - 48);
1555*5c47fe09SDavid du Colombier 		break;
1556*5c47fe09SDavid du Colombier 	default:
1557*5c47fe09SDavid du Colombier 		if(status){
1558*5c47fe09SDavid du Colombier 			if(!EVENTDEBUG)
1559*5c47fe09SDavid du Colombier 				print("ether4330: [%s] error status %ld flags %#x reason %ld\n",
1560*5c47fe09SDavid du Colombier 					evstring(event), status, flags, reason);
1561*5c47fe09SDavid du Colombier 			dump("event", p, len);
1562*5c47fe09SDavid du Colombier 		}
1563*5c47fe09SDavid du Colombier 	}
1564*5c47fe09SDavid du Colombier }
1565*5c47fe09SDavid du Colombier 
1566*5c47fe09SDavid du Colombier static int
joindone(void * a)1567*5c47fe09SDavid du Colombier joindone(void *a)
1568*5c47fe09SDavid du Colombier {
1569*5c47fe09SDavid du Colombier 	return ((Ctlr*)a)->joinstatus;
1570*5c47fe09SDavid du Colombier }
1571*5c47fe09SDavid du Colombier 
1572*5c47fe09SDavid du Colombier static int
waitjoin(Ctlr * ctl)1573*5c47fe09SDavid du Colombier waitjoin(Ctlr *ctl)
1574*5c47fe09SDavid du Colombier {
1575*5c47fe09SDavid du Colombier 	int n;
1576*5c47fe09SDavid du Colombier 
1577*5c47fe09SDavid du Colombier 	sleep(&ctl->joinr, joindone, ctl);
1578*5c47fe09SDavid du Colombier 	n = ctl->joinstatus;
1579*5c47fe09SDavid du Colombier 	ctl->joinstatus = 0;
1580*5c47fe09SDavid du Colombier 	return n - 1;
1581*5c47fe09SDavid du Colombier }
1582*5c47fe09SDavid du Colombier 
1583*5c47fe09SDavid du Colombier static int
cmddone(void * a)1584*5c47fe09SDavid du Colombier cmddone(void *a)
1585*5c47fe09SDavid du Colombier {
1586*5c47fe09SDavid du Colombier 	return ((Ctlr*)a)->rsp != nil;
1587*5c47fe09SDavid du Colombier }
1588*5c47fe09SDavid du Colombier 
1589*5c47fe09SDavid du Colombier static void
wlcmd(Ctlr * ctl,int write,int op,void * data,int dlen,void * res,int rlen)1590*5c47fe09SDavid du Colombier wlcmd(Ctlr *ctl, int write, int op, void *data, int dlen, void *res, int rlen)
1591*5c47fe09SDavid du Colombier {
1592*5c47fe09SDavid du Colombier 	Block *b;
1593*5c47fe09SDavid du Colombier 	Sdpcm *p;
1594*5c47fe09SDavid du Colombier 	Cmd *q;
1595*5c47fe09SDavid du Colombier 	int len, tlen;
1596*5c47fe09SDavid du Colombier 
1597*5c47fe09SDavid du Colombier 	if(write)
1598*5c47fe09SDavid du Colombier 		tlen = dlen + rlen;
1599*5c47fe09SDavid du Colombier 	else
1600*5c47fe09SDavid du Colombier 		tlen = MAX(dlen, rlen);
1601*5c47fe09SDavid du Colombier 	len = sizeof(Sdpcm) + sizeof(Cmd) + tlen;
1602*5c47fe09SDavid du Colombier 	b = allocb(len);
1603*5c47fe09SDavid du Colombier 	qlock(&ctl->cmdlock);
1604*5c47fe09SDavid du Colombier 	if(waserror()){
1605*5c47fe09SDavid du Colombier 		freeb(b);
1606*5c47fe09SDavid du Colombier 		qunlock(&ctl->cmdlock);
1607*5c47fe09SDavid du Colombier 		nexterror();
1608*5c47fe09SDavid du Colombier 	}
1609*5c47fe09SDavid du Colombier 	memset(b->wp, 0, len);
1610*5c47fe09SDavid du Colombier 	qlock(&ctl->pktlock);
1611*5c47fe09SDavid du Colombier 	p = (Sdpcm*)b->wp;
1612*5c47fe09SDavid du Colombier 	put2(p->len, len);
1613*5c47fe09SDavid du Colombier 	put2(p->lenck, ~len);
1614*5c47fe09SDavid du Colombier 	p->seq = ctl->txseq;
1615*5c47fe09SDavid du Colombier 	p->doffset = sizeof(Sdpcm);
1616*5c47fe09SDavid du Colombier 	b->wp += sizeof(*p);
1617*5c47fe09SDavid du Colombier 
1618*5c47fe09SDavid du Colombier 	q = (Cmd*)b->wp;
1619*5c47fe09SDavid du Colombier 	put4(q->cmd, op);
1620*5c47fe09SDavid du Colombier 	put4(q->len, tlen);
1621*5c47fe09SDavid du Colombier 	put2(q->flags, write? 2 : 0);
1622*5c47fe09SDavid du Colombier 	put2(q->id, ++ctl->reqid);
1623*5c47fe09SDavid du Colombier 	put4(q->status, 0);
1624*5c47fe09SDavid du Colombier 	b->wp += sizeof(*q);
1625*5c47fe09SDavid du Colombier 
1626*5c47fe09SDavid du Colombier 	if(dlen > 0)
1627*5c47fe09SDavid du Colombier 		memmove(b->wp, data, dlen);
1628*5c47fe09SDavid du Colombier 	if(write)
1629*5c47fe09SDavid du Colombier 		memmove(b->wp + dlen, res, rlen);
1630*5c47fe09SDavid du Colombier 	b->wp += tlen;
1631*5c47fe09SDavid du Colombier 
1632*5c47fe09SDavid du Colombier 	if(iodebug) dump("cmd", b->rp, len);
1633*5c47fe09SDavid du Colombier 	packetrw(1, b->rp, len);
1634*5c47fe09SDavid du Colombier 	ctl->txseq++;
1635*5c47fe09SDavid du Colombier 	qunlock(&ctl->pktlock);
1636*5c47fe09SDavid du Colombier 	freeb(b);
1637*5c47fe09SDavid du Colombier 	b = nil;
1638*5c47fe09SDavid du Colombier 	USED(b);
1639*5c47fe09SDavid du Colombier 	sleep(&ctl->cmdr, cmddone, ctl);
1640*5c47fe09SDavid du Colombier 	b = ctl->rsp;
1641*5c47fe09SDavid du Colombier 	ctl->rsp = nil;
1642*5c47fe09SDavid du Colombier 	assert(b != nil);
1643*5c47fe09SDavid du Colombier 	p = (Sdpcm*)b->rp;
1644*5c47fe09SDavid du Colombier 	q = (Cmd*)(b->rp + p->doffset);
1645*5c47fe09SDavid du Colombier 	if(q->status[0] | q->status[1] | q->status[2] | q->status[3]){
1646*5c47fe09SDavid du Colombier 		print("ether4330: cmd %d error status %ld\n", op, get4(q->status));
1647*5c47fe09SDavid du Colombier 		dump("ether4330: cmd error", b->rp, BLEN(b));
1648*5c47fe09SDavid du Colombier 		error("wlcmd error");
1649*5c47fe09SDavid du Colombier 	}
1650*5c47fe09SDavid du Colombier 	if(!write)
1651*5c47fe09SDavid du Colombier 		memmove(res, q + 1, rlen);
1652*5c47fe09SDavid du Colombier 	freeb(b);
1653*5c47fe09SDavid du Colombier 	qunlock(&ctl->cmdlock);
1654*5c47fe09SDavid du Colombier 	poperror();
1655*5c47fe09SDavid du Colombier }
1656*5c47fe09SDavid du Colombier 
1657*5c47fe09SDavid du Colombier static void
wlcmdint(Ctlr * ctl,int op,int val)1658*5c47fe09SDavid du Colombier wlcmdint(Ctlr *ctl, int op, int val)
1659*5c47fe09SDavid du Colombier {
1660*5c47fe09SDavid du Colombier 	uchar buf[4];
1661*5c47fe09SDavid du Colombier 
1662*5c47fe09SDavid du Colombier 	put4(buf, val);
1663*5c47fe09SDavid du Colombier 	wlcmd(ctl, 1, op, buf, 4, nil, 0);
1664*5c47fe09SDavid du Colombier }
1665*5c47fe09SDavid du Colombier 
1666*5c47fe09SDavid du Colombier static void
wlgetvar(Ctlr * ctl,char * name,void * val,int len)1667*5c47fe09SDavid du Colombier wlgetvar(Ctlr *ctl, char *name, void *val, int len)
1668*5c47fe09SDavid du Colombier {
1669*5c47fe09SDavid du Colombier 	wlcmd(ctl, 0, GetVar, name, strlen(name) + 1, val, len);
1670*5c47fe09SDavid du Colombier }
1671*5c47fe09SDavid du Colombier 
1672*5c47fe09SDavid du Colombier static void
wlsetvar(Ctlr * ctl,char * name,void * val,int len)1673*5c47fe09SDavid du Colombier wlsetvar(Ctlr *ctl, char *name, void *val, int len)
1674*5c47fe09SDavid du Colombier {
1675*5c47fe09SDavid du Colombier 	if(VARDEBUG){
1676*5c47fe09SDavid du Colombier 		char buf[32];
1677*5c47fe09SDavid du Colombier 		snprint(buf, sizeof buf, "wlsetvar %s:", name);
1678*5c47fe09SDavid du Colombier 		dump(buf, val, len);
1679*5c47fe09SDavid du Colombier 	}
1680*5c47fe09SDavid du Colombier 	wlcmd(ctl, 1, SetVar, name, strlen(name) + 1, val, len);
1681*5c47fe09SDavid du Colombier }
1682*5c47fe09SDavid du Colombier 
1683*5c47fe09SDavid du Colombier static void
wlsetint(Ctlr * ctl,char * name,int val)1684*5c47fe09SDavid du Colombier wlsetint(Ctlr *ctl, char *name, int val)
1685*5c47fe09SDavid du Colombier {
1686*5c47fe09SDavid du Colombier 	uchar buf[4];
1687*5c47fe09SDavid du Colombier 
1688*5c47fe09SDavid du Colombier 	put4(buf, val);
1689*5c47fe09SDavid du Colombier 	wlsetvar(ctl, name, buf, 4);
1690*5c47fe09SDavid du Colombier }
1691*5c47fe09SDavid du Colombier 
1692*5c47fe09SDavid du Colombier static void
wlwepkey(Ctlr * ctl,int i)1693*5c47fe09SDavid du Colombier wlwepkey(Ctlr *ctl, int i)
1694*5c47fe09SDavid du Colombier {
1695*5c47fe09SDavid du Colombier 	uchar params[164];
1696*5c47fe09SDavid du Colombier 	uchar *p;
1697*5c47fe09SDavid du Colombier 
1698*5c47fe09SDavid du Colombier 	memset(params, 0, sizeof params);
1699*5c47fe09SDavid du Colombier 	p = params;
1700*5c47fe09SDavid du Colombier 	p = put4(p, i);		/* index */
1701*5c47fe09SDavid du Colombier 	p = put4(p, ctl->keys[i].len);
1702*5c47fe09SDavid du Colombier 	memmove(p, ctl->keys[i].dat, ctl->keys[i].len);
1703*5c47fe09SDavid du Colombier 	p += 32 + 18*4;		/* keydata, pad */
1704*5c47fe09SDavid du Colombier 	if(ctl->keys[i].len == WMinKeyLen)
1705*5c47fe09SDavid du Colombier 		p = put4(p, 1);		/* algo = WEP1 */
1706*5c47fe09SDavid du Colombier 	else
1707*5c47fe09SDavid du Colombier 		p = put4(p, 3);		/* algo = WEP128 */
1708*5c47fe09SDavid du Colombier 	put4(p, 2);		/* flags = Primarykey */
1709*5c47fe09SDavid du Colombier 
1710*5c47fe09SDavid du Colombier 	wlsetvar(ctl, "wsec_key", params, sizeof params);
1711*5c47fe09SDavid du Colombier }
1712*5c47fe09SDavid du Colombier 
1713*5c47fe09SDavid du Colombier static void
memreverse(char * dst,char * src,int len)1714*5c47fe09SDavid du Colombier memreverse(char *dst, char *src, int len)
1715*5c47fe09SDavid du Colombier {
1716*5c47fe09SDavid du Colombier 	src += len;
1717*5c47fe09SDavid du Colombier 	while(len-- > 0)
1718*5c47fe09SDavid du Colombier 		*dst++ = *--src;
1719*5c47fe09SDavid du Colombier }
1720*5c47fe09SDavid du Colombier 
1721*5c47fe09SDavid du Colombier static void
wlwpakey(Ctlr * ctl,int id,uvlong iv,uchar * ea)1722*5c47fe09SDavid du Colombier wlwpakey(Ctlr *ctl, int id, uvlong iv, uchar *ea)
1723*5c47fe09SDavid du Colombier {
1724*5c47fe09SDavid du Colombier 	uchar params[164];
1725*5c47fe09SDavid du Colombier 	uchar *p;
1726*5c47fe09SDavid du Colombier 	int pairwise;
1727*5c47fe09SDavid du Colombier 
1728*5c47fe09SDavid du Colombier 	if(id == CMrxkey)
1729*5c47fe09SDavid du Colombier 		return;
1730*5c47fe09SDavid du Colombier 	pairwise = (id == CMrxkey || id == CMtxkey);
1731*5c47fe09SDavid du Colombier 	memset(params, 0, sizeof params);
1732*5c47fe09SDavid du Colombier 	p = params;
1733*5c47fe09SDavid du Colombier 	if(pairwise)
1734*5c47fe09SDavid du Colombier 		p = put4(p, 0);
1735*5c47fe09SDavid du Colombier 	else
1736*5c47fe09SDavid du Colombier 		p = put4(p, id - CMrxkey0);	/* group key id */
1737*5c47fe09SDavid du Colombier 	p = put4(p, ctl->keys[0].len);
1738*5c47fe09SDavid du Colombier 	memmove((char*)p,  ctl->keys[0].dat, ctl->keys[0].len);
1739*5c47fe09SDavid du Colombier 	p += 32 + 18*4;		/* keydata, pad */
1740*5c47fe09SDavid du Colombier 	if(ctl->cryptotype == Wpa)
1741*5c47fe09SDavid du Colombier 		p = put4(p, 2);	/* algo = TKIP */
1742*5c47fe09SDavid du Colombier 	else
1743*5c47fe09SDavid du Colombier 		p = put4(p, 4);	/* algo = AES_CCM */
1744*5c47fe09SDavid du Colombier 	if(pairwise)
1745*5c47fe09SDavid du Colombier 		p = put4(p, 0);
1746*5c47fe09SDavid du Colombier 	else
1747*5c47fe09SDavid du Colombier 		p = put4(p, 2);		/* flags = Primarykey */
1748*5c47fe09SDavid du Colombier 	p += 3*4;
1749*5c47fe09SDavid du Colombier 	p = put4(p, 0); //pairwise);		/* iv initialised */
1750*5c47fe09SDavid du Colombier 	p += 4;
1751*5c47fe09SDavid du Colombier 	p = put4(p, iv>>16);	/* iv high */
1752*5c47fe09SDavid du Colombier 	p = put2(p, iv&0xFFFF);	/* iv low */
1753*5c47fe09SDavid du Colombier 	p += 2 + 2*4;		/* align, pad */
1754*5c47fe09SDavid du Colombier 	if(pairwise)
1755*5c47fe09SDavid du Colombier 		memmove(p, ea, Eaddrlen);
1756*5c47fe09SDavid du Colombier 
1757*5c47fe09SDavid du Colombier 	wlsetvar(ctl, "wsec_key", params, sizeof params);
1758*5c47fe09SDavid du Colombier }
1759*5c47fe09SDavid du Colombier 
1760*5c47fe09SDavid du Colombier static void
wljoin(Ctlr * ctl,char * ssid,int chan)1761*5c47fe09SDavid du Colombier wljoin(Ctlr *ctl, char *ssid, int chan)
1762*5c47fe09SDavid du Colombier {
1763*5c47fe09SDavid du Colombier 	uchar params[72];
1764*5c47fe09SDavid du Colombier 	uchar *p;
1765*5c47fe09SDavid du Colombier 	int n;
1766*5c47fe09SDavid du Colombier 
1767*5c47fe09SDavid du Colombier 	if(chan != 0)
1768*5c47fe09SDavid du Colombier 		chan |= 0x2b00;		/* 20Mhz channel width */
1769*5c47fe09SDavid du Colombier 	p = params;
1770*5c47fe09SDavid du Colombier 	n = strlen(ssid);
1771*5c47fe09SDavid du Colombier 	n = MIN(n, 32);
1772*5c47fe09SDavid du Colombier 	p = put4(p, n);
1773*5c47fe09SDavid du Colombier 	memmove(p, ssid, n);
1774*5c47fe09SDavid du Colombier 	memset(p + n, 0, 32 - n);
1775*5c47fe09SDavid du Colombier 	p += 32;
1776*5c47fe09SDavid du Colombier 	p = put4(p, 0xff);	/* scan type */
1777*5c47fe09SDavid du Colombier 	if(chan != 0){
1778*5c47fe09SDavid du Colombier 		p = put4(p, 2);		/* num probes */
1779*5c47fe09SDavid du Colombier 		p = put4(p, 120);	/* active time */
1780*5c47fe09SDavid du Colombier 		p = put4(p, 390);	/* passive time */
1781*5c47fe09SDavid du Colombier 	}else{
1782*5c47fe09SDavid du Colombier 		p = put4(p, -1);	/* num probes */
1783*5c47fe09SDavid du Colombier 		p = put4(p, -1);	/* active time */
1784*5c47fe09SDavid du Colombier 		p = put4(p, -1);	/* passive time */
1785*5c47fe09SDavid du Colombier 	}
1786*5c47fe09SDavid du Colombier 	p = put4(p, -1);	/* home time */
1787*5c47fe09SDavid du Colombier 	memset(p, 0xFF, Eaddrlen);	/* bssid */
1788*5c47fe09SDavid du Colombier 	p += Eaddrlen;
1789*5c47fe09SDavid du Colombier 	p = put2(p, 0);		/* pad */
1790*5c47fe09SDavid du Colombier 	if(chan != 0){
1791*5c47fe09SDavid du Colombier 		p = put4(p, 1);		/* num chans */
1792*5c47fe09SDavid du Colombier 		p = put2(p, chan);	/* chan spec */
1793*5c47fe09SDavid du Colombier 		p = put2(p, 0);		/* pad */
1794*5c47fe09SDavid du Colombier 		assert(p == params + sizeof(params));
1795*5c47fe09SDavid du Colombier 	}else{
1796*5c47fe09SDavid du Colombier 		p = put4(p, 0);		/* num chans */
1797*5c47fe09SDavid du Colombier 		assert(p == params + sizeof(params) - 4);
1798*5c47fe09SDavid du Colombier 	}
1799*5c47fe09SDavid du Colombier 
1800*5c47fe09SDavid du Colombier 	wlsetvar(ctl, "join", params, chan? sizeof params : sizeof params - 4);
1801*5c47fe09SDavid du Colombier 	ctl->status = Connecting;
1802*5c47fe09SDavid du Colombier 	switch(waitjoin(ctl)){
1803*5c47fe09SDavid du Colombier 		case 0:
1804*5c47fe09SDavid du Colombier 			ctl->status = Connected;
1805*5c47fe09SDavid du Colombier 			break;
1806*5c47fe09SDavid du Colombier 		case 3:
1807*5c47fe09SDavid du Colombier 			ctl->status = Disconnected;
1808*5c47fe09SDavid du Colombier 			error("wifi join: network not found");
1809*5c47fe09SDavid du Colombier 		case 1:
1810*5c47fe09SDavid du Colombier 			ctl->status = Disconnected;
1811*5c47fe09SDavid du Colombier 			error("wifi join: failed");
1812*5c47fe09SDavid du Colombier 		default:
1813*5c47fe09SDavid du Colombier 			ctl->status = Disconnected;
1814*5c47fe09SDavid du Colombier 			error("wifi join: error");
1815*5c47fe09SDavid du Colombier 	}
1816*5c47fe09SDavid du Colombier }
1817*5c47fe09SDavid du Colombier 
1818*5c47fe09SDavid du Colombier static void
wlscanstart(Ctlr * ctl)1819*5c47fe09SDavid du Colombier wlscanstart(Ctlr *ctl)
1820*5c47fe09SDavid du Colombier {
1821*5c47fe09SDavid du Colombier 	/* version[4] action[2] sync_id[2] ssidlen[4] ssid[32] bssid[6] bss_type[1]
1822*5c47fe09SDavid du Colombier 		scan_type[1] nprobes[4] active_time[4] passive_time[4] home_time[4]
1823*5c47fe09SDavid du Colombier 		nchans[2] nssids[2] chans[nchans][2] ssids[nssids][32] */
1824*5c47fe09SDavid du Colombier 	/* hack - this is only correct on a little-endian cpu */
1825*5c47fe09SDavid du Colombier 	static uchar params[4+2+2+4+32+6+1+1+4*4+2+2+14*2+32+4] = {
1826*5c47fe09SDavid du Colombier 		1,0,0,0,
1827*5c47fe09SDavid du Colombier 		1,0,
1828*5c47fe09SDavid du Colombier 		0x34,0x12,
1829*5c47fe09SDavid du Colombier 		0,0,0,0,
1830*5c47fe09SDavid du Colombier 		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1831*5c47fe09SDavid du Colombier 		0xff,0xff,0xff,0xff,0xff,0xff,
1832*5c47fe09SDavid du Colombier 		2,
1833*5c47fe09SDavid du Colombier 		0,
1834*5c47fe09SDavid du Colombier 		0xff,0xff,0xff,0xff,
1835*5c47fe09SDavid du Colombier 		0xff,0xff,0xff,0xff,
1836*5c47fe09SDavid du Colombier 		0xff,0xff,0xff,0xff,
1837*5c47fe09SDavid du Colombier 		0xff,0xff,0xff,0xff,
1838*5c47fe09SDavid du Colombier 		14,0,
1839*5c47fe09SDavid du Colombier 		1,0,
1840*5c47fe09SDavid du Colombier 		0x01,0x2b,0x02,0x2b,0x03,0x2b,0x04,0x2b,0x05,0x2e,0x06,0x2e,0x07,0x2e,
1841*5c47fe09SDavid du Colombier 		0x08,0x2b,0x09,0x2b,0x0a,0x2b,0x0b,0x2b,0x0c,0x2b,0x0d,0x2b,0x0e,0x2b,
1842*5c47fe09SDavid du Colombier 		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1843*5c47fe09SDavid du Colombier 	};
1844*5c47fe09SDavid du Colombier 
1845*5c47fe09SDavid du Colombier 	wlcmdint(ctl, 49, 0);	/* PASSIVE_SCAN */
1846*5c47fe09SDavid du Colombier 	wlsetvar(ctl, "escan", params, sizeof params);
1847*5c47fe09SDavid du Colombier }
1848*5c47fe09SDavid du Colombier 
1849*5c47fe09SDavid du Colombier static uchar*
gettlv(uchar * p,uchar * ep,int tag)1850*5c47fe09SDavid du Colombier gettlv(uchar *p, uchar *ep, int tag)
1851*5c47fe09SDavid du Colombier {
1852*5c47fe09SDavid du Colombier 	int len;
1853*5c47fe09SDavid du Colombier 
1854*5c47fe09SDavid du Colombier 	while(p + 1 < ep){
1855*5c47fe09SDavid du Colombier 		len = p[1];
1856*5c47fe09SDavid du Colombier 		if(p + 2 + len > ep)
1857*5c47fe09SDavid du Colombier 			return nil;
1858*5c47fe09SDavid du Colombier 		if(p[0] == tag)
1859*5c47fe09SDavid du Colombier 			return p;
1860*5c47fe09SDavid du Colombier 		p += 2 + len;
1861*5c47fe09SDavid du Colombier 	}
1862*5c47fe09SDavid du Colombier 	return nil;
1863*5c47fe09SDavid du Colombier }
1864*5c47fe09SDavid du Colombier 
1865*5c47fe09SDavid du Colombier static void
addscan(Block * bp,uchar * p,int len)1866*5c47fe09SDavid du Colombier addscan(Block *bp, uchar *p, int len)
1867*5c47fe09SDavid du Colombier {
1868*5c47fe09SDavid du Colombier 	char bssid[24];
1869*5c47fe09SDavid du Colombier 	char *auth, *auth2;
1870*5c47fe09SDavid du Colombier 	uchar *t, *et;
1871*5c47fe09SDavid du Colombier 	int ielen;
1872*5c47fe09SDavid du Colombier 	static uchar wpaie1[4] = { 0x00, 0x50, 0xf2, 0x01 };
1873*5c47fe09SDavid du Colombier 
1874*5c47fe09SDavid du Colombier 	snprint(bssid, sizeof bssid, ";bssid=%E", p + 8);
1875*5c47fe09SDavid du Colombier 	if(strstr((char*)bp->rp, bssid) != nil)
1876*5c47fe09SDavid du Colombier 		return;
1877*5c47fe09SDavid du Colombier 	bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
1878*5c47fe09SDavid du Colombier 		"ssid=%.*s%s;signal=%d;noise=%d;chan=%d",
1879*5c47fe09SDavid du Colombier 		p[18], (char*)p+19, bssid,
1880*5c47fe09SDavid du Colombier 		(short)get2(p+78), (signed char)p[80],
1881*5c47fe09SDavid du Colombier 		get2(p+72) & 0xF);
1882*5c47fe09SDavid du Colombier 	auth = auth2 = "";
1883*5c47fe09SDavid du Colombier 	if(get2(p + 16) & 0x10)
1884*5c47fe09SDavid du Colombier 		auth = ";wep";
1885*5c47fe09SDavid du Colombier 	ielen = get4(p + 0x78);
1886*5c47fe09SDavid du Colombier 	if(ielen > 0){
1887*5c47fe09SDavid du Colombier 		t = p + get4(p + 0x74);
1888*5c47fe09SDavid du Colombier 		et = t + ielen;
1889*5c47fe09SDavid du Colombier 		if(et > p + len)
1890*5c47fe09SDavid du Colombier 			return;
1891*5c47fe09SDavid du Colombier 		if(gettlv(t, et, 0x30) != nil){
1892*5c47fe09SDavid du Colombier 			auth = "";
1893*5c47fe09SDavid du Colombier 			auth2 = ";wpa2";
1894*5c47fe09SDavid du Colombier 		}
1895*5c47fe09SDavid du Colombier 		while((t = gettlv(t, et, 0xdd)) != nil){
1896*5c47fe09SDavid du Colombier 			if(t[1] > 4 && memcmp(t+2, wpaie1, 4) == 0){
1897*5c47fe09SDavid du Colombier 				auth = ";wpa";
1898*5c47fe09SDavid du Colombier 				break;
1899*5c47fe09SDavid du Colombier 			}
1900*5c47fe09SDavid du Colombier 			t += 2 + t[1];
1901*5c47fe09SDavid du Colombier 		}
1902*5c47fe09SDavid du Colombier 	}
1903*5c47fe09SDavid du Colombier 	bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
1904*5c47fe09SDavid du Colombier 		"%s%s\n", auth, auth2);
1905*5c47fe09SDavid du Colombier }
1906*5c47fe09SDavid du Colombier 
1907*5c47fe09SDavid du Colombier 
1908*5c47fe09SDavid du Colombier static void
wlscanresult(Ether * edev,uchar * p,int len)1909*5c47fe09SDavid du Colombier wlscanresult(Ether *edev, uchar *p, int len)
1910*5c47fe09SDavid du Colombier {
1911*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1912*5c47fe09SDavid du Colombier 	Netfile **ep, *f, **fp;
1913*5c47fe09SDavid du Colombier 	Block *bp;
1914*5c47fe09SDavid du Colombier 	int nbss, i;
1915*5c47fe09SDavid du Colombier 
1916*5c47fe09SDavid du Colombier 	ctlr = edev->ctlr;
1917*5c47fe09SDavid du Colombier 	if(get4(p) > len)
1918*5c47fe09SDavid du Colombier 		return;
1919*5c47fe09SDavid du Colombier 	/* TODO: more syntax checking */
1920*5c47fe09SDavid du Colombier 	bp = ctlr->scanb;
1921*5c47fe09SDavid du Colombier 	if(bp == nil)
1922*5c47fe09SDavid du Colombier 		ctlr->scanb = bp = allocb(8192);
1923*5c47fe09SDavid du Colombier 	nbss = get2(p+10);
1924*5c47fe09SDavid du Colombier 	p += 12;
1925*5c47fe09SDavid du Colombier 	len -= 12;
1926*5c47fe09SDavid du Colombier 	if(0) dump("SCAN", p, len);
1927*5c47fe09SDavid du Colombier 	if(nbss){
1928*5c47fe09SDavid du Colombier 		addscan(bp, p, len);
1929*5c47fe09SDavid du Colombier 		return;
1930*5c47fe09SDavid du Colombier 	}
1931*5c47fe09SDavid du Colombier 	i = edev->scan;
1932*5c47fe09SDavid du Colombier 	ep = &edev->f[Ntypes];
1933*5c47fe09SDavid du Colombier 	for(fp = edev->f; fp < ep && i > 0; fp++){
1934*5c47fe09SDavid du Colombier 		f = *fp;
1935*5c47fe09SDavid du Colombier 		if(f == nil || f->scan == 0)
1936*5c47fe09SDavid du Colombier 			continue;
1937*5c47fe09SDavid du Colombier 		if(i == 1)
1938*5c47fe09SDavid du Colombier 			qpass(f->in, bp);
1939*5c47fe09SDavid du Colombier 		else
1940*5c47fe09SDavid du Colombier 			qpass(f->in, copyblock(bp, BLEN(bp)));
1941*5c47fe09SDavid du Colombier 		i--;
1942*5c47fe09SDavid du Colombier 	}
1943*5c47fe09SDavid du Colombier 	if(i)
1944*5c47fe09SDavid du Colombier 		freeb(bp);
1945*5c47fe09SDavid du Colombier 	ctlr->scanb = nil;
1946*5c47fe09SDavid du Colombier }
1947*5c47fe09SDavid du Colombier 
1948*5c47fe09SDavid du Colombier static void
lproc(void * a)1949*5c47fe09SDavid du Colombier lproc(void *a)
1950*5c47fe09SDavid du Colombier {
1951*5c47fe09SDavid du Colombier 	Ether *edev;
1952*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1953*5c47fe09SDavid du Colombier 	int secs;
1954*5c47fe09SDavid du Colombier 
1955*5c47fe09SDavid du Colombier 	edev = a;
1956*5c47fe09SDavid du Colombier 	ctlr = edev->ctlr;
1957*5c47fe09SDavid du Colombier 	secs = 0;
1958*5c47fe09SDavid du Colombier 	for(;;){
1959*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 1000);
1960*5c47fe09SDavid du Colombier 		if(ctlr->scansecs){
1961*5c47fe09SDavid du Colombier 			if(secs == 0){
1962*5c47fe09SDavid du Colombier 				if(waserror())
1963*5c47fe09SDavid du Colombier 					ctlr->scansecs = 0;
1964*5c47fe09SDavid du Colombier 				else{
1965*5c47fe09SDavid du Colombier 					wlscanstart(ctlr);
1966*5c47fe09SDavid du Colombier 					poperror();
1967*5c47fe09SDavid du Colombier 				}
1968*5c47fe09SDavid du Colombier 				secs = ctlr->scansecs;
1969*5c47fe09SDavid du Colombier 			}
1970*5c47fe09SDavid du Colombier 			--secs;
1971*5c47fe09SDavid du Colombier 		}else
1972*5c47fe09SDavid du Colombier 			secs = 0;
1973*5c47fe09SDavid du Colombier 	}
1974*5c47fe09SDavid du Colombier }
1975*5c47fe09SDavid du Colombier 
1976*5c47fe09SDavid du Colombier static void
wlinit(Ether * edev,Ctlr * ctlr)1977*5c47fe09SDavid du Colombier wlinit(Ether *edev, Ctlr *ctlr)
1978*5c47fe09SDavid du Colombier {
1979*5c47fe09SDavid du Colombier 	uchar ea[Eaddrlen];
1980*5c47fe09SDavid du Colombier 	uchar eventmask[16];
1981*5c47fe09SDavid du Colombier 	char version[128];
1982*5c47fe09SDavid du Colombier 	char *p;
1983*5c47fe09SDavid du Colombier 	static uchar keepalive[12] = {1, 0, 11, 0, 0xd8, 0xd6, 0, 0, 0, 0, 0, 0};
1984*5c47fe09SDavid du Colombier 
1985*5c47fe09SDavid du Colombier 	wlgetvar(ctlr, "cur_etheraddr", ea, Eaddrlen);
1986*5c47fe09SDavid du Colombier 	memmove(edev->ea, ea, Eaddrlen);
1987*5c47fe09SDavid du Colombier 	memmove(edev->addr, ea, Eaddrlen);
1988*5c47fe09SDavid du Colombier 	print("ether4330: addr %E\n", edev->ea);
1989*5c47fe09SDavid du Colombier 	wlsetint(ctlr, "assoc_listen", 10);
1990*5c47fe09SDavid du Colombier 	if(ctlr->chipid == 43430 || ctlr->chipid == 0x4345)
1991*5c47fe09SDavid du Colombier 		wlcmdint(ctlr, 0x56, 0);	/* powersave off */
1992*5c47fe09SDavid du Colombier 	else
1993*5c47fe09SDavid du Colombier 		wlcmdint(ctlr, 0x56, 2);	/* powersave FAST */
1994*5c47fe09SDavid du Colombier 	wlsetint(ctlr, "bus:txglom", 0);
1995*5c47fe09SDavid du Colombier 	wlsetint(ctlr, "bcn_timeout", 10);
1996*5c47fe09SDavid du Colombier 	wlsetint(ctlr, "assoc_retry_max", 3);
1997*5c47fe09SDavid du Colombier 	if(ctlr->chipid == 0x4330){
1998*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "btc_wire", 4);
1999*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "btc_mode", 1);
2000*5c47fe09SDavid du Colombier 		wlsetvar(ctlr, "mkeep_alive", keepalive, 11);
2001*5c47fe09SDavid du Colombier 	}
2002*5c47fe09SDavid du Colombier 	memset(eventmask, 0xFF, sizeof eventmask);
2003*5c47fe09SDavid du Colombier #define ENABLE(n)	eventmask[n/8] |= 1<<(n%8)
2004*5c47fe09SDavid du Colombier #define DISABLE(n)	eventmask[n/8] &= ~(1<<(n%8))
2005*5c47fe09SDavid du Colombier 	DISABLE(40);	/* E_RADIO */
2006*5c47fe09SDavid du Colombier 	DISABLE(44);	/* E_PROBREQ_MSG */
2007*5c47fe09SDavid du Colombier 	DISABLE(54);	/* E_IF */
2008*5c47fe09SDavid du Colombier 	DISABLE(71);	/* E_PROBRESP_MSG */
2009*5c47fe09SDavid du Colombier 	DISABLE(20);	/* E_TXFAIL */
2010*5c47fe09SDavid du Colombier 	DISABLE(124);	/* ? */
2011*5c47fe09SDavid du Colombier 	wlsetvar(ctlr, "event_msgs", eventmask, sizeof eventmask);
2012*5c47fe09SDavid du Colombier 	wlcmdint(ctlr, 0xb9, 0x28);	/* SET_SCAN_CHANNEL_TIME */
2013*5c47fe09SDavid du Colombier 	wlcmdint(ctlr, 0xbb, 0x28);	/* SET_SCAN_UNASSOC_TIME */
2014*5c47fe09SDavid du Colombier 	wlcmdint(ctlr, 0x102, 0x82);	/* SET_SCAN_PASSIVE_TIME */
2015*5c47fe09SDavid du Colombier 	wlcmdint(ctlr, 2, 0);		/* UP */
2016*5c47fe09SDavid du Colombier 	memset(version, 0, sizeof version);
2017*5c47fe09SDavid du Colombier 	wlgetvar(ctlr, "ver", version, sizeof version - 1);
2018*5c47fe09SDavid du Colombier 	if((p = strchr(version, '\n')) != nil)
2019*5c47fe09SDavid du Colombier 		*p = '\0';
2020*5c47fe09SDavid du Colombier 	if(0) print("ether4330: %s\n", version);
2021*5c47fe09SDavid du Colombier 	wlsetint(ctlr, "roam_off", 1);
2022*5c47fe09SDavid du Colombier 	wlcmdint(ctlr, 0x14, 1);	/* SET_INFRA 1 */
2023*5c47fe09SDavid du Colombier 	wlcmdint(ctlr, 10, 0);		/* SET_PROMISC */
2024*5c47fe09SDavid du Colombier 	//wlcmdint(ctlr, 0x8e, 0);	/* SET_BAND 0 */
2025*5c47fe09SDavid du Colombier 	//wlsetint(ctlr, "wsec", 1);
2026*5c47fe09SDavid du Colombier 	wlcmdint(ctlr, 2, 1);		/* UP */
2027*5c47fe09SDavid du Colombier 	ctlr->keys[0].len = WMinKeyLen;
2028*5c47fe09SDavid du Colombier 	//wlwepkey(ctlr, 0);
2029*5c47fe09SDavid du Colombier }
2030*5c47fe09SDavid du Colombier 
2031*5c47fe09SDavid du Colombier /*
2032*5c47fe09SDavid du Colombier  * Plan 9 driver interface
2033*5c47fe09SDavid du Colombier  */
2034*5c47fe09SDavid du Colombier 
2035*5c47fe09SDavid du Colombier static long
etherbcmifstat(Ether * edev,void * a,long n,ulong offset)2036*5c47fe09SDavid du Colombier etherbcmifstat(Ether* edev, void* a, long n, ulong offset)
2037*5c47fe09SDavid du Colombier {
2038*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
2039*5c47fe09SDavid du Colombier 	char *p;
2040*5c47fe09SDavid du Colombier 	int l;
2041*5c47fe09SDavid du Colombier 	static char *cryptoname[4] = {
2042*5c47fe09SDavid du Colombier 		[0]	"off",
2043*5c47fe09SDavid du Colombier 		[Wep]	"wep",
2044*5c47fe09SDavid du Colombier 		[Wpa]	"wpa",
2045*5c47fe09SDavid du Colombier 		[Wpa2]	"wpa2",
2046*5c47fe09SDavid du Colombier 	};
2047*5c47fe09SDavid du Colombier 	/* these strings are known by aux/wpa */
2048*5c47fe09SDavid du Colombier 	static char* connectstate[] = {
2049*5c47fe09SDavid du Colombier 		[Disconnected]	= "unassociated",
2050*5c47fe09SDavid du Colombier 		[Connecting] = "connecting",
2051*5c47fe09SDavid du Colombier 		[Connected] = "associated",
2052*5c47fe09SDavid du Colombier 	};
2053*5c47fe09SDavid du Colombier 
2054*5c47fe09SDavid du Colombier 	ctlr = edev->ctlr;
2055*5c47fe09SDavid du Colombier 	if(ctlr == nil)
2056*5c47fe09SDavid du Colombier 		return 0;
2057*5c47fe09SDavid du Colombier 	p = malloc(READSTR);
2058*5c47fe09SDavid du Colombier 	l = 0;
2059*5c47fe09SDavid du Colombier 
2060*5c47fe09SDavid du Colombier 	l += snprint(p+l, READSTR-l, "channel: %d\n", ctlr->chanid);
2061*5c47fe09SDavid du Colombier 	l += snprint(p+l, READSTR-l, "essid: %s\n", ctlr->essid);
2062*5c47fe09SDavid du Colombier 	l += snprint(p+l, READSTR-l, "crypt: %s\n", cryptoname[ctlr->cryptotype]);
2063*5c47fe09SDavid du Colombier 	l += snprint(p+l, READSTR-l, "oq: %d\n", qlen(edev->oq));
2064*5c47fe09SDavid du Colombier 	l += snprint(p+l, READSTR-l, "txwin: %d\n", ctlr->txwindow);
2065*5c47fe09SDavid du Colombier 	l += snprint(p+l, READSTR-l, "txseq: %d\n", ctlr->txseq);
2066*5c47fe09SDavid du Colombier 	l += snprint(p+l, READSTR-l, "status: %s\n", connectstate[ctlr->status]);
2067*5c47fe09SDavid du Colombier 	USED(l);
2068*5c47fe09SDavid du Colombier 	n = readstr(offset, a, n, p);
2069*5c47fe09SDavid du Colombier 	free(p);
2070*5c47fe09SDavid du Colombier 	return n;
2071*5c47fe09SDavid du Colombier }
2072*5c47fe09SDavid du Colombier 
2073*5c47fe09SDavid du Colombier static void
etherbcmtransmit(Ether * edev)2074*5c47fe09SDavid du Colombier etherbcmtransmit(Ether *edev)
2075*5c47fe09SDavid du Colombier {
2076*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
2077*5c47fe09SDavid du Colombier 
2078*5c47fe09SDavid du Colombier 	ctlr = edev->ctlr;
2079*5c47fe09SDavid du Colombier 	if(ctlr == nil)
2080*5c47fe09SDavid du Colombier 		return;
2081*5c47fe09SDavid du Colombier 	txstart(edev);
2082*5c47fe09SDavid du Colombier }
2083*5c47fe09SDavid du Colombier 
2084*5c47fe09SDavid du Colombier static int
parsehex(char * buf,int buflen,char * a)2085*5c47fe09SDavid du Colombier parsehex(char *buf, int buflen, char *a)
2086*5c47fe09SDavid du Colombier {
2087*5c47fe09SDavid du Colombier 	int i, k, n;
2088*5c47fe09SDavid du Colombier 
2089*5c47fe09SDavid du Colombier 	k = 0;
2090*5c47fe09SDavid du Colombier 	for(i = 0;k < buflen && *a; i++){
2091*5c47fe09SDavid du Colombier 		if(*a >= '0' && *a <= '9')
2092*5c47fe09SDavid du Colombier 			n = *a++ - '0';
2093*5c47fe09SDavid du Colombier 		else if(*a >= 'a' && *a <= 'f')
2094*5c47fe09SDavid du Colombier 			n = *a++ - 'a' + 10;
2095*5c47fe09SDavid du Colombier 		else if(*a >= 'A' && *a <= 'F')
2096*5c47fe09SDavid du Colombier 			n = *a++ - 'A' + 10;
2097*5c47fe09SDavid du Colombier 		else
2098*5c47fe09SDavid du Colombier 			break;
2099*5c47fe09SDavid du Colombier 
2100*5c47fe09SDavid du Colombier 		if(i & 1){
2101*5c47fe09SDavid du Colombier 			buf[k] |= n;
2102*5c47fe09SDavid du Colombier 			k++;
2103*5c47fe09SDavid du Colombier 		}
2104*5c47fe09SDavid du Colombier 		else
2105*5c47fe09SDavid du Colombier 			buf[k] = n<<4;
2106*5c47fe09SDavid du Colombier 	}
2107*5c47fe09SDavid du Colombier 	if(i & 1)
2108*5c47fe09SDavid du Colombier 		return -1;
2109*5c47fe09SDavid du Colombier 	return k;
2110*5c47fe09SDavid du Colombier }
2111*5c47fe09SDavid du Colombier 
2112*5c47fe09SDavid du Colombier static int
wepparsekey(WKey * key,char * a)2113*5c47fe09SDavid du Colombier wepparsekey(WKey* key, char* a)
2114*5c47fe09SDavid du Colombier {
2115*5c47fe09SDavid du Colombier 	int i, k, len, n;
2116*5c47fe09SDavid du Colombier 	char buf[WMaxKeyLen];
2117*5c47fe09SDavid du Colombier 
2118*5c47fe09SDavid du Colombier 	len = strlen(a);
2119*5c47fe09SDavid du Colombier 	if(len == WMinKeyLen || len == WMaxKeyLen){
2120*5c47fe09SDavid du Colombier 		memset(key->dat, 0, sizeof(key->dat));
2121*5c47fe09SDavid du Colombier 		memmove(key->dat, a, len);
2122*5c47fe09SDavid du Colombier 		key->len = len;
2123*5c47fe09SDavid du Colombier 
2124*5c47fe09SDavid du Colombier 		return 0;
2125*5c47fe09SDavid du Colombier 	}
2126*5c47fe09SDavid du Colombier 	else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
2127*5c47fe09SDavid du Colombier 		k = 0;
2128*5c47fe09SDavid du Colombier 		for(i = 0; i < len; i++){
2129*5c47fe09SDavid du Colombier 			if(*a >= '0' && *a <= '9')
2130*5c47fe09SDavid du Colombier 				n = *a++ - '0';
2131*5c47fe09SDavid du Colombier 			else if(*a >= 'a' && *a <= 'f')
2132*5c47fe09SDavid du Colombier 				n = *a++ - 'a' + 10;
2133*5c47fe09SDavid du Colombier 			else if(*a >= 'A' && *a <= 'F')
2134*5c47fe09SDavid du Colombier 				n = *a++ - 'A' + 10;
2135*5c47fe09SDavid du Colombier 			else
2136*5c47fe09SDavid du Colombier 				return -1;
2137*5c47fe09SDavid du Colombier 
2138*5c47fe09SDavid du Colombier 			if(i & 1){
2139*5c47fe09SDavid du Colombier 				buf[k] |= n;
2140*5c47fe09SDavid du Colombier 				k++;
2141*5c47fe09SDavid du Colombier 			}
2142*5c47fe09SDavid du Colombier 			else
2143*5c47fe09SDavid du Colombier 				buf[k] = n<<4;
2144*5c47fe09SDavid du Colombier 		}
2145*5c47fe09SDavid du Colombier 
2146*5c47fe09SDavid du Colombier 		memset(key->dat, 0, sizeof(key->dat));
2147*5c47fe09SDavid du Colombier 		memmove(key->dat, buf, k);
2148*5c47fe09SDavid du Colombier 		key->len = k;
2149*5c47fe09SDavid du Colombier 
2150*5c47fe09SDavid du Colombier 		return 0;
2151*5c47fe09SDavid du Colombier 	}
2152*5c47fe09SDavid du Colombier 
2153*5c47fe09SDavid du Colombier 	return -1;
2154*5c47fe09SDavid du Colombier }
2155*5c47fe09SDavid du Colombier 
2156*5c47fe09SDavid du Colombier static int
wpaparsekey(WKey * key,uvlong * ivp,char * a)2157*5c47fe09SDavid du Colombier wpaparsekey(WKey *key, uvlong *ivp, char *a)
2158*5c47fe09SDavid du Colombier {
2159*5c47fe09SDavid du Colombier 	int len;
2160*5c47fe09SDavid du Colombier 	char *e;
2161*5c47fe09SDavid du Colombier 
2162*5c47fe09SDavid du Colombier 	if(cistrncmp(a, "tkip:", 5) == 0 || cistrncmp(a, "ccmp:", 5) == 0)
2163*5c47fe09SDavid du Colombier 		a += 5;
2164*5c47fe09SDavid du Colombier 	else
2165*5c47fe09SDavid du Colombier 		return 1;
2166*5c47fe09SDavid du Colombier 	len = parsehex(key->dat, sizeof(key->dat), a);
2167*5c47fe09SDavid du Colombier 	if(len <= 0)
2168*5c47fe09SDavid du Colombier 		return 1;
2169*5c47fe09SDavid du Colombier 	key->len = len;
2170*5c47fe09SDavid du Colombier 	a += 2*len;
2171*5c47fe09SDavid du Colombier 	if(*a++ != '@')
2172*5c47fe09SDavid du Colombier 		return 1;
2173*5c47fe09SDavid du Colombier 	*ivp = strtoull(a, &e, 16);
2174*5c47fe09SDavid du Colombier 	if(e == a)
2175*5c47fe09SDavid du Colombier 		return -1;
2176*5c47fe09SDavid du Colombier 	return 0;
2177*5c47fe09SDavid du Colombier }
2178*5c47fe09SDavid du Colombier 
2179*5c47fe09SDavid du Colombier static void
setauth(Ctlr * ctlr,Cmdbuf * cb,char * a)2180*5c47fe09SDavid du Colombier setauth(Ctlr *ctlr, Cmdbuf *cb, char *a)
2181*5c47fe09SDavid du Colombier {
2182*5c47fe09SDavid du Colombier 	uchar wpaie[32];
2183*5c47fe09SDavid du Colombier 	int i;
2184*5c47fe09SDavid du Colombier 
2185*5c47fe09SDavid du Colombier 	i = parsehex((char*)wpaie, sizeof wpaie, a);
2186*5c47fe09SDavid du Colombier 	if(i < 2 || i != wpaie[1] + 2)
2187*5c47fe09SDavid du Colombier 		cmderror(cb, "bad wpa ie syntax");
2188*5c47fe09SDavid du Colombier 	if(wpaie[0] == 0xdd)
2189*5c47fe09SDavid du Colombier 		ctlr->cryptotype = Wpa;
2190*5c47fe09SDavid du Colombier 	else if(wpaie[0] == 0x30)
2191*5c47fe09SDavid du Colombier 		ctlr->cryptotype = Wpa2;
2192*5c47fe09SDavid du Colombier 	else
2193*5c47fe09SDavid du Colombier 		cmderror(cb, "bad wpa ie");
2194*5c47fe09SDavid du Colombier 	wlsetvar(ctlr, "wpaie", wpaie, i);
2195*5c47fe09SDavid du Colombier 	if(ctlr->cryptotype == Wpa){
2196*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "wpa_auth", 4|2);	/* auth_psk | auth_unspecified */
2197*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "auth", 0);
2198*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "wsec", 2);		/* tkip */
2199*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "wpa_auth", 4);		/* auth_psk */
2200*5c47fe09SDavid du Colombier 	}else{
2201*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "wpa_auth", 0x80|0x40);	/* auth_psk | auth_unspecified */
2202*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "auth", 0);
2203*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "wsec", 4);		/* aes */
2204*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "wpa_auth", 0x80);	/* auth_psk */
2205*5c47fe09SDavid du Colombier 	}
2206*5c47fe09SDavid du Colombier }
2207*5c47fe09SDavid du Colombier 
2208*5c47fe09SDavid du Colombier static int
setcrypt(Ctlr * ctlr,Cmdbuf *,char * a)2209*5c47fe09SDavid du Colombier setcrypt(Ctlr *ctlr, Cmdbuf*, char *a)
2210*5c47fe09SDavid du Colombier {
2211*5c47fe09SDavid du Colombier 	if(cistrcmp(a, "wep") == 0 || cistrcmp(a, "on") == 0)
2212*5c47fe09SDavid du Colombier 		ctlr->cryptotype = Wep;
2213*5c47fe09SDavid du Colombier 	else if(cistrcmp(a, "off") == 0 || cistrcmp(a, "none") == 0)
2214*5c47fe09SDavid du Colombier 		ctlr->cryptotype = 0;
2215*5c47fe09SDavid du Colombier 	else
2216*5c47fe09SDavid du Colombier 		return 0;
2217*5c47fe09SDavid du Colombier 	wlsetint(ctlr, "auth", ctlr->cryptotype);
2218*5c47fe09SDavid du Colombier 	return 1;
2219*5c47fe09SDavid du Colombier }
2220*5c47fe09SDavid du Colombier 
2221*5c47fe09SDavid du Colombier static long
etherbcmctl(Ether * edev,void * buf,long n)2222*5c47fe09SDavid du Colombier etherbcmctl(Ether* edev, void* buf, long n)
2223*5c47fe09SDavid du Colombier {
2224*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
2225*5c47fe09SDavid du Colombier 	Cmdbuf *cb;
2226*5c47fe09SDavid du Colombier 	Cmdtab *ct;
2227*5c47fe09SDavid du Colombier 	uchar ea[Eaddrlen];
2228*5c47fe09SDavid du Colombier 	uvlong iv;
2229*5c47fe09SDavid du Colombier 	int i;
2230*5c47fe09SDavid du Colombier 
2231*5c47fe09SDavid du Colombier 	if((ctlr = edev->ctlr) == nil)
2232*5c47fe09SDavid du Colombier 		error(Enonexist);
2233*5c47fe09SDavid du Colombier 	USED(ctlr);
2234*5c47fe09SDavid du Colombier 
2235*5c47fe09SDavid du Colombier 	cb = parsecmd(buf, n);
2236*5c47fe09SDavid du Colombier 	if(waserror()){
2237*5c47fe09SDavid du Colombier 		free(cb);
2238*5c47fe09SDavid du Colombier 		nexterror();
2239*5c47fe09SDavid du Colombier 	}
2240*5c47fe09SDavid du Colombier 	ct = lookupcmd(cb, cmds, nelem(cmds));
2241*5c47fe09SDavid du Colombier 	switch(ct->index){
2242*5c47fe09SDavid du Colombier 	case CMauth:
2243*5c47fe09SDavid du Colombier 		setauth(ctlr, cb, cb->f[1]);
2244*5c47fe09SDavid du Colombier 		if(ctlr->essid[0])
2245*5c47fe09SDavid du Colombier 			wljoin(ctlr, ctlr->essid, ctlr->chanid);
2246*5c47fe09SDavid du Colombier 		break;
2247*5c47fe09SDavid du Colombier 	case CMchannel:
2248*5c47fe09SDavid du Colombier 		if((i = atoi(cb->f[1])) < 0 || i > 16)
2249*5c47fe09SDavid du Colombier 			cmderror(cb, "bad channel number");
2250*5c47fe09SDavid du Colombier 		//wlcmdint(ctlr, 30, i);	/* SET_CHANNEL */
2251*5c47fe09SDavid du Colombier 		ctlr->chanid = i;
2252*5c47fe09SDavid du Colombier 		break;
2253*5c47fe09SDavid du Colombier 	case CMcrypt:
2254*5c47fe09SDavid du Colombier 		if(setcrypt(ctlr, cb, cb->f[1])){
2255*5c47fe09SDavid du Colombier 			if(ctlr->essid[0])
2256*5c47fe09SDavid du Colombier 				wljoin(ctlr, ctlr->essid, ctlr->chanid);
2257*5c47fe09SDavid du Colombier 		}else
2258*5c47fe09SDavid du Colombier 			cmderror(cb, "bad crypt type");
2259*5c47fe09SDavid du Colombier 		break;
2260*5c47fe09SDavid du Colombier 	case CMessid:
2261*5c47fe09SDavid du Colombier 		if(cistrcmp(cb->f[1], "default") == 0)
2262*5c47fe09SDavid du Colombier 			memset(ctlr->essid, 0, sizeof(ctlr->essid));
2263*5c47fe09SDavid du Colombier 		else{
2264*5c47fe09SDavid du Colombier 			strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid) - 1);
2265*5c47fe09SDavid du Colombier 			ctlr->essid[sizeof(ctlr->essid) - 1] = '\0';
2266*5c47fe09SDavid du Colombier 		}
2267*5c47fe09SDavid du Colombier 		if(!waserror()){
2268*5c47fe09SDavid du Colombier 			wljoin(ctlr, ctlr->essid, ctlr->chanid);
2269*5c47fe09SDavid du Colombier 			poperror();
2270*5c47fe09SDavid du Colombier 		}
2271*5c47fe09SDavid du Colombier 		break;
2272*5c47fe09SDavid du Colombier 	case CMjoin:	/* join essid channel wep|on|off|wpakey */
2273*5c47fe09SDavid du Colombier 		if(strcmp(cb->f[1], "") != 0){	/* empty string for no change */
2274*5c47fe09SDavid du Colombier 			if(cistrcmp(cb->f[1], "default") != 0){
2275*5c47fe09SDavid du Colombier 				strncpy(ctlr->essid, cb->f[1], sizeof(ctlr->essid)-1);
2276*5c47fe09SDavid du Colombier 				ctlr->essid[sizeof(ctlr->essid)-1] = 0;
2277*5c47fe09SDavid du Colombier 			}else
2278*5c47fe09SDavid du Colombier 				memset(ctlr->essid, 0, sizeof(ctlr->essid));
2279*5c47fe09SDavid du Colombier 		}else if(ctlr->essid[0] == 0)
2280*5c47fe09SDavid du Colombier 			cmderror(cb, "essid not set");
2281*5c47fe09SDavid du Colombier 		if((i = atoi(cb->f[2])) >= 0 && i <= 16)
2282*5c47fe09SDavid du Colombier 			ctlr->chanid = i;
2283*5c47fe09SDavid du Colombier 		else
2284*5c47fe09SDavid du Colombier 			cmderror(cb, "bad channel number");
2285*5c47fe09SDavid du Colombier 		if(!setcrypt(ctlr, cb, cb->f[3]))
2286*5c47fe09SDavid du Colombier 			setauth(ctlr, cb, cb->f[3]);
2287*5c47fe09SDavid du Colombier 		if(ctlr->essid[0])
2288*5c47fe09SDavid du Colombier 			wljoin(ctlr, ctlr->essid, ctlr->chanid);
2289*5c47fe09SDavid du Colombier 		break;
2290*5c47fe09SDavid du Colombier 	case CMkey1:
2291*5c47fe09SDavid du Colombier 	case CMkey2:
2292*5c47fe09SDavid du Colombier 	case CMkey3:
2293*5c47fe09SDavid du Colombier 	case CMkey4:
2294*5c47fe09SDavid du Colombier 		i = ct->index - CMkey1;
2295*5c47fe09SDavid du Colombier 		if(wepparsekey(&ctlr->keys[i], cb->f[1]))
2296*5c47fe09SDavid du Colombier 			cmderror(cb, "bad WEP key syntax");
2297*5c47fe09SDavid du Colombier 		wlsetint(ctlr, "wsec", 1);	/* wep enabled */
2298*5c47fe09SDavid du Colombier 		wlwepkey(ctlr, i);
2299*5c47fe09SDavid du Colombier 		break;
2300*5c47fe09SDavid du Colombier 	case CMrxkey:
2301*5c47fe09SDavid du Colombier 	case CMrxkey0:
2302*5c47fe09SDavid du Colombier 	case CMrxkey1:
2303*5c47fe09SDavid du Colombier 	case CMrxkey2:
2304*5c47fe09SDavid du Colombier 	case CMrxkey3:
2305*5c47fe09SDavid du Colombier 	case CMtxkey:
2306*5c47fe09SDavid du Colombier 		if(parseether(ea, cb->f[1]) < 0)
2307*5c47fe09SDavid du Colombier 			cmderror(cb, "bad ether addr");
2308*5c47fe09SDavid du Colombier 		if(wpaparsekey(&ctlr->keys[0], &iv, cb->f[2]))
2309*5c47fe09SDavid du Colombier 			cmderror(cb, "bad wpa key");
2310*5c47fe09SDavid du Colombier 		wlwpakey(ctlr, ct->index, iv, ea);
2311*5c47fe09SDavid du Colombier 		break;
2312*5c47fe09SDavid du Colombier 	case CMdebug:
2313*5c47fe09SDavid du Colombier 		iodebug = atoi(cb->f[1]);
2314*5c47fe09SDavid du Colombier 		break;
2315*5c47fe09SDavid du Colombier 	}
2316*5c47fe09SDavid du Colombier 	poperror();
2317*5c47fe09SDavid du Colombier 	free(cb);
2318*5c47fe09SDavid du Colombier 	return n;
2319*5c47fe09SDavid du Colombier }
2320*5c47fe09SDavid du Colombier 
2321*5c47fe09SDavid du Colombier static void
etherbcmscan(void * a,uint secs)2322*5c47fe09SDavid du Colombier etherbcmscan(void *a, uint secs)
2323*5c47fe09SDavid du Colombier {
2324*5c47fe09SDavid du Colombier 	Ether* edev;
2325*5c47fe09SDavid du Colombier 	Ctlr* ctlr;
2326*5c47fe09SDavid du Colombier 
2327*5c47fe09SDavid du Colombier 	edev = a;
2328*5c47fe09SDavid du Colombier 	ctlr = edev->ctlr;
2329*5c47fe09SDavid du Colombier 	ctlr->scansecs = secs;
2330*5c47fe09SDavid du Colombier }
2331*5c47fe09SDavid du Colombier 
2332*5c47fe09SDavid du Colombier static void
etherbcmattach(Ether * edev)2333*5c47fe09SDavid du Colombier etherbcmattach(Ether* edev)
2334*5c47fe09SDavid du Colombier {
2335*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
2336*5c47fe09SDavid du Colombier 
2337*5c47fe09SDavid du Colombier 	ctlr = edev->ctlr;
2338*5c47fe09SDavid du Colombier 	qlock(&ctlr->alock);
2339*5c47fe09SDavid du Colombier 	if(waserror()){
2340*5c47fe09SDavid du Colombier 		//print("ether4330: attach failed: %s\n", up->errstr);
2341*5c47fe09SDavid du Colombier 		qunlock(&ctlr->alock);
2342*5c47fe09SDavid du Colombier 		nexterror();
2343*5c47fe09SDavid du Colombier 	}
2344*5c47fe09SDavid du Colombier 	if(ctlr->edev == nil){
2345*5c47fe09SDavid du Colombier 		if(ctlr->chipid == 0){
2346*5c47fe09SDavid du Colombier 			sdioinit();
2347*5c47fe09SDavid du Colombier 			sbinit(ctlr);
2348*5c47fe09SDavid du Colombier 		}
2349*5c47fe09SDavid du Colombier 		fwload(ctlr);
2350*5c47fe09SDavid du Colombier 		sbenable(ctlr);
2351*5c47fe09SDavid du Colombier 		kproc("wifireader", rproc, edev);
2352*5c47fe09SDavid du Colombier 		kproc("wifitimer", lproc, edev);
2353*5c47fe09SDavid du Colombier 		if(ctlr->regufile)
2354*5c47fe09SDavid du Colombier 			reguload(ctlr, ctlr->regufile);
2355*5c47fe09SDavid du Colombier 		wlinit(edev, ctlr);
2356*5c47fe09SDavid du Colombier 		ctlr->edev = edev;
2357*5c47fe09SDavid du Colombier 	}
2358*5c47fe09SDavid du Colombier 	qunlock(&ctlr->alock);
2359*5c47fe09SDavid du Colombier 	poperror();
2360*5c47fe09SDavid du Colombier }
2361*5c47fe09SDavid du Colombier 
2362*5c47fe09SDavid du Colombier static void
etherbcmshutdown(Ether *)2363*5c47fe09SDavid du Colombier etherbcmshutdown(Ether*)
2364*5c47fe09SDavid du Colombier {
2365*5c47fe09SDavid du Colombier 	sdioreset();
2366*5c47fe09SDavid du Colombier }
2367*5c47fe09SDavid du Colombier 
2368*5c47fe09SDavid du Colombier 
2369*5c47fe09SDavid du Colombier static int
etherbcmpnp(Ether * edev)2370*5c47fe09SDavid du Colombier etherbcmpnp(Ether* edev)
2371*5c47fe09SDavid du Colombier {
2372*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
2373*5c47fe09SDavid du Colombier 
2374*5c47fe09SDavid du Colombier 	ctlr = malloc(sizeof(Ctlr));
2375*5c47fe09SDavid du Colombier 	ctlr->chanid = Wifichan;
2376*5c47fe09SDavid du Colombier 	edev->ctlr = ctlr;
2377*5c47fe09SDavid du Colombier 	edev->attach = etherbcmattach;
2378*5c47fe09SDavid du Colombier 	edev->transmit = etherbcmtransmit;
2379*5c47fe09SDavid du Colombier 	edev->ifstat = etherbcmifstat;
2380*5c47fe09SDavid du Colombier 	edev->ctl = etherbcmctl;
2381*5c47fe09SDavid du Colombier 	edev->scanbs = etherbcmscan;
2382*5c47fe09SDavid du Colombier 	edev->shutdown = etherbcmshutdown;
2383*5c47fe09SDavid du Colombier 	edev->arg = edev;
2384*5c47fe09SDavid du Colombier 
2385*5c47fe09SDavid du Colombier 	return 0;
2386*5c47fe09SDavid du Colombier }
2387*5c47fe09SDavid du Colombier 
2388*5c47fe09SDavid du Colombier void
ether4330link(void)2389*5c47fe09SDavid du Colombier ether4330link(void)
2390*5c47fe09SDavid du Colombier {
2391*5c47fe09SDavid du Colombier 	addethercard("4330", etherbcmpnp);
2392*5c47fe09SDavid du Colombier }
2393