Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
62
csr_ack(Ctlr *ctlr, int ev)
63
{
64
	csr_outs(ctlr, WR_EvAck, ev);
65
}
66
 
67
static void
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
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
99
w_intdis(Ctlr* ctlr)
100
{
101
	csr_outs(ctlr, WR_IntEna, 0);
102
	csr_ack(ctlr, 0xffff);
103
}
104
 
105
static void
106
w_intena(Ctlr* ctlr)
107
{
108
	csr_outs(ctlr, WR_IntEna, WEvs);
109
}
110
 
111
int
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
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
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
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
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
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
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*
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
};