xref: /plan9/sys/src/9/pc/wavelan.c (revision aa72973a2891ccbd3fb042462446761159389e19)
1 /*
2 	Lucent Wavelan IEEE 802.11 pcmcia.
3 	There is almost no documentation for the card.
4 	the driver is done using both the FreeBSD, Linux and
5 	original Plan 9 drivers as `documentation'.
6 
7 	Has been used with the card plugged in during all up time.
8 	no cards removals/insertions yet.
9 
10 	For known BUGS see the comments below. Besides,
11 	the driver keeps interrupts disabled for just too
12 	long. When it gets robust, locks should be revisited.
13 
14 	BUGS: check endian, alignment and mem/io issues;
15 	      receive watchdog interrupts.
16 	TODO: automatic power management;
17 	      multicast filtering;
18 	      improve locking.
19  */
20 #include "u.h"
21 #include "../port/lib.h"
22 #include "mem.h"
23 #include "dat.h"
24 #include "fns.h"
25 #include "io.h"
26 #include "../port/error.h"
27 #include "../port/netif.h"
28 #include "etherif.h"
29 #include "wavelan.h"
30 
31 enum
32 {
33 	MSperTick=	50,	/* ms between ticks of kproc */
34 };
35 
36 /*
37  * When we're using a PCI device and memory-mapped I/O,
38  * the registers are spaced out as though each takes 32 bits,
39  * even though they are only 16-bit registers.  Thus,
40  * ctlr->mmb[reg] is the right way to access register reg,
41  * even though a priori you'd expect to use ctlr->mmb[reg/2].
42  */
43 void
csr_outs(Ctlr * ctlr,int reg,ushort arg)44 csr_outs(Ctlr *ctlr, int reg, ushort arg)
45 {
46 	if(ctlr->mmb)
47 		ctlr->mmb[reg] = arg;
48 	else
49 		outs(ctlr->iob+reg, arg);
50 }
51 
52 ushort
csr_ins(Ctlr * ctlr,int reg)53 csr_ins(Ctlr *ctlr, int reg)
54 {
55 	if(ctlr->mmb)
56 		return ctlr->mmb[reg];
57 	else
58 		return ins(ctlr->iob+reg);
59 }
60 
61 static void
csr_ack(Ctlr * ctlr,int ev)62 csr_ack(Ctlr *ctlr, int ev)
63 {
64 	csr_outs(ctlr, WR_EvAck, ev);
65 }
66 
67 static void
csr_inss(Ctlr * ctlr,int reg,void * dat,int ndat)68 csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat)
69 {
70 	ushort *rp, *wp;
71 
72 	if(ctlr->mmb){
73 		rp = &ctlr->mmb[reg];
74 		wp = dat;
75 		while(ndat-- > 0)
76 			*wp++ = *rp;
77 	}else
78 		inss(ctlr->iob+reg, dat, ndat);
79 }
80 
81 static void
csr_outss(Ctlr * ctlr,int reg,void * dat,int ndat)82 csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat)
83 {
84 	ushort *rp, *wp;
85 
86 	if(ctlr->mmb){
87 		rp = dat;
88 		wp = &ctlr->mmb[reg];
89 		while(ndat-- > 0)
90 			*wp = *rp++;
91 	}else
92 		outss(ctlr->iob+reg, dat, ndat);
93 }
94 
95 // w_... routines do not ilock the Ctlr and should
96 // be called locked.
97 
98 void
w_intdis(Ctlr * ctlr)99 w_intdis(Ctlr* ctlr)
100 {
101 	csr_outs(ctlr, WR_IntEna, 0);
102 	csr_ack(ctlr, 0xffff);
103 }
104 
105 static void
w_intena(Ctlr * ctlr)106 w_intena(Ctlr* ctlr)
107 {
108 	csr_outs(ctlr, WR_IntEna, WEvs);
109 }
110 
111 int
w_cmd(Ctlr * ctlr,ushort cmd,ushort arg)112 w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
113 {
114 	int i, rc;
115 
116 	for(i=0; i<WTmOut; i++)
117 		if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0)
118 			break;
119 	if(i==WTmOut){
120 		print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd));
121 		return -1;
122 	}
123 
124 	csr_outs(ctlr, WR_Parm0, arg);
125 	csr_outs(ctlr, WR_Cmd, cmd);
126 
127 	for(i=0; i<WTmOut; i++)
128 		if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
129 			break;
130 	if(i==WTmOut){
131 		/*
132 		 * WCmdIni can take a really long time.
133 		 */
134 		enum { IniTmOut = 2000 };
135 		for(i=0; i<IniTmOut; i++){
136 			if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
137 				break;
138 			microdelay(100);
139 		}
140 		if(i < IniTmOut)
141 			if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i);
142 		if(i == IniTmOut){
143 			print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
144 			return -1;
145 		}
146 	}
147 	rc = csr_ins(ctlr, WR_Sts);
148 	csr_ack(ctlr, WCmdEv);
149 
150 	if((rc&WCmdMsk) != (cmd&WCmdMsk)){
151 		print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
152 		return -1;
153 	}
154 	if(rc&WResSts){
155 		/*
156 		 * Don't print; this happens on every WCmdAccWr for some reason.
157 		 */
158 		if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
159 		return -1;
160 	}
161 	return 0;
162 }
163 
164 static int
w_seek(Ctlr * ctlr,ushort id,ushort offset,int chan)165 w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
166 {
167 	int i, rc;
168 	static ushort sel[] = { WR_Sel0, WR_Sel1 };
169 	static ushort off[] = { WR_Off0, WR_Off1 };
170 
171 	if(chan != 0 && chan != 1)
172 		panic("wavelan: bad chan\n");
173 	csr_outs(ctlr, sel[chan], id);
174 	csr_outs(ctlr, off[chan], offset);
175 	for (i=0; i<WTmOut; i++){
176 		rc = csr_ins(ctlr, off[chan]);
177 		if((rc & (WBusyOff|WErrOff)) == 0)
178 			return 0;
179 	}
180 	return -1;
181 }
182 
183 int
w_inltv(Ctlr * ctlr,Wltv * ltv)184 w_inltv(Ctlr* ctlr, Wltv* ltv)
185 {
186 	int len;
187 	ushort code;
188 
189 	if(w_cmd(ctlr, WCmdAccRd, ltv->type)){
190 		DEBUG("wavelan: access read failed\n");
191 		return -1;
192 	}
193 	if(w_seek(ctlr,ltv->type,0,1)){
194 		DEBUG("wavelan: seek failed\n");
195 		return -1;
196 	}
197 	len = csr_ins(ctlr, WR_Data1);
198 	if(len > ltv->len)
199 		return -1;
200 	ltv->len = len;
201 	if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){
202 		USED(code);
203 		DEBUG("wavelan: type %x != code %x\n",ltv->type,code);
204 		return -1;
205 	}
206 	if(ltv->len > 0)
207 		csr_inss(ctlr, WR_Data1, &ltv->val, ltv->len-1);
208 
209 	return 0;
210 }
211 
212 static void
w_outltv(Ctlr * ctlr,Wltv * ltv)213 w_outltv(Ctlr* ctlr, Wltv* ltv)
214 {
215 	if(w_seek(ctlr,ltv->type, 0, 1))
216 		return;
217 	csr_outss(ctlr, WR_Data1, ltv, ltv->len+1);
218 	w_cmd(ctlr, WCmdAccWr, ltv->type);
219 }
220 
221 void
ltv_outs(Ctlr * ctlr,int type,ushort val)222 ltv_outs(Ctlr* ctlr, int type, ushort val)
223 {
224 	Wltv ltv;
225 
226 	ltv.len = 2;
227 	ltv.type = type;
228 	ltv.val = val;
229 	w_outltv(ctlr, &ltv);
230 }
231 
232 int
ltv_ins(Ctlr * ctlr,int type)233 ltv_ins(Ctlr* ctlr, int type)
234 {
235 	Wltv ltv;
236 
237 	ltv.len = 2;
238 	ltv.type = type;
239 	ltv.val = 0;
240 	if(w_inltv(ctlr, &ltv))
241 		return -1;
242 	return ltv.val;
243 }
244 
245 static void
ltv_outstr(Ctlr * ctlr,int type,char * val)246 ltv_outstr(Ctlr* ctlr, int type, char* val)
247 {
248 	Wltv ltv;
249 	int len;
250 
251 	len = strlen(val);
252 	if(len > sizeof(ltv.s))
253 		len = sizeof(ltv.s);
254 	memset(&ltv, 0, sizeof(ltv));
255 	ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2;
256 	ltv.type = type;
257 
258 //	This should be ltv.slen = len; according to Axel Belinfante
259 	ltv.slen = len;
260 
261 	strncpy(ltv.s, val, len);
262 	w_outltv(ctlr, &ltv);
263 }
264 
265 static char Unkname[] = "who knows";
266 static char Nilname[] = "card does not tell";
267 
268 static char*
ltv_inname(Ctlr * ctlr,int type)269 ltv_inname(Ctlr* ctlr, int type)
270 {
271 	static Wltv ltv;
272 	int len;
273 
274 	memset(&ltv,0,sizeof(ltv));
275 	ltv.len = WNameLen/2+2;
276 	ltv.type = type;
277 	if(w_inltv(ctlr, &ltv))
278 		return Unkname;
279 	len = ltv.slen;
280 	if(len == 0 || ltv.s[0] == 0)
281 		return Nilname;
282 	if(len >= sizeof ltv.s)
283 		len = sizeof ltv.s - 1;
284 	ltv.s[len] = '\0';
285 	return ltv.s;
286 }
287 
288 static int
w_read(Ctlr * ctlr,int type,int off,void * buf,ulong len)289 w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
290 {
291 	if(w_seek(ctlr, type, off, 1)){
292 		DEBUG("wavelan: w_read: seek failed");
293 		return 0;
294 	}
295 	csr_inss(ctlr, WR_Data1, buf, len/2);
296 
297 	return len;
298 }
299 
300 static int
w_write(Ctlr * ctlr,int type,int off,void * buf,ulong len)301 w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
302 {
303 	if(w_seek(ctlr, type, off, 0)){
304 		DEBUG("wavelan: w_write: seek failed\n");
305 		return 0;
306 	}
307 
308 	csr_outss(ctlr, WR_Data0, buf, len/2);
309 	csr_outs(ctlr, WR_Data0, 0xdead);
310 	csr_outs(ctlr, WR_Data0, 0xbeef);
311 	if(w_seek(ctlr, type, off + len, 0)){
312 		DEBUG("wavelan: write seek failed\n");
313 		return 0;
314 	}
315 	if(csr_ins(ctlr, WR_Data0) == 0xdead && csr_ins(ctlr, WR_Data0) == 0xbeef)
316 		return len;
317 
318 	DEBUG("wavelan: Hermes bug byte.\n");
319 	return 0;
320 }
321 
322 static int
w_alloc(Ctlr * ctlr,int len)323 w_alloc(Ctlr* ctlr, int len)
324 {
325 	int rc;
326 	int i,j;
327 
328 	if(w_cmd(ctlr, WCmdMalloc, len)==0)
329 		for (i = 0; i<WTmOut; i++)
330 			if(csr_ins(ctlr, WR_EvSts) & WAllocEv){
331 				csr_ack(ctlr, WAllocEv);
332 				rc=csr_ins(ctlr, WR_Alloc);
333 				if(w_seek(ctlr, rc, 0, 0))
334 					return -1;
335 				len = len/2;
336 				for (j=0; j<len; j++)
337 					csr_outs(ctlr, WR_Data0, 0);
338 				return rc;
339 			}
340 	return -1;
341 }
342 
343 static int
w_enable(Ether * ether)344 w_enable(Ether* ether)
345 {
346 	Wltv ltv;
347 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
348 
349 	if(!ctlr)
350 		return -1;
351 
352 	w_intdis(ctlr);
353 	w_cmd(ctlr, WCmdDis, 0);
354 	w_intdis(ctlr);
355 	if(w_cmd(ctlr, WCmdIni, 0))
356 		return -1;
357 	w_intdis(ctlr);
358 
359 	ltv_outs(ctlr, WTyp_Tick, 8);
360 	ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen);
361 	ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype);
362  	ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss);
363 	ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres);
364 	ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
365 	ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
366 	ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
367 	ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
368 	if(*ctlr->netname)
369 		ltv_outstr(ctlr, WTyp_NetName, ctlr->netname);
370 	if(*ctlr->wantname)
371 		ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname);
372 	ltv_outs(ctlr, WTyp_Chan, ctlr->chan);
373 	if(*ctlr->nodename)
374 		ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename);
375 	ltv.len = 4;
376 	ltv.type = WTyp_Mac;
377 	memmove(ltv.addr, ether->ea, Eaddrlen);
378 	w_outltv(ctlr, &ltv);
379 
380 	ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));
381 
382 	if(ctlr->hascrypt && ctlr->crypt){
383 		ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt);
384 		ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey);
385 		w_outltv(ctlr, &ctlr->keys);
386 		ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
387 	}
388 
389 	// BUG: set multicast addresses
390 
391 	if(w_cmd(ctlr, WCmdEna, 0)){
392 		DEBUG("wavelan: Enable failed");
393 		return -1;
394 	}
395 	ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
396 	ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
397 	if(ctlr->txdid == -1 || ctlr->txmid == -1)
398 		DEBUG("wavelan: alloc failed");
399 	ctlr->txbusy = 0;
400 	w_intena(ctlr);
401 	return 0;
402 }
403 
404 static void
w_rxdone(Ether * ether)405 w_rxdone(Ether* ether)
406 {
407 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
408 	int len, sp;
409 	WFrame f;
410 	Block* bp=0;
411 	Etherpkt* ep;
412 
413 	sp = csr_ins(ctlr, WR_RXId);
414 	len = w_read(ctlr, sp, 0, &f, sizeof(f));
415 	if(len == 0){
416 		DEBUG("wavelan: read frame error\n");
417 		goto rxerror;
418 	}
419 	if(f.sts&WF_Err){
420 		goto rxerror;
421 	}
422 	switch(f.sts){
423 	case WF_1042:
424 	case WF_Tunnel:
425 	case WF_WMP:
426 		len = f.dlen + WSnapHdrLen;
427 		bp = iallocb(ETHERHDRSIZE + len + 2);
428 		if(!bp)
429 			goto rxerror;
430 		ep = (Etherpkt*) bp->wp;
431 		memmove(ep->d, f.addr1, Eaddrlen);
432 		memmove(ep->s, f.addr2, Eaddrlen);
433 		memmove(ep->type,&f.type,2);
434 		bp->wp += ETHERHDRSIZE;
435 		if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){
436 			DEBUG("wavelan: read 802.11 error\n");
437 			goto rxerror;
438 		}
439 		bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen);
440 		break;
441 	default:
442 		len = ETHERHDRSIZE + f.dlen + 2;
443 		bp = iallocb(len);
444 		if(!bp)
445 			goto rxerror;
446 		if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){
447 			DEBUG("wavelan: read 800.3 error\n");
448 			goto rxerror;
449 		}
450 		bp->wp += len;
451 	}
452 
453 	ctlr->nrx++;
454 	etheriq(ether,bp,1);
455 	ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16;
456 	ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16;
457 	return;
458 
459 rxerror:
460 	freeb(bp);
461 	ctlr->nrxerr++;
462 }
463 
464 static void
w_txstart(Ether * ether)465 w_txstart(Ether* ether)
466 {
467 	Etherpkt *pkt;
468 	Ctlr *ctlr;
469 	Block *bp;
470 	int len, off;
471 
472 	if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy)
473 		return;
474 
475 	if((bp = qget(ether->oq)) == nil)
476 		return;
477 	pkt = (Etherpkt*)bp->rp;
478 
479 	//
480 	// If the packet header type field is > 1500 it is an IP or
481 	// ARP datagram, otherwise it is an 802.3 packet. See RFC1042.
482 	//
483 	memset(&ctlr->txf, 0, sizeof(ctlr->txf));
484 	if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){
485 		ctlr->txf.framectl = WF_Data;
486 		memmove(ctlr->txf.addr1, pkt->d, Eaddrlen);
487 		memmove(ctlr->txf.addr2, pkt->s, Eaddrlen);
488 		memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen);
489 		memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen);
490 		memmove(&ctlr->txf.type, pkt->type, 2);
491 		bp->rp += ETHERHDRSIZE;
492 		len = BLEN(bp);
493 		off = WF_802_11_Off;
494 		ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen;
495 		hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
496 		hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
497 		hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen);
498 	}
499 	else{
500 		len = BLEN(bp);
501 		off = WF_802_3_Off;
502 		ctlr->txf.dlen = len;
503 	}
504 	w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf));
505 	w_write(ctlr, ctlr->txdid, off, bp->rp, len+2);
506 
507 	if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){
508 		DEBUG("wavelan: transmit failed\n");
509 		ctlr->ntxerr++;
510 	}
511 	else{
512 		ctlr->txbusy = 1;
513 		ctlr->txtmout = 2;
514 	}
515 	freeb(bp);
516 }
517 
518 static void
w_txdone(Ctlr * ctlr,int sts)519 w_txdone(Ctlr* ctlr, int sts)
520 {
521 	ctlr->txbusy = 0;
522 	ctlr->txtmout = 0;
523 	if(sts & WTxErrEv)
524 		ctlr->ntxerr++;
525 	else
526 		ctlr->ntx++;
527 }
528 
529 /* save the stats info in the ctlr struct */
530 static void
w_stats(Ctlr * ctlr,int len)531 w_stats(Ctlr* ctlr, int len)
532 {
533 	int i, rc;
534 	ulong* p = (ulong*)&ctlr->WStats;
535 	ulong* pend = (ulong*)&ctlr->end;
536 
537 	for (i = 0; i < len && p < pend; i++){
538 		rc = csr_ins(ctlr, WR_Data1);
539 		if(rc > 0xf000)
540 			rc = ~rc & 0xffff;
541 		p[i] += rc;
542 	}
543 }
544 
545 /* send the base station scan info to any readers */
546 static void
w_scaninfo(Ether * ether,Ctlr * ctlr,int len)547 w_scaninfo(Ether* ether, Ctlr *ctlr, int len)
548 {
549 	int i, j;
550 	Netfile **ep, *f, **fp;
551 	Block *bp;
552 	WScan *wsp;
553 	ushort *scanbuf;
554 
555 	scanbuf = malloc(len*2);
556 	if(scanbuf == nil)
557 		return;
558 
559 	for (i = 0; i < len ; i++)
560 		scanbuf[i] = csr_ins(ctlr, WR_Data1);
561 
562 	/* calculate number of samples */
563 	len /= 25;
564 	if(len == 0)
565 		goto out;
566 
567 	i = ether->scan;
568 	ep = &ether->f[Ntypes];
569 	for(fp = ether->f; fp < ep && i > 0; fp++){
570 		f = *fp;
571 		if(f == nil || f->scan == 0)
572 			continue;
573 
574 		bp = iallocb(100*len);
575 		if(bp == nil)
576 			break;
577 		for(j = 0; j < len; j++){
578 			wsp = (WScan*)(&scanbuf[j*25]);
579 			if(wsp->ssid_len > 32)
580 				wsp->ssid_len = 32;
581 			bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
582 				"ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n",
583 				wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal,
584 				wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":"");
585 		}
586 		qpass(f->in, bp);
587 		i--;
588 	}
589 out:
590 	free(scanbuf);
591 }
592 
593 static int
w_info(Ether * ether,Ctlr * ctlr)594 w_info(Ether *ether, Ctlr* ctlr)
595 {
596 	int sp;
597 	Wltv ltv;
598 
599 	sp = csr_ins(ctlr, WR_InfoId);
600 	ltv.len = ltv.type = 0;
601 	w_read(ctlr, sp, 0, &ltv, 4);
602 	ltv.len--;
603 	switch(ltv.type){
604 	case WTyp_Stats:
605 		w_stats(ctlr, ltv.len);
606 		return 0;
607 	case WTyp_Scan:
608 		w_scaninfo(ether, ctlr, ltv.len);
609 		return 0;
610 	}
611 	return -1;
612 }
613 
614 /* set scanning interval */
615 static void
w_scanbs(void * a,uint secs)616 w_scanbs(void *a, uint secs)
617 {
618 	Ether *ether = a;
619 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
620 
621 	ctlr->scanticks = secs*(1000/MSperTick);
622 }
623 
624 static void
w_intr(Ether * ether)625 w_intr(Ether *ether)
626 {
627 	int rc, txid;
628 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
629 
630 	if((ctlr->state & Power) == 0)
631 		return;
632 
633 	if((ctlr->state & Attached) == 0){
634 		csr_ack(ctlr, 0xffff);
635 		csr_outs(ctlr, WR_IntEna, 0);
636 		return;
637 	}
638 
639 	rc = csr_ins(ctlr, WR_EvSts);
640 	csr_ack(ctlr, ~WEvs);	// Not interested in them
641 	if(rc & WRXEv){
642 		w_rxdone(ether);
643 		csr_ack(ctlr, WRXEv);
644 	}
645 	if(rc & WTXEv){
646 		w_txdone(ctlr, rc);
647 		csr_ack(ctlr, WTXEv);
648 	}
649 	if(rc & WAllocEv){
650 		ctlr->nalloc++;
651 		txid = csr_ins(ctlr, WR_Alloc);
652 		csr_ack(ctlr, WAllocEv);
653 		if(txid == ctlr->txdid){
654 			if((rc & WTXEv) == 0)
655 				w_txdone(ctlr, rc);
656 		}
657 	}
658 	if(rc & WInfoEv){
659 		ctlr->ninfo++;
660 		w_info(ether, ctlr);
661 		csr_ack(ctlr, WInfoEv);
662 	}
663 	if(rc & WTxErrEv){
664 		w_txdone(ctlr, rc);
665 		csr_ack(ctlr, WTxErrEv);
666 	}
667 	if(rc & WIDropEv){
668 		ctlr->nidrop++;
669 		csr_ack(ctlr, WIDropEv);
670 	}
671 	w_txstart(ether);
672 }
673 
674 // Watcher to ensure that the card still works properly and
675 // to request WStats updates once a minute.
676 // BUG: it runs much more often, see the comment below.
677 
678 static void
w_timer(void * arg)679 w_timer(void* arg)
680 {
681 	Ether* ether = (Ether*) arg;
682 	Ctlr* ctlr = (Ctlr*)ether->ctlr;
683 
684 	ctlr->timerproc = up;
685 	for(;;){
686 		tsleep(&up->sleep, return0, 0, MSperTick);
687 		ctlr = (Ctlr*)ether->ctlr;
688 		if(ctlr == 0)
689 			break;
690 		if((ctlr->state & (Attached|Power)) != (Attached|Power))
691 			continue;
692 		ctlr->ticks++;
693 
694 		ilock(ctlr);
695 
696 		// Seems that the card gets frames BUT does
697 		// not send the interrupt; this is a problem because
698 		// I suspect it runs out of receive buffers and
699 		// stops receiving until a transmit watchdog
700 		// reenables the card.
701 		// The problem is serious because it leads to
702 		// poor rtts.
703 		// This can be seen clearly by commenting out
704 		// the next if and doing a ping: it will stop
705 		// receiving (although the icmp replies are being
706 		// issued from the remote) after a few seconds.
707 		// Of course this `bug' could be because I'm reading
708 		// the card frames in the wrong way; due to the
709 		// lack of documentation I cannot know.
710 
711 		if(csr_ins(ctlr, WR_EvSts)&WEvs){
712 			ctlr->tickintr++;
713 			w_intr(ether);
714 		}
715 
716 		if((ctlr->ticks % 10) == 0) {
717 			if(ctlr->txtmout && --ctlr->txtmout == 0){
718 				ctlr->nwatchdogs++;
719 				w_txdone(ctlr, WTxErrEv);
720 				if(w_enable(ether)){
721 					DEBUG("wavelan: wdog enable failed\n");
722 				}
723 				w_txstart(ether);
724 			}
725 			if((ctlr->ticks % 120) == 0)
726 			if(ctlr->txbusy == 0)
727 				w_cmd(ctlr, WCmdEnquire, WTyp_Stats);
728 			if(ctlr->scanticks > 0)
729 			if((ctlr->ticks % ctlr->scanticks) == 0)
730 			if(ctlr->txbusy == 0)
731 				w_cmd(ctlr, WCmdEnquire, WTyp_Scan);
732 		}
733 		iunlock(ctlr);
734 	}
735 	pexit("terminated", 0);
736 }
737 
738 void
w_multicast(void * ether,uchar *,int add)739 w_multicast(void *ether, uchar*, int add)
740 {
741 	/* BUG: use controller's multicast filter */
742 	if (add)
743 		w_promiscuous(ether, 1);
744 }
745 
746 void
w_attach(Ether * ether)747 w_attach(Ether* ether)
748 {
749 	Ctlr* ctlr;
750 	char name[64];
751 	int rc;
752 
753 	if(ether->ctlr == 0)
754 		return;
755 
756 	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
757 	ctlr = (Ctlr*) ether->ctlr;
758 	if((ctlr->state & Attached) == 0){
759 		ilock(ctlr);
760 		rc = w_enable(ether);
761 		iunlock(ctlr);
762 		if(rc == 0){
763 			ctlr->state |= Attached;
764 			kproc(name, w_timer, ether);
765 		} else
766 			print("#l%d: enable failed\n",ether->ctlrno);
767 	}
768 }
769 
770 void
w_detach(Ether * ether)771 w_detach(Ether* ether)
772 {
773 	Ctlr* ctlr;
774 	char name[64];
775 
776 	if(ether->ctlr == nil)
777 		return;
778 
779 	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
780 	ctlr = (Ctlr*) ether->ctlr;
781 	if(ctlr->state & Attached){
782 		ilock(ctlr);
783 		w_intdis(ctlr);
784 		if(ctlr->timerproc){
785 			if(!postnote(ctlr->timerproc, 1, "kill", NExit))
786 				print("timerproc note not posted\n");
787 			print("w_detach, killing 0x%p\n", ctlr->timerproc);
788 		}
789 		ctlr->state &= ~Attached;
790 		iunlock(ctlr);
791 	}
792 	ether->ctlr = nil;
793 }
794 
795 void
w_power(Ether * ether,int on)796 w_power(Ether* ether, int on)
797 {
798 	Ctlr *ctlr;
799 
800 	ctlr = (Ctlr*) ether->ctlr;
801 	ilock(ctlr);
802 iprint("w_power %d\n", on);
803 	if(on){
804 		if((ctlr->state & Power) == 0){
805 			if (wavelanreset(ether, ctlr) < 0){
806 				iprint("w_power: reset failed\n");
807 				iunlock(ctlr);
808 				w_detach(ether);
809 				free(ctlr);
810 				return;
811 			}
812 			if(ctlr->state & Attached)
813 				w_enable(ether);
814 			ctlr->state |= Power;
815 		}
816 	}else{
817 		if(ctlr->state & Power){
818 			if(ctlr->state & Attached)
819 				w_intdis(ctlr);
820 			ctlr->state &= ~Power;
821 		}
822 	}
823 	iunlock(ctlr);
824 }
825 
826 #define PRINTSTAT(fmt,val)	l += snprint(p+l, READSTR-l, (fmt), (val))
827 #define PRINTSTR(fmt)		l += snprint(p+l, READSTR-l, (fmt))
828 
829 long
w_ifstat(Ether * ether,void * a,long n,ulong offset)830 w_ifstat(Ether* ether, void* a, long n, ulong offset)
831 {
832 	Ctlr *ctlr = (Ctlr*) ether->ctlr;
833 	char *k, *p;
834 	int i, l, txid;
835 
836 	ether->oerrs = ctlr->ntxerr;
837 	ether->crcs = ctlr->nrxfcserr;
838 	ether->frames = 0;
839 	ether->buffs = ctlr->nrxdropnobuf;
840 	ether->overflows = 0;
841 
842 	//
843 	// Offset must be zero or there's a possibility the
844 	// new data won't match the previous read.
845 	//
846 	if(n == 0 || offset != 0)
847 		return 0;
848 
849 	p = malloc(READSTR);
850 	if(p == nil)
851 		error(Enomem);
852 	l = 0;
853 
854 	PRINTSTAT("Signal: %d\n", ctlr->signal-149);
855 	PRINTSTAT("Noise: %d\n", ctlr->noise-149);
856 	PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
857 	PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
858 	PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint);
859 	PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
860 	PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
861 	PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);
862 	PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr);
863 	PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq);
864 	PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc);
865 	PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo);
866 	PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop);
867 	PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs);
868 	PRINTSTAT("Ticks: %ud\n", ctlr->ticks);
869 	PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr);
870 	k = ((ctlr->state & Attached) ? "attached" : "not attached");
871 	PRINTSTAT("Card %s", k);
872 	k = ((ctlr->state & Power) ? "on" : "off");
873 	PRINTSTAT(", power %s", k);
874 	k = ((ctlr->txbusy)? ", txbusy" : "");
875 	PRINTSTAT("%s\n", k);
876 
877 	if(ctlr->hascrypt){
878 		PRINTSTR("Keys: ");
879 		for (i = 0; i < WNKeys; i++){
880 			if(ctlr->keys.keys[i].len == 0)
881 				PRINTSTR("none ");
882 			else if(SEEKEYS == 0)
883 				PRINTSTR("set ");
884 			else
885 				PRINTSTAT("%s ", ctlr->keys.keys[i].dat);
886 		}
887 		PRINTSTR("\n");
888 	}
889 
890 	// real card stats
891 	ilock(ctlr);
892 	PRINTSTR("\nCard stats: \n");
893 	PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
894 	PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
895 	i = ltv_ins(ctlr, WTyp_Ptype);
896 	PRINTSTAT("Port type: %d\n", i);
897 	PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
898 	PRINTSTAT("Current Transmit rate: %d\n",
899 		ltv_ins(ctlr, WTyp_CurTxRate));
900 	PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan));
901 	PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens));
902 	PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
903 	if(i == WPTypeAdHoc)
904 		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
905 	else {
906 		Wltv ltv;
907 		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
908 		ltv.type = WTyp_BaseID;
909 		ltv.len = 4;
910 		if(w_inltv(ctlr, &ltv))
911 			print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
912 		l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
913 			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
914 	}
915 	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
916 	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
917 	if(ltv_ins(ctlr, WTyp_HasCrypt) == 0)
918 		PRINTSTR("WEP: not supported\n");
919 	else {
920 		if(ltv_ins(ctlr, WTyp_Crypt) == 0)
921 			PRINTSTR("WEP: disabled\n");
922 		else{
923 			PRINTSTR("WEP: enabled\n");
924 			k = ((ctlr->xclear)? "excluded": "included");
925 			PRINTSTAT("Clear packets: %s\n", k);
926 			txid = ltv_ins(ctlr, WTyp_TxKey);
927 			PRINTSTAT("Transmit key id: %d\n", txid);
928 		}
929 	}
930 	iunlock(ctlr);
931 
932 	PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
933 	PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
934 	PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags);
935 	PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes);
936 	PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes);
937 	PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred);
938 	PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries);
939 	PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries);
940 	PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit);
941 	PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards);
942 	PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes);
943 	PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes);
944 	PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags);
945 	PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes);
946 	PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes);
947 	PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr);
948 	PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf);
949 	PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa);
950 	PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt);
951 	PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag);
952 	PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag);
953 	USED(l);
954 	n = readstr(offset, a, n, p);
955 	free(p);
956 	return n;
957 }
958 #undef PRINTSTR
959 #undef PRINTSTAT
960 
961 static int
parsekey(WKey * key,char * a)962 parsekey(WKey* key, char* a)
963 {
964 	int i, k, len, n;
965 	char buf[WMaxKeyLen];
966 
967 	len = strlen(a);
968 	if(len == WMinKeyLen || len == WMaxKeyLen){
969 		memset(key->dat, 0, sizeof(key->dat));
970 		memmove(key->dat, a, len);
971 		key->len = len;
972 
973 		return 0;
974 	}
975 	else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
976 		k = 0;
977 		for(i = 0; i < len; i++){
978 			if(*a >= '0' && *a <= '9')
979 				n = *a++ - '0';
980 			else if(*a >= 'a' && *a <= 'f')
981 				n = *a++ - 'a' + 10;
982 			else if(*a >= 'A' && *a <= 'F')
983 				n = *a++ - 'A' + 10;
984 			else
985 				return -1;
986 
987 			if(i & 1){
988 				buf[k] |= n;
989 				k++;
990 			}
991 			else
992 				buf[k] = n<<4;
993 		}
994 
995 		memset(key->dat, 0, sizeof(key->dat));
996 		memmove(key->dat, buf, k);
997 		key->len = k;
998 
999 		return 0;
1000 	}
1001 
1002 	return -1;
1003 }
1004 
1005 int
w_option(Ctlr * ctlr,char * buf,long n)1006 w_option(Ctlr* ctlr, char* buf, long n)
1007 {
1008 	char *p;
1009 	int i, r;
1010 	Cmdbuf *cb;
1011 
1012 	r = 0;
1013 
1014 	cb = parsecmd(buf, n);
1015 	if(cb->nf < 2)
1016 		r = -1;
1017 	else if(cistrcmp(cb->f[0], "essid") == 0){
1018 		if(cistrcmp(cb->f[1],"default") == 0)
1019 			p = "";
1020 		else
1021 			p = cb->f[1];
1022 		if(ctlr->ptype == WPTypeAdHoc){
1023 			memset(ctlr->netname, 0, sizeof(ctlr->netname));
1024 			strncpy(ctlr->netname, p, WNameLen);
1025 		}
1026 		else{
1027 			memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
1028 			strncpy(ctlr->wantname, p, WNameLen);
1029 		}
1030 	}
1031 	else if(cistrcmp(cb->f[0], "station") == 0){
1032 		memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
1033 		strncpy(ctlr->nodename, cb->f[1], WNameLen);
1034 	}
1035 	else if(cistrcmp(cb->f[0], "channel") == 0){
1036 		if((i = atoi(cb->f[1])) >= 1 && i <= 16)
1037 			ctlr->chan = i;
1038 		else
1039 			r = -1;
1040 	}
1041 	else if(cistrcmp(cb->f[0], "mode") == 0){
1042 		if(cistrcmp(cb->f[1], "managed") == 0)
1043 			ctlr->ptype = WPTypeManaged;
1044 		else if(cistrcmp(cb->f[1], "wds") == 0)
1045 			ctlr->ptype = WPTypeWDS;
1046 		else if(cistrcmp(cb->f[1], "adhoc") == 0)
1047 			ctlr->ptype = WPTypeAdHoc;
1048 		else if((i = atoi(cb->f[1])) >= 0 && i <= 3)
1049 			ctlr->ptype = i;
1050 		else
1051 			r = -1;
1052 	}
1053 	else if(cistrcmp(cb->f[0], "ibss") == 0){
1054 		if(cistrcmp(cb->f[1], "on") == 0)
1055 			ctlr->createibss = 1;
1056 		else
1057 			ctlr->createibss = 0;
1058 	}
1059 	else if(cistrcmp(cb->f[0], "crypt") == 0){
1060 		if(cistrcmp(cb->f[1], "off") == 0)
1061 			ctlr->crypt = 0;
1062 		else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt)
1063 			ctlr->crypt = 1;
1064 		else
1065 			r = -1;
1066 	}
1067 	else if(cistrcmp(cb->f[0], "clear") == 0){
1068 		if(cistrcmp(cb->f[1], "on") == 0)
1069 			ctlr->xclear = 0;
1070 		else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt)
1071 			ctlr->xclear = 1;
1072 		else
1073 			r = -1;
1074 	}
1075 	else if(cistrncmp(cb->f[0], "key", 3) == 0){
1076 		if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){
1077 			ctlr->txkey = i-1;
1078 			if(parsekey(&ctlr->keys.keys[ctlr->txkey], cb->f[1]))
1079 				r = -1;
1080 		}
1081 		else
1082 			r = -1;
1083 	}
1084 	else if(cistrcmp(cb->f[0], "txkey") == 0){
1085 		if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys)
1086 			ctlr->txkey = i-1;
1087 		else
1088 			r = -1;
1089 	}
1090 	else if(cistrcmp(cb->f[0], "pm") == 0){
1091 		if(cistrcmp(cb->f[1], "off") == 0)
1092 			ctlr->pmena = 0;
1093 		else if(cistrcmp(cb->f[1], "on") == 0){
1094 			ctlr->pmena = 1;
1095 			if(cb->nf == 3){
1096 				i = atoi(cb->f[2]);
1097 				// check range here? what are the units?
1098 				ctlr->pmwait = i;
1099 			}
1100 		}
1101 		else
1102 			r = -1;
1103 	}
1104 	else
1105 		r = -2;
1106 	free(cb);
1107 
1108 	return r;
1109 }
1110 
1111 long
w_ctl(Ether * ether,void * buf,long n)1112 w_ctl(Ether* ether, void* buf, long n)
1113 {
1114 	Ctlr *ctlr;
1115 
1116 	if((ctlr = ether->ctlr) == nil)
1117 		error(Enonexist);
1118 	if((ctlr->state & Attached) == 0)
1119 		error(Eshutdown);
1120 
1121 	ilock(ctlr);
1122 	if(w_option(ctlr, buf, n)){
1123 		iunlock(ctlr);
1124 		error(Ebadctl);
1125 	}
1126 	if(ctlr->txbusy)
1127 		w_txdone(ctlr, WTxErrEv);
1128 	w_enable(ether);
1129 	w_txstart(ether);
1130 	iunlock(ctlr);
1131 
1132 	return n;
1133 }
1134 
1135 void
w_transmit(Ether * ether)1136 w_transmit(Ether* ether)
1137 {
1138 	Ctlr* ctlr = ether->ctlr;
1139 
1140 	if(ctlr == 0)
1141 		return;
1142 
1143 	ilock(ctlr);
1144 	ctlr->ntxrq++;
1145 	w_txstart(ether);
1146 	iunlock(ctlr);
1147 }
1148 
1149 void
w_promiscuous(void * arg,int on)1150 w_promiscuous(void* arg, int on)
1151 {
1152 	Ether* ether = (Ether*)arg;
1153 	Ctlr* ctlr = ether->ctlr;
1154 
1155 	if(ctlr == nil)
1156 		error("card not found");
1157 	if((ctlr->state & Attached) == 0)
1158 		error("card not attached");
1159 	ilock(ctlr);
1160 	ltv_outs(ctlr, WTyp_Prom, (on?1:0));
1161 	iunlock(ctlr);
1162 }
1163 
1164 void
w_interrupt(Ureg *,void * arg)1165 w_interrupt(Ureg* ,void* arg)
1166 {
1167 	Ether* ether = (Ether*) arg;
1168 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
1169 
1170 	if(ctlr == 0)
1171 		return;
1172 	ilock(ctlr);
1173 	ctlr->nints++;
1174 	w_intr(ether);
1175 	iunlock(ctlr);
1176 }
1177 
1178 static void
w_shutdown(Ether * ether)1179 w_shutdown(Ether* ether)
1180 {
1181 	Ctlr *ctlr;
1182 
1183 	ctlr = ether->ctlr;
1184 	w_intdis(ctlr);
1185 	if(w_cmd(ctlr,WCmdIni,0))
1186 		iprint("#l%d: init failed\n", ether->ctlrno);
1187 	w_intdis(ctlr);
1188 }
1189 
1190 int
wavelanreset(Ether * ether,Ctlr * ctlr)1191 wavelanreset(Ether* ether, Ctlr *ctlr)
1192 {
1193 	Wltv ltv;
1194 
1195 	iprint("wavelanreset, iob 0x%ux\n", ctlr->iob);
1196 	w_intdis(ctlr);
1197 	if(w_cmd(ctlr,WCmdIni,0)){
1198 		iprint("#l%d: init failed\n", ether->ctlrno);
1199 		return -1;
1200 	}
1201 	w_intdis(ctlr);
1202 	ltv_outs(ctlr, WTyp_Tick, 8);
1203 
1204 	ctlr->chan = 0;
1205 	ctlr->ptype = WDfltPType;
1206 	ctlr->txkey = 0;
1207 	ctlr->createibss = 0;
1208 	ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1;
1209 	ctlr->keys.type = WTyp_Keys;
1210 	if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt))
1211 		ctlr->crypt = 1;
1212 	*ctlr->netname = *ctlr->wantname = 0;
1213 	strcpy(ctlr->nodename, "Plan 9 STA");
1214 
1215 	ctlr->netname[WNameLen-1] = 0;
1216 	ctlr->wantname[WNameLen-1] = 0;
1217 	ctlr->nodename[WNameLen-1] =0;
1218 
1219 	ltv.type = WTyp_Mac;
1220 	ltv.len	= 4;
1221 	if(w_inltv(ctlr, &ltv)){
1222 		iprint("#l%d: unable to read mac addr\n",
1223 			ether->ctlrno);
1224 		return -1;
1225 	}
1226 	memmove(ether->ea, ltv.addr, Eaddrlen);
1227 
1228 	if(ctlr->chan == 0)
1229 		ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
1230 	ctlr->apdensity = WDfltApDens;
1231 	ctlr->rtsthres = WDfltRtsThres;
1232 	ctlr->txrate = WDfltTxRate;
1233 	ctlr->maxlen = WMaxLen;
1234 	ctlr->pmena = 0;
1235 	ctlr->pmwait = 100;
1236 	ctlr->signal = 1;
1237 	ctlr->noise = 1;
1238 	ctlr->state |= Power;
1239 
1240 	// free old Ctlr struct if resetting after suspend
1241 	if(ether->ctlr && ether->ctlr != ctlr)
1242 		free(ether->ctlr);
1243 
1244 	// link to ether
1245 	ether->ctlr = ctlr;
1246 	ether->mbps = 10;
1247 	ether->attach = w_attach;
1248 	ether->detach = w_detach;
1249 	ether->interrupt = w_interrupt;
1250 	ether->transmit = w_transmit;
1251 	ether->ifstat = w_ifstat;
1252 	ether->ctl = w_ctl;
1253 	ether->power = w_power;
1254 	ether->promiscuous = w_promiscuous;
1255 	ether->multicast = w_multicast;
1256 	ether->shutdown = w_shutdown;
1257 	ether->scanbs = w_scanbs;
1258 	ether->arg = ether;
1259 
1260 	DEBUG("#l%d: irq %d port %lx type %s",
1261 		ether->ctlrno, ether->irq, ether->port,	ether->type);
1262 	DEBUG(" %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux\n",
1263 		ether->ea[0], ether->ea[1], ether->ea[2],
1264 		ether->ea[3], ether->ea[4], ether->ea[5]);
1265 
1266 	return 0;
1267 }
1268 
1269 char* wavenames[] = {
1270 	"WaveLAN/IEEE",
1271 	"TrueMobile 1150",
1272 	"Instant Wireless ; Network PC CARD",
1273 	"Instant Wireless Network PC Card",
1274 	"Avaya Wireless PC Card",
1275 	"AirLancer MC-11",
1276 	"INTERSIL;HFA384x/IEEE;Version 01.02;",
1277 	nil,
1278 };
1279