xref: /plan9/sys/src/9/pc/wavelan.c (revision aa72973a2891ccbd3fb042462446761159389e19)
13ff48bf5SDavid du Colombier /*
23ff48bf5SDavid du Colombier 	Lucent Wavelan IEEE 802.11 pcmcia.
33ff48bf5SDavid du Colombier 	There is almost no documentation for the card.
43ff48bf5SDavid du Colombier 	the driver is done using both the FreeBSD, Linux and
53ff48bf5SDavid du Colombier 	original Plan 9 drivers as `documentation'.
63ff48bf5SDavid du Colombier 
73ff48bf5SDavid du Colombier 	Has been used with the card plugged in during all up time.
83ff48bf5SDavid du Colombier 	no cards removals/insertions yet.
93ff48bf5SDavid du Colombier 
103ff48bf5SDavid du Colombier 	For known BUGS see the comments below. Besides,
113ff48bf5SDavid du Colombier 	the driver keeps interrupts disabled for just too
123ff48bf5SDavid du Colombier 	long. When it gets robust, locks should be revisited.
133ff48bf5SDavid du Colombier 
143ff48bf5SDavid du Colombier 	BUGS: check endian, alignment and mem/io issues;
153ff48bf5SDavid du Colombier 	      receive watchdog interrupts.
163ff48bf5SDavid du Colombier 	TODO: automatic power management;
175fb521f9SDavid du Colombier 	      multicast filtering;
183ff48bf5SDavid du Colombier 	      improve locking.
193ff48bf5SDavid du Colombier  */
203ff48bf5SDavid du Colombier #include "u.h"
213ff48bf5SDavid du Colombier #include "../port/lib.h"
223ff48bf5SDavid du Colombier #include "mem.h"
233ff48bf5SDavid du Colombier #include "dat.h"
243ff48bf5SDavid du Colombier #include "fns.h"
253ff48bf5SDavid du Colombier #include "io.h"
263ff48bf5SDavid du Colombier #include "../port/error.h"
273ff48bf5SDavid du Colombier #include "../port/netif.h"
283ff48bf5SDavid du Colombier #include "etherif.h"
293ff48bf5SDavid du Colombier #include "wavelan.h"
303ff48bf5SDavid du Colombier 
3115763c87SDavid du Colombier enum
3215763c87SDavid du Colombier {
3315763c87SDavid du Colombier 	MSperTick=	50,	/* ms between ticks of kproc */
3415763c87SDavid du Colombier };
3515763c87SDavid du Colombier 
363ff48bf5SDavid du Colombier /*
373ff48bf5SDavid du Colombier  * When we're using a PCI device and memory-mapped I/O,
383ff48bf5SDavid du Colombier  * the registers are spaced out as though each takes 32 bits,
393ff48bf5SDavid du Colombier  * even though they are only 16-bit registers.  Thus,
403ff48bf5SDavid du Colombier  * ctlr->mmb[reg] is the right way to access register reg,
413ff48bf5SDavid du Colombier  * even though a priori you'd expect to use ctlr->mmb[reg/2].
423ff48bf5SDavid du Colombier  */
433ff48bf5SDavid du Colombier void
csr_outs(Ctlr * ctlr,int reg,ushort arg)443ff48bf5SDavid du Colombier csr_outs(Ctlr *ctlr, int reg, ushort arg)
453ff48bf5SDavid du Colombier {
463ff48bf5SDavid du Colombier 	if(ctlr->mmb)
473ff48bf5SDavid du Colombier 		ctlr->mmb[reg] = arg;
483ff48bf5SDavid du Colombier 	else
493ff48bf5SDavid du Colombier 		outs(ctlr->iob+reg, arg);
503ff48bf5SDavid du Colombier }
513ff48bf5SDavid du Colombier 
523ff48bf5SDavid du Colombier ushort
csr_ins(Ctlr * ctlr,int reg)533ff48bf5SDavid du Colombier csr_ins(Ctlr *ctlr, int reg)
543ff48bf5SDavid du Colombier {
553ff48bf5SDavid du Colombier 	if(ctlr->mmb)
563ff48bf5SDavid du Colombier 		return ctlr->mmb[reg];
573ff48bf5SDavid du Colombier 	else
583ff48bf5SDavid du Colombier 		return ins(ctlr->iob+reg);
593ff48bf5SDavid du Colombier }
603ff48bf5SDavid du Colombier 
613ff48bf5SDavid du Colombier static void
csr_ack(Ctlr * ctlr,int ev)623ff48bf5SDavid du Colombier csr_ack(Ctlr *ctlr, int ev)
633ff48bf5SDavid du Colombier {
643ff48bf5SDavid du Colombier 	csr_outs(ctlr, WR_EvAck, ev);
653ff48bf5SDavid du Colombier }
663ff48bf5SDavid du Colombier 
673ff48bf5SDavid du Colombier static void
csr_inss(Ctlr * ctlr,int reg,void * dat,int ndat)683ff48bf5SDavid du Colombier csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat)
693ff48bf5SDavid du Colombier {
703ff48bf5SDavid du Colombier 	ushort *rp, *wp;
713ff48bf5SDavid du Colombier 
723ff48bf5SDavid du Colombier 	if(ctlr->mmb){
733ff48bf5SDavid du Colombier 		rp = &ctlr->mmb[reg];
743ff48bf5SDavid du Colombier 		wp = dat;
753ff48bf5SDavid du Colombier 		while(ndat-- > 0)
763ff48bf5SDavid du Colombier 			*wp++ = *rp;
773ff48bf5SDavid du Colombier 	}else
783ff48bf5SDavid du Colombier 		inss(ctlr->iob+reg, dat, ndat);
793ff48bf5SDavid du Colombier }
803ff48bf5SDavid du Colombier 
813ff48bf5SDavid du Colombier static void
csr_outss(Ctlr * ctlr,int reg,void * dat,int ndat)823ff48bf5SDavid du Colombier csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat)
833ff48bf5SDavid du Colombier {
843ff48bf5SDavid du Colombier 	ushort *rp, *wp;
853ff48bf5SDavid du Colombier 
863ff48bf5SDavid du Colombier 	if(ctlr->mmb){
873ff48bf5SDavid du Colombier 		rp = dat;
883ff48bf5SDavid du Colombier 		wp = &ctlr->mmb[reg];
893ff48bf5SDavid du Colombier 		while(ndat-- > 0)
903ff48bf5SDavid du Colombier 			*wp = *rp++;
913ff48bf5SDavid du Colombier 	}else
923ff48bf5SDavid du Colombier 		outss(ctlr->iob+reg, dat, ndat);
933ff48bf5SDavid du Colombier }
943ff48bf5SDavid du Colombier 
953ff48bf5SDavid du Colombier // w_... routines do not ilock the Ctlr and should
963ff48bf5SDavid du Colombier // be called locked.
973ff48bf5SDavid du Colombier 
983ff48bf5SDavid du Colombier void
w_intdis(Ctlr * ctlr)993ff48bf5SDavid du Colombier w_intdis(Ctlr* ctlr)
1003ff48bf5SDavid du Colombier {
1013ff48bf5SDavid du Colombier 	csr_outs(ctlr, WR_IntEna, 0);
1023ff48bf5SDavid du Colombier 	csr_ack(ctlr, 0xffff);
1033ff48bf5SDavid du Colombier }
1043ff48bf5SDavid du Colombier 
1053ff48bf5SDavid du Colombier static void
w_intena(Ctlr * ctlr)1063ff48bf5SDavid du Colombier w_intena(Ctlr* ctlr)
1073ff48bf5SDavid du Colombier {
1083ff48bf5SDavid du Colombier 	csr_outs(ctlr, WR_IntEna, WEvs);
1093ff48bf5SDavid du Colombier }
1103ff48bf5SDavid du Colombier 
1113ff48bf5SDavid du Colombier int
w_cmd(Ctlr * ctlr,ushort cmd,ushort arg)1123ff48bf5SDavid du Colombier w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
1133ff48bf5SDavid du Colombier {
1143ff48bf5SDavid du Colombier 	int i, rc;
1153ff48bf5SDavid du Colombier 
1163ff48bf5SDavid du Colombier 	for(i=0; i<WTmOut; i++)
1173ff48bf5SDavid du Colombier 		if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0)
1183ff48bf5SDavid du Colombier 			break;
1193ff48bf5SDavid du Colombier 	if(i==WTmOut){
1203ff48bf5SDavid du Colombier 		print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd));
1213ff48bf5SDavid du Colombier 		return -1;
1223ff48bf5SDavid du Colombier 	}
1233ff48bf5SDavid du Colombier 
1243ff48bf5SDavid du Colombier 	csr_outs(ctlr, WR_Parm0, arg);
1253ff48bf5SDavid du Colombier 	csr_outs(ctlr, WR_Cmd, cmd);
1263ff48bf5SDavid du Colombier 
1273ff48bf5SDavid du Colombier 	for(i=0; i<WTmOut; i++)
1283ff48bf5SDavid du Colombier 		if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
1293ff48bf5SDavid du Colombier 			break;
1303ff48bf5SDavid du Colombier 	if(i==WTmOut){
1313ff48bf5SDavid du Colombier 		/*
1323ff48bf5SDavid du Colombier 		 * WCmdIni can take a really long time.
1333ff48bf5SDavid du Colombier 		 */
1343ff48bf5SDavid du Colombier 		enum { IniTmOut = 2000 };
1353ff48bf5SDavid du Colombier 		for(i=0; i<IniTmOut; i++){
1363ff48bf5SDavid du Colombier 			if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
1373ff48bf5SDavid du Colombier 				break;
1383ff48bf5SDavid du Colombier 			microdelay(100);
1393ff48bf5SDavid du Colombier 		}
1403ff48bf5SDavid du Colombier 		if(i < IniTmOut)
1413ff48bf5SDavid du Colombier 			if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i);
1423ff48bf5SDavid du Colombier 		if(i == IniTmOut){
1433ff48bf5SDavid du Colombier 			print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
1443ff48bf5SDavid du Colombier 			return -1;
1453ff48bf5SDavid du Colombier 		}
1463ff48bf5SDavid du Colombier 	}
1473ff48bf5SDavid du Colombier 	rc = csr_ins(ctlr, WR_Sts);
1483ff48bf5SDavid du Colombier 	csr_ack(ctlr, WCmdEv);
1493ff48bf5SDavid du Colombier 
1503ff48bf5SDavid du Colombier 	if((rc&WCmdMsk) != (cmd&WCmdMsk)){
1513ff48bf5SDavid du Colombier 		print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
1523ff48bf5SDavid du Colombier 		return -1;
1533ff48bf5SDavid du Colombier 	}
1543ff48bf5SDavid du Colombier 	if(rc&WResSts){
1553ff48bf5SDavid du Colombier 		/*
1563ff48bf5SDavid du Colombier 		 * Don't print; this happens on every WCmdAccWr for some reason.
1573ff48bf5SDavid du Colombier 		 */
1583ff48bf5SDavid du Colombier 		if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
1593ff48bf5SDavid du Colombier 		return -1;
1603ff48bf5SDavid du Colombier 	}
1613ff48bf5SDavid du Colombier 	return 0;
1623ff48bf5SDavid du Colombier }
1633ff48bf5SDavid du Colombier 
1643ff48bf5SDavid du Colombier static int
w_seek(Ctlr * ctlr,ushort id,ushort offset,int chan)1653ff48bf5SDavid du Colombier w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
1663ff48bf5SDavid du Colombier {
1673ff48bf5SDavid du Colombier 	int i, rc;
1683ff48bf5SDavid du Colombier 	static ushort sel[] = { WR_Sel0, WR_Sel1 };
1693ff48bf5SDavid du Colombier 	static ushort off[] = { WR_Off0, WR_Off1 };
1703ff48bf5SDavid du Colombier 
1713ff48bf5SDavid du Colombier 	if(chan != 0 && chan != 1)
1723ff48bf5SDavid du Colombier 		panic("wavelan: bad chan\n");
1733ff48bf5SDavid du Colombier 	csr_outs(ctlr, sel[chan], id);
1743ff48bf5SDavid du Colombier 	csr_outs(ctlr, off[chan], offset);
1753ff48bf5SDavid du Colombier 	for (i=0; i<WTmOut; i++){
1763ff48bf5SDavid du Colombier 		rc = csr_ins(ctlr, off[chan]);
1773ff48bf5SDavid du Colombier 		if((rc & (WBusyOff|WErrOff)) == 0)
1783ff48bf5SDavid du Colombier 			return 0;
1793ff48bf5SDavid du Colombier 	}
1803ff48bf5SDavid du Colombier 	return -1;
1813ff48bf5SDavid du Colombier }
1823ff48bf5SDavid du Colombier 
1833ff48bf5SDavid du Colombier int
w_inltv(Ctlr * ctlr,Wltv * ltv)1843ff48bf5SDavid du Colombier w_inltv(Ctlr* ctlr, Wltv* ltv)
1853ff48bf5SDavid du Colombier {
1863ff48bf5SDavid du Colombier 	int len;
1873ff48bf5SDavid du Colombier 	ushort code;
1883ff48bf5SDavid du Colombier 
1893ff48bf5SDavid du Colombier 	if(w_cmd(ctlr, WCmdAccRd, ltv->type)){
1903ff48bf5SDavid du Colombier 		DEBUG("wavelan: access read failed\n");
1913ff48bf5SDavid du Colombier 		return -1;
1923ff48bf5SDavid du Colombier 	}
1933ff48bf5SDavid du Colombier 	if(w_seek(ctlr,ltv->type,0,1)){
1943ff48bf5SDavid du Colombier 		DEBUG("wavelan: seek failed\n");
1953ff48bf5SDavid du Colombier 		return -1;
1963ff48bf5SDavid du Colombier 	}
1973ff48bf5SDavid du Colombier 	len = csr_ins(ctlr, WR_Data1);
1983ff48bf5SDavid du Colombier 	if(len > ltv->len)
1993ff48bf5SDavid du Colombier 		return -1;
2003ff48bf5SDavid du Colombier 	ltv->len = len;
2013ff48bf5SDavid du Colombier 	if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){
2023ff48bf5SDavid du Colombier 		USED(code);
2033ff48bf5SDavid du Colombier 		DEBUG("wavelan: type %x != code %x\n",ltv->type,code);
2043ff48bf5SDavid du Colombier 		return -1;
2053ff48bf5SDavid du Colombier 	}
2063ff48bf5SDavid du Colombier 	if(ltv->len > 0)
2073ff48bf5SDavid du Colombier 		csr_inss(ctlr, WR_Data1, &ltv->val, ltv->len-1);
2083ff48bf5SDavid du Colombier 
2093ff48bf5SDavid du Colombier 	return 0;
2103ff48bf5SDavid du Colombier }
2113ff48bf5SDavid du Colombier 
2123ff48bf5SDavid du Colombier static void
w_outltv(Ctlr * ctlr,Wltv * ltv)2133ff48bf5SDavid du Colombier w_outltv(Ctlr* ctlr, Wltv* ltv)
2143ff48bf5SDavid du Colombier {
2153ff48bf5SDavid du Colombier 	if(w_seek(ctlr,ltv->type, 0, 1))
2163ff48bf5SDavid du Colombier 		return;
2173ff48bf5SDavid du Colombier 	csr_outss(ctlr, WR_Data1, ltv, ltv->len+1);
2183ff48bf5SDavid du Colombier 	w_cmd(ctlr, WCmdAccWr, ltv->type);
2193ff48bf5SDavid du Colombier }
2203ff48bf5SDavid du Colombier 
2213ff48bf5SDavid du Colombier void
ltv_outs(Ctlr * ctlr,int type,ushort val)2223ff48bf5SDavid du Colombier ltv_outs(Ctlr* ctlr, int type, ushort val)
2233ff48bf5SDavid du Colombier {
2243ff48bf5SDavid du Colombier 	Wltv ltv;
2253ff48bf5SDavid du Colombier 
2263ff48bf5SDavid du Colombier 	ltv.len = 2;
2273ff48bf5SDavid du Colombier 	ltv.type = type;
2283ff48bf5SDavid du Colombier 	ltv.val = val;
2293ff48bf5SDavid du Colombier 	w_outltv(ctlr, &ltv);
2303ff48bf5SDavid du Colombier }
2313ff48bf5SDavid du Colombier 
2323ff48bf5SDavid du Colombier int
ltv_ins(Ctlr * ctlr,int type)2333ff48bf5SDavid du Colombier ltv_ins(Ctlr* ctlr, int type)
2343ff48bf5SDavid du Colombier {
2353ff48bf5SDavid du Colombier 	Wltv ltv;
2363ff48bf5SDavid du Colombier 
2373ff48bf5SDavid du Colombier 	ltv.len = 2;
2383ff48bf5SDavid du Colombier 	ltv.type = type;
2393ff48bf5SDavid du Colombier 	ltv.val = 0;
2403ff48bf5SDavid du Colombier 	if(w_inltv(ctlr, &ltv))
2413ff48bf5SDavid du Colombier 		return -1;
2423ff48bf5SDavid du Colombier 	return ltv.val;
2433ff48bf5SDavid du Colombier }
2443ff48bf5SDavid du Colombier 
2453ff48bf5SDavid du Colombier static void
ltv_outstr(Ctlr * ctlr,int type,char * val)2463ff48bf5SDavid du Colombier ltv_outstr(Ctlr* ctlr, int type, char* val)
2473ff48bf5SDavid du Colombier {
2483ff48bf5SDavid du Colombier 	Wltv ltv;
2493ff48bf5SDavid du Colombier 	int len;
2503ff48bf5SDavid du Colombier 
2513ff48bf5SDavid du Colombier 	len = strlen(val);
2523ff48bf5SDavid du Colombier 	if(len > sizeof(ltv.s))
2533ff48bf5SDavid du Colombier 		len = sizeof(ltv.s);
2543ff48bf5SDavid du Colombier 	memset(&ltv, 0, sizeof(ltv));
2553ff48bf5SDavid du Colombier 	ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2;
2563ff48bf5SDavid du Colombier 	ltv.type = type;
2573ff48bf5SDavid du Colombier 
2583ff48bf5SDavid du Colombier //	This should be ltv.slen = len; according to Axel Belinfante
2593ff48bf5SDavid du Colombier 	ltv.slen = len;
2603ff48bf5SDavid du Colombier 
2613ff48bf5SDavid du Colombier 	strncpy(ltv.s, val, len);
2623ff48bf5SDavid du Colombier 	w_outltv(ctlr, &ltv);
2633ff48bf5SDavid du Colombier }
2643ff48bf5SDavid du Colombier 
2653ff48bf5SDavid du Colombier static char Unkname[] = "who knows";
2663ff48bf5SDavid du Colombier static char Nilname[] = "card does not tell";
2673ff48bf5SDavid du Colombier 
2683ff48bf5SDavid du Colombier static char*
ltv_inname(Ctlr * ctlr,int type)2693ff48bf5SDavid du Colombier ltv_inname(Ctlr* ctlr, int type)
2703ff48bf5SDavid du Colombier {
2713ff48bf5SDavid du Colombier 	static Wltv ltv;
2723ff48bf5SDavid du Colombier 	int len;
2733ff48bf5SDavid du Colombier 
2743ff48bf5SDavid du Colombier 	memset(&ltv,0,sizeof(ltv));
2753ff48bf5SDavid du Colombier 	ltv.len = WNameLen/2+2;
2763ff48bf5SDavid du Colombier 	ltv.type = type;
2773ff48bf5SDavid du Colombier 	if(w_inltv(ctlr, &ltv))
2783ff48bf5SDavid du Colombier 		return Unkname;
2793ff48bf5SDavid du Colombier 	len = ltv.slen;
2803ff48bf5SDavid du Colombier 	if(len == 0 || ltv.s[0] == 0)
2813ff48bf5SDavid du Colombier 		return Nilname;
2823ff48bf5SDavid du Colombier 	if(len >= sizeof ltv.s)
2833ff48bf5SDavid du Colombier 		len = sizeof ltv.s - 1;
2843ff48bf5SDavid du Colombier 	ltv.s[len] = '\0';
2853ff48bf5SDavid du Colombier 	return ltv.s;
2863ff48bf5SDavid du Colombier }
2873ff48bf5SDavid du Colombier 
2883ff48bf5SDavid du Colombier static int
w_read(Ctlr * ctlr,int type,int off,void * buf,ulong len)2893ff48bf5SDavid du Colombier w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
2903ff48bf5SDavid du Colombier {
2913ff48bf5SDavid du Colombier 	if(w_seek(ctlr, type, off, 1)){
2923ff48bf5SDavid du Colombier 		DEBUG("wavelan: w_read: seek failed");
2933ff48bf5SDavid du Colombier 		return 0;
2943ff48bf5SDavid du Colombier 	}
2953ff48bf5SDavid du Colombier 	csr_inss(ctlr, WR_Data1, buf, len/2);
2963ff48bf5SDavid du Colombier 
2973ff48bf5SDavid du Colombier 	return len;
2983ff48bf5SDavid du Colombier }
2993ff48bf5SDavid du Colombier 
3003ff48bf5SDavid du Colombier static int
w_write(Ctlr * ctlr,int type,int off,void * buf,ulong len)3013ff48bf5SDavid du Colombier w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
3023ff48bf5SDavid du Colombier {
3033ff48bf5SDavid du Colombier 	if(w_seek(ctlr, type, off, 0)){
3043ff48bf5SDavid du Colombier 		DEBUG("wavelan: w_write: seek failed\n");
3053ff48bf5SDavid du Colombier 		return 0;
3063ff48bf5SDavid du Colombier 	}
3073ff48bf5SDavid du Colombier 
3083ff48bf5SDavid du Colombier 	csr_outss(ctlr, WR_Data0, buf, len/2);
3093ff48bf5SDavid du Colombier 	csr_outs(ctlr, WR_Data0, 0xdead);
3103ff48bf5SDavid du Colombier 	csr_outs(ctlr, WR_Data0, 0xbeef);
3113ff48bf5SDavid du Colombier 	if(w_seek(ctlr, type, off + len, 0)){
3123ff48bf5SDavid du Colombier 		DEBUG("wavelan: write seek failed\n");
3133ff48bf5SDavid du Colombier 		return 0;
3143ff48bf5SDavid du Colombier 	}
3159acf0835SDavid du Colombier 	if(csr_ins(ctlr, WR_Data0) == 0xdead && csr_ins(ctlr, WR_Data0) == 0xbeef)
3163ff48bf5SDavid du Colombier 		return len;
3179acf0835SDavid du Colombier 
3183ff48bf5SDavid du Colombier 	DEBUG("wavelan: Hermes bug byte.\n");
3193ff48bf5SDavid du Colombier 	return 0;
3203ff48bf5SDavid du Colombier }
3213ff48bf5SDavid du Colombier 
3223ff48bf5SDavid du Colombier static int
w_alloc(Ctlr * ctlr,int len)3233ff48bf5SDavid du Colombier w_alloc(Ctlr* ctlr, int len)
3243ff48bf5SDavid du Colombier {
3253ff48bf5SDavid du Colombier 	int rc;
3263ff48bf5SDavid du Colombier 	int i,j;
3273ff48bf5SDavid du Colombier 
3283ff48bf5SDavid du Colombier 	if(w_cmd(ctlr, WCmdMalloc, len)==0)
3293ff48bf5SDavid du Colombier 		for (i = 0; i<WTmOut; i++)
3303ff48bf5SDavid du Colombier 			if(csr_ins(ctlr, WR_EvSts) & WAllocEv){
3313ff48bf5SDavid du Colombier 				csr_ack(ctlr, WAllocEv);
3323ff48bf5SDavid du Colombier 				rc=csr_ins(ctlr, WR_Alloc);
3333ff48bf5SDavid du Colombier 				if(w_seek(ctlr, rc, 0, 0))
3343ff48bf5SDavid du Colombier 					return -1;
3353ff48bf5SDavid du Colombier 				len = len/2;
3363ff48bf5SDavid du Colombier 				for (j=0; j<len; j++)
3373ff48bf5SDavid du Colombier 					csr_outs(ctlr, WR_Data0, 0);
3383ff48bf5SDavid du Colombier 				return rc;
3393ff48bf5SDavid du Colombier 			}
3403ff48bf5SDavid du Colombier 	return -1;
3413ff48bf5SDavid du Colombier }
3423ff48bf5SDavid du Colombier 
3433ff48bf5SDavid du Colombier static int
w_enable(Ether * ether)3443ff48bf5SDavid du Colombier w_enable(Ether* ether)
3453ff48bf5SDavid du Colombier {
3463ff48bf5SDavid du Colombier 	Wltv ltv;
3473ff48bf5SDavid du Colombier 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
3483ff48bf5SDavid du Colombier 
3493ff48bf5SDavid du Colombier 	if(!ctlr)
3503ff48bf5SDavid du Colombier 		return -1;
3513ff48bf5SDavid du Colombier 
3523ff48bf5SDavid du Colombier 	w_intdis(ctlr);
3533ff48bf5SDavid du Colombier 	w_cmd(ctlr, WCmdDis, 0);
3543ff48bf5SDavid du Colombier 	w_intdis(ctlr);
3553ff48bf5SDavid du Colombier 	if(w_cmd(ctlr, WCmdIni, 0))
3563ff48bf5SDavid du Colombier 		return -1;
3573ff48bf5SDavid du Colombier 	w_intdis(ctlr);
3583ff48bf5SDavid du Colombier 
3593ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_Tick, 8);
3603ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen);
3613ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype);
3623ff48bf5SDavid du Colombier  	ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss);
3633ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres);
3643ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
3653ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
3663ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
3673ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
3683ff48bf5SDavid du Colombier 	if(*ctlr->netname)
3693ff48bf5SDavid du Colombier 		ltv_outstr(ctlr, WTyp_NetName, ctlr->netname);
3703ff48bf5SDavid du Colombier 	if(*ctlr->wantname)
3713ff48bf5SDavid du Colombier 		ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname);
3723ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_Chan, ctlr->chan);
3733ff48bf5SDavid du Colombier 	if(*ctlr->nodename)
3743ff48bf5SDavid du Colombier 		ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename);
3753ff48bf5SDavid du Colombier 	ltv.len = 4;
3763ff48bf5SDavid du Colombier 	ltv.type = WTyp_Mac;
3773ff48bf5SDavid du Colombier 	memmove(ltv.addr, ether->ea, Eaddrlen);
3783ff48bf5SDavid du Colombier 	w_outltv(ctlr, &ltv);
3793ff48bf5SDavid du Colombier 
3803ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));
3813ff48bf5SDavid du Colombier 
38215763c87SDavid du Colombier 	if(ctlr->hascrypt && ctlr->crypt){
3833ff48bf5SDavid du Colombier 		ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt);
3843ff48bf5SDavid du Colombier 		ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey);
3853ff48bf5SDavid du Colombier 		w_outltv(ctlr, &ctlr->keys);
3863ff48bf5SDavid du Colombier 		ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
3873ff48bf5SDavid du Colombier 	}
3883ff48bf5SDavid du Colombier 
3893ff48bf5SDavid du Colombier 	// BUG: set multicast addresses
3903ff48bf5SDavid du Colombier 
3913ff48bf5SDavid du Colombier 	if(w_cmd(ctlr, WCmdEna, 0)){
3923ff48bf5SDavid du Colombier 		DEBUG("wavelan: Enable failed");
3933ff48bf5SDavid du Colombier 		return -1;
3943ff48bf5SDavid du Colombier 	}
3953ff48bf5SDavid du Colombier 	ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
3963ff48bf5SDavid du Colombier 	ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
3973ff48bf5SDavid du Colombier 	if(ctlr->txdid == -1 || ctlr->txmid == -1)
3983ff48bf5SDavid du Colombier 		DEBUG("wavelan: alloc failed");
3993ff48bf5SDavid du Colombier 	ctlr->txbusy = 0;
4003ff48bf5SDavid du Colombier 	w_intena(ctlr);
4013ff48bf5SDavid du Colombier 	return 0;
4023ff48bf5SDavid du Colombier }
4033ff48bf5SDavid du Colombier 
4043ff48bf5SDavid du Colombier static void
w_rxdone(Ether * ether)4053ff48bf5SDavid du Colombier w_rxdone(Ether* ether)
4063ff48bf5SDavid du Colombier {
4073ff48bf5SDavid du Colombier 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
4083ff48bf5SDavid du Colombier 	int len, sp;
4093ff48bf5SDavid du Colombier 	WFrame f;
4103ff48bf5SDavid du Colombier 	Block* bp=0;
4113ff48bf5SDavid du Colombier 	Etherpkt* ep;
4123ff48bf5SDavid du Colombier 
4133ff48bf5SDavid du Colombier 	sp = csr_ins(ctlr, WR_RXId);
4143ff48bf5SDavid du Colombier 	len = w_read(ctlr, sp, 0, &f, sizeof(f));
4153ff48bf5SDavid du Colombier 	if(len == 0){
4163ff48bf5SDavid du Colombier 		DEBUG("wavelan: read frame error\n");
4173ff48bf5SDavid du Colombier 		goto rxerror;
4183ff48bf5SDavid du Colombier 	}
4193ff48bf5SDavid du Colombier 	if(f.sts&WF_Err){
4203ff48bf5SDavid du Colombier 		goto rxerror;
4213ff48bf5SDavid du Colombier 	}
4223ff48bf5SDavid du Colombier 	switch(f.sts){
4233ff48bf5SDavid du Colombier 	case WF_1042:
4243ff48bf5SDavid du Colombier 	case WF_Tunnel:
4253ff48bf5SDavid du Colombier 	case WF_WMP:
4263ff48bf5SDavid du Colombier 		len = f.dlen + WSnapHdrLen;
4273ff48bf5SDavid du Colombier 		bp = iallocb(ETHERHDRSIZE + len + 2);
4283ff48bf5SDavid du Colombier 		if(!bp)
4293ff48bf5SDavid du Colombier 			goto rxerror;
4303ff48bf5SDavid du Colombier 		ep = (Etherpkt*) bp->wp;
4313ff48bf5SDavid du Colombier 		memmove(ep->d, f.addr1, Eaddrlen);
4323ff48bf5SDavid du Colombier 		memmove(ep->s, f.addr2, Eaddrlen);
4333ff48bf5SDavid du Colombier 		memmove(ep->type,&f.type,2);
4343ff48bf5SDavid du Colombier 		bp->wp += ETHERHDRSIZE;
4353ff48bf5SDavid du Colombier 		if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){
4363ff48bf5SDavid du Colombier 			DEBUG("wavelan: read 802.11 error\n");
4373ff48bf5SDavid du Colombier 			goto rxerror;
4383ff48bf5SDavid du Colombier 		}
4393ff48bf5SDavid du Colombier 		bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen);
4403ff48bf5SDavid du Colombier 		break;
4413ff48bf5SDavid du Colombier 	default:
4423ff48bf5SDavid du Colombier 		len = ETHERHDRSIZE + f.dlen + 2;
4433ff48bf5SDavid du Colombier 		bp = iallocb(len);
4443ff48bf5SDavid du Colombier 		if(!bp)
4453ff48bf5SDavid du Colombier 			goto rxerror;
4463ff48bf5SDavid du Colombier 		if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){
4473ff48bf5SDavid du Colombier 			DEBUG("wavelan: read 800.3 error\n");
4483ff48bf5SDavid du Colombier 			goto rxerror;
4493ff48bf5SDavid du Colombier 		}
4503ff48bf5SDavid du Colombier 		bp->wp += len;
4513ff48bf5SDavid du Colombier 	}
4523ff48bf5SDavid du Colombier 
4533ff48bf5SDavid du Colombier 	ctlr->nrx++;
4543ff48bf5SDavid du Colombier 	etheriq(ether,bp,1);
4553ff48bf5SDavid du Colombier 	ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16;
4563ff48bf5SDavid du Colombier 	ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16;
4573ff48bf5SDavid du Colombier 	return;
4583ff48bf5SDavid du Colombier 
4593ff48bf5SDavid du Colombier rxerror:
4603ff48bf5SDavid du Colombier 	freeb(bp);
4613ff48bf5SDavid du Colombier 	ctlr->nrxerr++;
4623ff48bf5SDavid du Colombier }
4633ff48bf5SDavid du Colombier 
4643ff48bf5SDavid du Colombier static void
w_txstart(Ether * ether)4653ff48bf5SDavid du Colombier w_txstart(Ether* ether)
4663ff48bf5SDavid du Colombier {
4673ff48bf5SDavid du Colombier 	Etherpkt *pkt;
4683ff48bf5SDavid du Colombier 	Ctlr *ctlr;
4693ff48bf5SDavid du Colombier 	Block *bp;
4703ff48bf5SDavid du Colombier 	int len, off;
4713ff48bf5SDavid du Colombier 
4723ff48bf5SDavid du Colombier 	if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy)
4733ff48bf5SDavid du Colombier 		return;
4743ff48bf5SDavid du Colombier 
4753ff48bf5SDavid du Colombier 	if((bp = qget(ether->oq)) == nil)
4763ff48bf5SDavid du Colombier 		return;
4773ff48bf5SDavid du Colombier 	pkt = (Etherpkt*)bp->rp;
4783ff48bf5SDavid du Colombier 
4793ff48bf5SDavid du Colombier 	//
4803ff48bf5SDavid du Colombier 	// If the packet header type field is > 1500 it is an IP or
4813ff48bf5SDavid du Colombier 	// ARP datagram, otherwise it is an 802.3 packet. See RFC1042.
4823ff48bf5SDavid du Colombier 	//
4833ff48bf5SDavid du Colombier 	memset(&ctlr->txf, 0, sizeof(ctlr->txf));
4843ff48bf5SDavid du Colombier 	if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){
4853ff48bf5SDavid du Colombier 		ctlr->txf.framectl = WF_Data;
4863ff48bf5SDavid du Colombier 		memmove(ctlr->txf.addr1, pkt->d, Eaddrlen);
4873ff48bf5SDavid du Colombier 		memmove(ctlr->txf.addr2, pkt->s, Eaddrlen);
4883ff48bf5SDavid du Colombier 		memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen);
4893ff48bf5SDavid du Colombier 		memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen);
4903ff48bf5SDavid du Colombier 		memmove(&ctlr->txf.type, pkt->type, 2);
4913ff48bf5SDavid du Colombier 		bp->rp += ETHERHDRSIZE;
4923ff48bf5SDavid du Colombier 		len = BLEN(bp);
4933ff48bf5SDavid du Colombier 		off = WF_802_11_Off;
4943ff48bf5SDavid du Colombier 		ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen;
4953ff48bf5SDavid du Colombier 		hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
4963ff48bf5SDavid du Colombier 		hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
4973ff48bf5SDavid du Colombier 		hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen);
4983ff48bf5SDavid du Colombier 	}
4993ff48bf5SDavid du Colombier 	else{
5003ff48bf5SDavid du Colombier 		len = BLEN(bp);
5013ff48bf5SDavid du Colombier 		off = WF_802_3_Off;
5023ff48bf5SDavid du Colombier 		ctlr->txf.dlen = len;
5033ff48bf5SDavid du Colombier 	}
5043ff48bf5SDavid du Colombier 	w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf));
5053ff48bf5SDavid du Colombier 	w_write(ctlr, ctlr->txdid, off, bp->rp, len+2);
5063ff48bf5SDavid du Colombier 
5073ff48bf5SDavid du Colombier 	if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){
5083ff48bf5SDavid du Colombier 		DEBUG("wavelan: transmit failed\n");
5093ff48bf5SDavid du Colombier 		ctlr->ntxerr++;
5103ff48bf5SDavid du Colombier 	}
5113ff48bf5SDavid du Colombier 	else{
5123ff48bf5SDavid du Colombier 		ctlr->txbusy = 1;
5133ff48bf5SDavid du Colombier 		ctlr->txtmout = 2;
5143ff48bf5SDavid du Colombier 	}
5153ff48bf5SDavid du Colombier 	freeb(bp);
5163ff48bf5SDavid du Colombier }
5173ff48bf5SDavid du Colombier 
5183ff48bf5SDavid du Colombier static void
w_txdone(Ctlr * ctlr,int sts)5193ff48bf5SDavid du Colombier w_txdone(Ctlr* ctlr, int sts)
5203ff48bf5SDavid du Colombier {
5213ff48bf5SDavid du Colombier 	ctlr->txbusy = 0;
5223ff48bf5SDavid du Colombier 	ctlr->txtmout = 0;
5233ff48bf5SDavid du Colombier 	if(sts & WTxErrEv)
5243ff48bf5SDavid du Colombier 		ctlr->ntxerr++;
5253ff48bf5SDavid du Colombier 	else
5263ff48bf5SDavid du Colombier 		ctlr->ntx++;
5273ff48bf5SDavid du Colombier }
5283ff48bf5SDavid du Colombier 
52915763c87SDavid du Colombier /* save the stats info in the ctlr struct */
53015763c87SDavid du Colombier static void
w_stats(Ctlr * ctlr,int len)53115763c87SDavid du Colombier w_stats(Ctlr* ctlr, int len)
5323ff48bf5SDavid du Colombier {
53315763c87SDavid du Colombier 	int i, rc;
5343ff48bf5SDavid du Colombier 	ulong* p = (ulong*)&ctlr->WStats;
5353ff48bf5SDavid du Colombier 	ulong* pend = (ulong*)&ctlr->end;
5363ff48bf5SDavid du Colombier 
53715763c87SDavid du Colombier 	for (i = 0; i < len && p < pend; i++){
5383ff48bf5SDavid du Colombier 		rc = csr_ins(ctlr, WR_Data1);
5393ff48bf5SDavid du Colombier 		if(rc > 0xf000)
5403ff48bf5SDavid du Colombier 			rc = ~rc & 0xffff;
5413ff48bf5SDavid du Colombier 		p[i] += rc;
5423ff48bf5SDavid du Colombier 	}
54315763c87SDavid du Colombier }
54415763c87SDavid du Colombier 
54515763c87SDavid du Colombier /* send the base station scan info to any readers */
54615763c87SDavid du Colombier static void
w_scaninfo(Ether * ether,Ctlr * ctlr,int len)54715763c87SDavid du Colombier w_scaninfo(Ether* ether, Ctlr *ctlr, int len)
54815763c87SDavid du Colombier {
54915763c87SDavid du Colombier 	int i, j;
55015763c87SDavid du Colombier 	Netfile **ep, *f, **fp;
55115763c87SDavid du Colombier 	Block *bp;
55215763c87SDavid du Colombier 	WScan *wsp;
553fb7f0c93SDavid du Colombier 	ushort *scanbuf;
554fb7f0c93SDavid du Colombier 
555fb7f0c93SDavid du Colombier 	scanbuf = malloc(len*2);
556fb7f0c93SDavid du Colombier 	if(scanbuf == nil)
557fb7f0c93SDavid du Colombier 		return;
55815763c87SDavid du Colombier 
55915763c87SDavid du Colombier 	for (i = 0; i < len ; i++)
560fb7f0c93SDavid du Colombier 		scanbuf[i] = csr_ins(ctlr, WR_Data1);
56115763c87SDavid du Colombier 
562fb7f0c93SDavid du Colombier 	/* calculate number of samples */
563fb7f0c93SDavid du Colombier 	len /= 25;
564fb7f0c93SDavid du Colombier 	if(len == 0)
565fb7f0c93SDavid du Colombier 		goto out;
566fb7f0c93SDavid du Colombier 
56715763c87SDavid du Colombier 	i = ether->scan;
56815763c87SDavid du Colombier 	ep = &ether->f[Ntypes];
56915763c87SDavid du Colombier 	for(fp = ether->f; fp < ep && i > 0; fp++){
57015763c87SDavid du Colombier 		f = *fp;
57115763c87SDavid du Colombier 		if(f == nil || f->scan == 0)
57215763c87SDavid du Colombier 			continue;
57315763c87SDavid du Colombier 
574fb7f0c93SDavid du Colombier 		bp = iallocb(100*len);
57515763c87SDavid du Colombier 		if(bp == nil)
57615763c87SDavid du Colombier 			break;
577fb7f0c93SDavid du Colombier 		for(j = 0; j < len; j++){
578fb7f0c93SDavid du Colombier 			wsp = (WScan*)(&scanbuf[j*25]);
57915763c87SDavid du Colombier 			if(wsp->ssid_len > 32)
58015763c87SDavid du Colombier 				wsp->ssid_len = 32;
581fb7f0c93SDavid du Colombier 			bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim,
58215763c87SDavid du Colombier 				"ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n",
58315763c87SDavid du Colombier 				wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal,
58415763c87SDavid du Colombier 				wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":"");
58515763c87SDavid du Colombier 		}
58615763c87SDavid du Colombier 		qpass(f->in, bp);
58715763c87SDavid du Colombier 		i--;
58815763c87SDavid du Colombier 	}
589fb7f0c93SDavid du Colombier out:
590fb7f0c93SDavid du Colombier 	free(scanbuf);
59115763c87SDavid du Colombier }
59215763c87SDavid du Colombier 
59315763c87SDavid du Colombier static int
w_info(Ether * ether,Ctlr * ctlr)59415763c87SDavid du Colombier w_info(Ether *ether, Ctlr* ctlr)
59515763c87SDavid du Colombier {
59615763c87SDavid du Colombier 	int sp;
59715763c87SDavid du Colombier 	Wltv ltv;
59815763c87SDavid du Colombier 
59915763c87SDavid du Colombier 	sp = csr_ins(ctlr, WR_InfoId);
60015763c87SDavid du Colombier 	ltv.len = ltv.type = 0;
60115763c87SDavid du Colombier 	w_read(ctlr, sp, 0, &ltv, 4);
60215763c87SDavid du Colombier 	ltv.len--;
60315763c87SDavid du Colombier 	switch(ltv.type){
60415763c87SDavid du Colombier 	case WTyp_Stats:
60515763c87SDavid du Colombier 		w_stats(ctlr, ltv.len);
60615763c87SDavid du Colombier 		return 0;
60715763c87SDavid du Colombier 	case WTyp_Scan:
60815763c87SDavid du Colombier 		w_scaninfo(ether, ctlr, ltv.len);
6093ff48bf5SDavid du Colombier 		return 0;
6103ff48bf5SDavid du Colombier 	}
6113ff48bf5SDavid du Colombier 	return -1;
6123ff48bf5SDavid du Colombier }
6133ff48bf5SDavid du Colombier 
61415763c87SDavid du Colombier /* set scanning interval */
61515763c87SDavid du Colombier static void
w_scanbs(void * a,uint secs)61615763c87SDavid du Colombier w_scanbs(void *a, uint secs)
61715763c87SDavid du Colombier {
61815763c87SDavid du Colombier 	Ether *ether = a;
61915763c87SDavid du Colombier 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
62015763c87SDavid du Colombier 
62115763c87SDavid du Colombier 	ctlr->scanticks = secs*(1000/MSperTick);
62215763c87SDavid du Colombier }
62315763c87SDavid du Colombier 
6243ff48bf5SDavid du Colombier static void
w_intr(Ether * ether)6253ff48bf5SDavid du Colombier w_intr(Ether *ether)
6263ff48bf5SDavid du Colombier {
6273ff48bf5SDavid du Colombier 	int rc, txid;
6283ff48bf5SDavid du Colombier 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
6293ff48bf5SDavid du Colombier 
6303ff48bf5SDavid du Colombier 	if((ctlr->state & Power) == 0)
6313ff48bf5SDavid du Colombier 		return;
6323ff48bf5SDavid du Colombier 
6333ff48bf5SDavid du Colombier 	if((ctlr->state & Attached) == 0){
6343ff48bf5SDavid du Colombier 		csr_ack(ctlr, 0xffff);
6353ff48bf5SDavid du Colombier 		csr_outs(ctlr, WR_IntEna, 0);
6363ff48bf5SDavid du Colombier 		return;
6373ff48bf5SDavid du Colombier 	}
6383ff48bf5SDavid du Colombier 
6393ff48bf5SDavid du Colombier 	rc = csr_ins(ctlr, WR_EvSts);
6403ff48bf5SDavid du Colombier 	csr_ack(ctlr, ~WEvs);	// Not interested in them
6413ff48bf5SDavid du Colombier 	if(rc & WRXEv){
6423ff48bf5SDavid du Colombier 		w_rxdone(ether);
6433ff48bf5SDavid du Colombier 		csr_ack(ctlr, WRXEv);
6443ff48bf5SDavid du Colombier 	}
6453ff48bf5SDavid du Colombier 	if(rc & WTXEv){
6463ff48bf5SDavid du Colombier 		w_txdone(ctlr, rc);
6473ff48bf5SDavid du Colombier 		csr_ack(ctlr, WTXEv);
6483ff48bf5SDavid du Colombier 	}
6493ff48bf5SDavid du Colombier 	if(rc & WAllocEv){
6503ff48bf5SDavid du Colombier 		ctlr->nalloc++;
6513ff48bf5SDavid du Colombier 		txid = csr_ins(ctlr, WR_Alloc);
6523ff48bf5SDavid du Colombier 		csr_ack(ctlr, WAllocEv);
6533ff48bf5SDavid du Colombier 		if(txid == ctlr->txdid){
6543ff48bf5SDavid du Colombier 			if((rc & WTXEv) == 0)
6553ff48bf5SDavid du Colombier 				w_txdone(ctlr, rc);
6563ff48bf5SDavid du Colombier 		}
6573ff48bf5SDavid du Colombier 	}
6583ff48bf5SDavid du Colombier 	if(rc & WInfoEv){
6593ff48bf5SDavid du Colombier 		ctlr->ninfo++;
66015763c87SDavid du Colombier 		w_info(ether, ctlr);
6613ff48bf5SDavid du Colombier 		csr_ack(ctlr, WInfoEv);
6623ff48bf5SDavid du Colombier 	}
6633ff48bf5SDavid du Colombier 	if(rc & WTxErrEv){
6643ff48bf5SDavid du Colombier 		w_txdone(ctlr, rc);
6653ff48bf5SDavid du Colombier 		csr_ack(ctlr, WTxErrEv);
6663ff48bf5SDavid du Colombier 	}
6673ff48bf5SDavid du Colombier 	if(rc & WIDropEv){
6683ff48bf5SDavid du Colombier 		ctlr->nidrop++;
6693ff48bf5SDavid du Colombier 		csr_ack(ctlr, WIDropEv);
6703ff48bf5SDavid du Colombier 	}
6713ff48bf5SDavid du Colombier 	w_txstart(ether);
6723ff48bf5SDavid du Colombier }
6733ff48bf5SDavid du Colombier 
6743ff48bf5SDavid du Colombier // Watcher to ensure that the card still works properly and
6753ff48bf5SDavid du Colombier // to request WStats updates once a minute.
6763ff48bf5SDavid du Colombier // BUG: it runs much more often, see the comment below.
6773ff48bf5SDavid du Colombier 
6783ff48bf5SDavid du Colombier static void
w_timer(void * arg)6793ff48bf5SDavid du Colombier w_timer(void* arg)
6803ff48bf5SDavid du Colombier {
6813ff48bf5SDavid du Colombier 	Ether* ether = (Ether*) arg;
6823ff48bf5SDavid du Colombier 	Ctlr* ctlr = (Ctlr*)ether->ctlr;
6833ff48bf5SDavid du Colombier 
6843ff48bf5SDavid du Colombier 	ctlr->timerproc = up;
6853ff48bf5SDavid du Colombier 	for(;;){
686dc5a79c1SDavid du Colombier 		tsleep(&up->sleep, return0, 0, MSperTick);
6873ff48bf5SDavid du Colombier 		ctlr = (Ctlr*)ether->ctlr;
6883ff48bf5SDavid du Colombier 		if(ctlr == 0)
6893ff48bf5SDavid du Colombier 			break;
6903ff48bf5SDavid du Colombier 		if((ctlr->state & (Attached|Power)) != (Attached|Power))
6913ff48bf5SDavid du Colombier 			continue;
6923ff48bf5SDavid du Colombier 		ctlr->ticks++;
6933ff48bf5SDavid du Colombier 
6943ff48bf5SDavid du Colombier 		ilock(ctlr);
6953ff48bf5SDavid du Colombier 
6963ff48bf5SDavid du Colombier 		// Seems that the card gets frames BUT does
6973ff48bf5SDavid du Colombier 		// not send the interrupt; this is a problem because
6983ff48bf5SDavid du Colombier 		// I suspect it runs out of receive buffers and
6993ff48bf5SDavid du Colombier 		// stops receiving until a transmit watchdog
7003ff48bf5SDavid du Colombier 		// reenables the card.
7013ff48bf5SDavid du Colombier 		// The problem is serious because it leads to
7023ff48bf5SDavid du Colombier 		// poor rtts.
7033ff48bf5SDavid du Colombier 		// This can be seen clearly by commenting out
7043ff48bf5SDavid du Colombier 		// the next if and doing a ping: it will stop
7053ff48bf5SDavid du Colombier 		// receiving (although the icmp replies are being
7063ff48bf5SDavid du Colombier 		// issued from the remote) after a few seconds.
7073ff48bf5SDavid du Colombier 		// Of course this `bug' could be because I'm reading
7083ff48bf5SDavid du Colombier 		// the card frames in the wrong way; due to the
7093ff48bf5SDavid du Colombier 		// lack of documentation I cannot know.
7103ff48bf5SDavid du Colombier 
7113ff48bf5SDavid du Colombier 		if(csr_ins(ctlr, WR_EvSts)&WEvs){
7123ff48bf5SDavid du Colombier 			ctlr->tickintr++;
7133ff48bf5SDavid du Colombier 			w_intr(ether);
7143ff48bf5SDavid du Colombier 		}
7153ff48bf5SDavid du Colombier 
7163ff48bf5SDavid du Colombier 		if((ctlr->ticks % 10) == 0) {
7173ff48bf5SDavid du Colombier 			if(ctlr->txtmout && --ctlr->txtmout == 0){
7183ff48bf5SDavid du Colombier 				ctlr->nwatchdogs++;
7193ff48bf5SDavid du Colombier 				w_txdone(ctlr, WTxErrEv);
7203ff48bf5SDavid du Colombier 				if(w_enable(ether)){
7213ff48bf5SDavid du Colombier 					DEBUG("wavelan: wdog enable failed\n");
7223ff48bf5SDavid du Colombier 				}
7233ff48bf5SDavid du Colombier 				w_txstart(ether);
7243ff48bf5SDavid du Colombier 			}
7253ff48bf5SDavid du Colombier 			if((ctlr->ticks % 120) == 0)
7263ff48bf5SDavid du Colombier 			if(ctlr->txbusy == 0)
72715763c87SDavid du Colombier 				w_cmd(ctlr, WCmdEnquire, WTyp_Stats);
72815763c87SDavid du Colombier 			if(ctlr->scanticks > 0)
72915763c87SDavid du Colombier 			if((ctlr->ticks % ctlr->scanticks) == 0)
73015763c87SDavid du Colombier 			if(ctlr->txbusy == 0)
73115763c87SDavid du Colombier 				w_cmd(ctlr, WCmdEnquire, WTyp_Scan);
7323ff48bf5SDavid du Colombier 		}
7333ff48bf5SDavid du Colombier 		iunlock(ctlr);
7343ff48bf5SDavid du Colombier 	}
7353ff48bf5SDavid du Colombier 	pexit("terminated", 0);
7363ff48bf5SDavid du Colombier }
7373ff48bf5SDavid du Colombier 
7383ff48bf5SDavid du Colombier void
w_multicast(void * ether,uchar *,int add)7395fb521f9SDavid du Colombier w_multicast(void *ether, uchar*, int add)
7403ff48bf5SDavid du Colombier {
7415fb521f9SDavid du Colombier 	/* BUG: use controller's multicast filter */
7425fb521f9SDavid du Colombier 	if (add)
7435fb521f9SDavid du Colombier 		w_promiscuous(ether, 1);
7443ff48bf5SDavid du Colombier }
7453ff48bf5SDavid du Colombier 
7463ff48bf5SDavid du Colombier void
w_attach(Ether * ether)7473ff48bf5SDavid du Colombier w_attach(Ether* ether)
7483ff48bf5SDavid du Colombier {
7493ff48bf5SDavid du Colombier 	Ctlr* ctlr;
7503ff48bf5SDavid du Colombier 	char name[64];
7513ff48bf5SDavid du Colombier 	int rc;
7523ff48bf5SDavid du Colombier 
7533ff48bf5SDavid du Colombier 	if(ether->ctlr == 0)
7543ff48bf5SDavid du Colombier 		return;
7553ff48bf5SDavid du Colombier 
7563ff48bf5SDavid du Colombier 	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
7573ff48bf5SDavid du Colombier 	ctlr = (Ctlr*) ether->ctlr;
7583ff48bf5SDavid du Colombier 	if((ctlr->state & Attached) == 0){
7593ff48bf5SDavid du Colombier 		ilock(ctlr);
7603ff48bf5SDavid du Colombier 		rc = w_enable(ether);
7613ff48bf5SDavid du Colombier 		iunlock(ctlr);
7623ff48bf5SDavid du Colombier 		if(rc == 0){
7633ff48bf5SDavid du Colombier 			ctlr->state |= Attached;
7643ff48bf5SDavid du Colombier 			kproc(name, w_timer, ether);
7653ff48bf5SDavid du Colombier 		} else
7663ff48bf5SDavid du Colombier 			print("#l%d: enable failed\n",ether->ctlrno);
7673ff48bf5SDavid du Colombier 	}
7683ff48bf5SDavid du Colombier }
7693ff48bf5SDavid du Colombier 
7703ff48bf5SDavid du Colombier void
w_detach(Ether * ether)7713ff48bf5SDavid du Colombier w_detach(Ether* ether)
7723ff48bf5SDavid du Colombier {
7733ff48bf5SDavid du Colombier 	Ctlr* ctlr;
7743ff48bf5SDavid du Colombier 	char name[64];
7753ff48bf5SDavid du Colombier 
7763ff48bf5SDavid du Colombier 	if(ether->ctlr == nil)
7773ff48bf5SDavid du Colombier 		return;
7783ff48bf5SDavid du Colombier 
7793ff48bf5SDavid du Colombier 	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
7803ff48bf5SDavid du Colombier 	ctlr = (Ctlr*) ether->ctlr;
7813ff48bf5SDavid du Colombier 	if(ctlr->state & Attached){
7823ff48bf5SDavid du Colombier 		ilock(ctlr);
7833ff48bf5SDavid du Colombier 		w_intdis(ctlr);
7843ff48bf5SDavid du Colombier 		if(ctlr->timerproc){
7853ff48bf5SDavid du Colombier 			if(!postnote(ctlr->timerproc, 1, "kill", NExit))
7863ff48bf5SDavid du Colombier 				print("timerproc note not posted\n");
7873ff48bf5SDavid du Colombier 			print("w_detach, killing 0x%p\n", ctlr->timerproc);
7883ff48bf5SDavid du Colombier 		}
7893ff48bf5SDavid du Colombier 		ctlr->state &= ~Attached;
7903ff48bf5SDavid du Colombier 		iunlock(ctlr);
7913ff48bf5SDavid du Colombier 	}
7923ff48bf5SDavid du Colombier 	ether->ctlr = nil;
7933ff48bf5SDavid du Colombier }
7943ff48bf5SDavid du Colombier 
7953ff48bf5SDavid du Colombier void
w_power(Ether * ether,int on)7963ff48bf5SDavid du Colombier w_power(Ether* ether, int on)
7973ff48bf5SDavid du Colombier {
7983ff48bf5SDavid du Colombier 	Ctlr *ctlr;
7993ff48bf5SDavid du Colombier 
8003ff48bf5SDavid du Colombier 	ctlr = (Ctlr*) ether->ctlr;
8013ff48bf5SDavid du Colombier 	ilock(ctlr);
8023ff48bf5SDavid du Colombier iprint("w_power %d\n", on);
8033ff48bf5SDavid du Colombier 	if(on){
8043ff48bf5SDavid du Colombier 		if((ctlr->state & Power) == 0){
8053ff48bf5SDavid du Colombier 			if (wavelanreset(ether, ctlr) < 0){
8063ff48bf5SDavid du Colombier 				iprint("w_power: reset failed\n");
8073ff48bf5SDavid du Colombier 				iunlock(ctlr);
8083ff48bf5SDavid du Colombier 				w_detach(ether);
8093ff48bf5SDavid du Colombier 				free(ctlr);
8103ff48bf5SDavid du Colombier 				return;
8113ff48bf5SDavid du Colombier 			}
8123ff48bf5SDavid du Colombier 			if(ctlr->state & Attached)
8133ff48bf5SDavid du Colombier 				w_enable(ether);
8143ff48bf5SDavid du Colombier 			ctlr->state |= Power;
8153ff48bf5SDavid du Colombier 		}
8163ff48bf5SDavid du Colombier 	}else{
8173ff48bf5SDavid du Colombier 		if(ctlr->state & Power){
8183ff48bf5SDavid du Colombier 			if(ctlr->state & Attached)
8193ff48bf5SDavid du Colombier 				w_intdis(ctlr);
8203ff48bf5SDavid du Colombier 			ctlr->state &= ~Power;
8213ff48bf5SDavid du Colombier 		}
8223ff48bf5SDavid du Colombier 	}
8233ff48bf5SDavid du Colombier 	iunlock(ctlr);
8243ff48bf5SDavid du Colombier }
8253ff48bf5SDavid du Colombier 
8263ff48bf5SDavid du Colombier #define PRINTSTAT(fmt,val)	l += snprint(p+l, READSTR-l, (fmt), (val))
8273ff48bf5SDavid du Colombier #define PRINTSTR(fmt)		l += snprint(p+l, READSTR-l, (fmt))
8283ff48bf5SDavid du Colombier 
8293ff48bf5SDavid du Colombier long
w_ifstat(Ether * ether,void * a,long n,ulong offset)8303ff48bf5SDavid du Colombier w_ifstat(Ether* ether, void* a, long n, ulong offset)
8313ff48bf5SDavid du Colombier {
8323ff48bf5SDavid du Colombier 	Ctlr *ctlr = (Ctlr*) ether->ctlr;
8333ff48bf5SDavid du Colombier 	char *k, *p;
8343ff48bf5SDavid du Colombier 	int i, l, txid;
8353ff48bf5SDavid du Colombier 
8363ff48bf5SDavid du Colombier 	ether->oerrs = ctlr->ntxerr;
8373ff48bf5SDavid du Colombier 	ether->crcs = ctlr->nrxfcserr;
8383ff48bf5SDavid du Colombier 	ether->frames = 0;
8393ff48bf5SDavid du Colombier 	ether->buffs = ctlr->nrxdropnobuf;
8403ff48bf5SDavid du Colombier 	ether->overflows = 0;
8413ff48bf5SDavid du Colombier 
8423ff48bf5SDavid du Colombier 	//
8433ff48bf5SDavid du Colombier 	// Offset must be zero or there's a possibility the
8443ff48bf5SDavid du Colombier 	// new data won't match the previous read.
8453ff48bf5SDavid du Colombier 	//
8463ff48bf5SDavid du Colombier 	if(n == 0 || offset != 0)
8473ff48bf5SDavid du Colombier 		return 0;
8483ff48bf5SDavid du Colombier 
8493ff48bf5SDavid du Colombier 	p = malloc(READSTR);
850*aa72973aSDavid du Colombier 	if(p == nil)
851*aa72973aSDavid du Colombier 		error(Enomem);
8523ff48bf5SDavid du Colombier 	l = 0;
8533ff48bf5SDavid du Colombier 
8543ff48bf5SDavid du Colombier 	PRINTSTAT("Signal: %d\n", ctlr->signal-149);
8553ff48bf5SDavid du Colombier 	PRINTSTAT("Noise: %d\n", ctlr->noise-149);
8563ff48bf5SDavid du Colombier 	PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
8573ff48bf5SDavid du Colombier 	PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
8583ff48bf5SDavid du Colombier 	PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint);
8593ff48bf5SDavid du Colombier 	PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
8603ff48bf5SDavid du Colombier 	PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
8613ff48bf5SDavid du Colombier 	PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);
8623ff48bf5SDavid du Colombier 	PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr);
8633ff48bf5SDavid du Colombier 	PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq);
8643ff48bf5SDavid du Colombier 	PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc);
8653ff48bf5SDavid du Colombier 	PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo);
8663ff48bf5SDavid du Colombier 	PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop);
8673ff48bf5SDavid du Colombier 	PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs);
8683ff48bf5SDavid du Colombier 	PRINTSTAT("Ticks: %ud\n", ctlr->ticks);
8693ff48bf5SDavid du Colombier 	PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr);
8703ff48bf5SDavid du Colombier 	k = ((ctlr->state & Attached) ? "attached" : "not attached");
8713ff48bf5SDavid du Colombier 	PRINTSTAT("Card %s", k);
8723ff48bf5SDavid du Colombier 	k = ((ctlr->state & Power) ? "on" : "off");
87315763c87SDavid du Colombier 	PRINTSTAT(", power %s", k);
8743ff48bf5SDavid du Colombier 	k = ((ctlr->txbusy)? ", txbusy" : "");
8753ff48bf5SDavid du Colombier 	PRINTSTAT("%s\n", k);
8763ff48bf5SDavid du Colombier 
8773ff48bf5SDavid du Colombier 	if(ctlr->hascrypt){
8783ff48bf5SDavid du Colombier 		PRINTSTR("Keys: ");
8793ff48bf5SDavid du Colombier 		for (i = 0; i < WNKeys; i++){
8803ff48bf5SDavid du Colombier 			if(ctlr->keys.keys[i].len == 0)
8813ff48bf5SDavid du Colombier 				PRINTSTR("none ");
8823ff48bf5SDavid du Colombier 			else if(SEEKEYS == 0)
8833ff48bf5SDavid du Colombier 				PRINTSTR("set ");
8843ff48bf5SDavid du Colombier 			else
8853ff48bf5SDavid du Colombier 				PRINTSTAT("%s ", ctlr->keys.keys[i].dat);
8863ff48bf5SDavid du Colombier 		}
8873ff48bf5SDavid du Colombier 		PRINTSTR("\n");
8883ff48bf5SDavid du Colombier 	}
8893ff48bf5SDavid du Colombier 
8903ff48bf5SDavid du Colombier 	// real card stats
8913ff48bf5SDavid du Colombier 	ilock(ctlr);
8923ff48bf5SDavid du Colombier 	PRINTSTR("\nCard stats: \n");
8933ff48bf5SDavid du Colombier 	PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
8943ff48bf5SDavid du Colombier 	PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
8953ff48bf5SDavid du Colombier 	i = ltv_ins(ctlr, WTyp_Ptype);
8963ff48bf5SDavid du Colombier 	PRINTSTAT("Port type: %d\n", i);
8973ff48bf5SDavid du Colombier 	PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
8983ff48bf5SDavid du Colombier 	PRINTSTAT("Current Transmit rate: %d\n",
8993ff48bf5SDavid du Colombier 		ltv_ins(ctlr, WTyp_CurTxRate));
9003ff48bf5SDavid du Colombier 	PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan));
9013ff48bf5SDavid du Colombier 	PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens));
9023ff48bf5SDavid du Colombier 	PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
90315763c87SDavid du Colombier 	if(i == WPTypeAdHoc)
9043ff48bf5SDavid du Colombier 		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
9053ff48bf5SDavid du Colombier 	else {
9063ff48bf5SDavid du Colombier 		Wltv ltv;
9073ff48bf5SDavid du Colombier 		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
9083ff48bf5SDavid du Colombier 		ltv.type = WTyp_BaseID;
9093ff48bf5SDavid du Colombier 		ltv.len = 4;
9103ff48bf5SDavid du Colombier 		if(w_inltv(ctlr, &ltv))
9113ff48bf5SDavid du Colombier 			print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
9123ff48bf5SDavid du Colombier 		l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
9133ff48bf5SDavid du Colombier 			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
9143ff48bf5SDavid du Colombier 	}
9153ff48bf5SDavid du Colombier 	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
9163ff48bf5SDavid du Colombier 	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
9173ff48bf5SDavid du Colombier 	if(ltv_ins(ctlr, WTyp_HasCrypt) == 0)
9183ff48bf5SDavid du Colombier 		PRINTSTR("WEP: not supported\n");
9193ff48bf5SDavid du Colombier 	else {
9203ff48bf5SDavid du Colombier 		if(ltv_ins(ctlr, WTyp_Crypt) == 0)
9213ff48bf5SDavid du Colombier 			PRINTSTR("WEP: disabled\n");
9223ff48bf5SDavid du Colombier 		else{
9233ff48bf5SDavid du Colombier 			PRINTSTR("WEP: enabled\n");
9243ff48bf5SDavid du Colombier 			k = ((ctlr->xclear)? "excluded": "included");
9253ff48bf5SDavid du Colombier 			PRINTSTAT("Clear packets: %s\n", k);
9263ff48bf5SDavid du Colombier 			txid = ltv_ins(ctlr, WTyp_TxKey);
9273ff48bf5SDavid du Colombier 			PRINTSTAT("Transmit key id: %d\n", txid);
9283ff48bf5SDavid du Colombier 		}
9293ff48bf5SDavid du Colombier 	}
9303ff48bf5SDavid du Colombier 	iunlock(ctlr);
9313ff48bf5SDavid du Colombier 
9323ff48bf5SDavid du Colombier 	PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
9333ff48bf5SDavid du Colombier 	PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
9343ff48bf5SDavid du Colombier 	PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags);
9353ff48bf5SDavid du Colombier 	PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes);
9363ff48bf5SDavid du Colombier 	PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes);
9373ff48bf5SDavid du Colombier 	PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred);
9383ff48bf5SDavid du Colombier 	PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries);
9393ff48bf5SDavid du Colombier 	PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries);
9403ff48bf5SDavid du Colombier 	PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit);
9413ff48bf5SDavid du Colombier 	PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards);
9423ff48bf5SDavid du Colombier 	PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes);
9433ff48bf5SDavid du Colombier 	PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes);
9443ff48bf5SDavid du Colombier 	PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags);
9453ff48bf5SDavid du Colombier 	PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes);
9463ff48bf5SDavid du Colombier 	PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes);
9473ff48bf5SDavid du Colombier 	PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr);
9483ff48bf5SDavid du Colombier 	PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf);
9493ff48bf5SDavid du Colombier 	PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa);
9503ff48bf5SDavid du Colombier 	PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt);
9513ff48bf5SDavid du Colombier 	PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag);
9523ff48bf5SDavid du Colombier 	PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag);
9533ff48bf5SDavid du Colombier 	USED(l);
9543ff48bf5SDavid du Colombier 	n = readstr(offset, a, n, p);
9553ff48bf5SDavid du Colombier 	free(p);
9563ff48bf5SDavid du Colombier 	return n;
9573ff48bf5SDavid du Colombier }
9583ff48bf5SDavid du Colombier #undef PRINTSTR
9593ff48bf5SDavid du Colombier #undef PRINTSTAT
9603ff48bf5SDavid du Colombier 
9617c881178SDavid du Colombier static int
parsekey(WKey * key,char * a)9627c881178SDavid du Colombier parsekey(WKey* key, char* a)
9637c881178SDavid du Colombier {
9647c881178SDavid du Colombier 	int i, k, len, n;
9657c881178SDavid du Colombier 	char buf[WMaxKeyLen];
9667c881178SDavid du Colombier 
9677c881178SDavid du Colombier 	len = strlen(a);
9687c881178SDavid du Colombier 	if(len == WMinKeyLen || len == WMaxKeyLen){
9697c881178SDavid du Colombier 		memset(key->dat, 0, sizeof(key->dat));
9707c881178SDavid du Colombier 		memmove(key->dat, a, len);
9717c881178SDavid du Colombier 		key->len = len;
9727c881178SDavid du Colombier 
9737c881178SDavid du Colombier 		return 0;
9747c881178SDavid du Colombier 	}
9757c881178SDavid du Colombier 	else if(len == WMinKeyLen*2 || len == WMaxKeyLen*2){
9767c881178SDavid du Colombier 		k = 0;
9777c881178SDavid du Colombier 		for(i = 0; i < len; i++){
9787c881178SDavid du Colombier 			if(*a >= '0' && *a <= '9')
9797c881178SDavid du Colombier 				n = *a++ - '0';
9807c881178SDavid du Colombier 			else if(*a >= 'a' && *a <= 'f')
9817c881178SDavid du Colombier 				n = *a++ - 'a' + 10;
9827c881178SDavid du Colombier 			else if(*a >= 'A' && *a <= 'F')
9837c881178SDavid du Colombier 				n = *a++ - 'A' + 10;
9847c881178SDavid du Colombier 			else
9857c881178SDavid du Colombier 				return -1;
9867c881178SDavid du Colombier 
9877c881178SDavid du Colombier 			if(i & 1){
9887c881178SDavid du Colombier 				buf[k] |= n;
9897c881178SDavid du Colombier 				k++;
9907c881178SDavid du Colombier 			}
9917c881178SDavid du Colombier 			else
9927c881178SDavid du Colombier 				buf[k] = n<<4;
9937c881178SDavid du Colombier 		}
9947c881178SDavid du Colombier 
9957c881178SDavid du Colombier 		memset(key->dat, 0, sizeof(key->dat));
9967c881178SDavid du Colombier 		memmove(key->dat, buf, k);
9977c881178SDavid du Colombier 		key->len = k;
9987c881178SDavid du Colombier 
9997c881178SDavid du Colombier 		return 0;
10007c881178SDavid du Colombier 	}
10017c881178SDavid du Colombier 
10027c881178SDavid du Colombier 	return -1;
10037c881178SDavid du Colombier }
10047c881178SDavid du Colombier 
10053ff48bf5SDavid du Colombier int
w_option(Ctlr * ctlr,char * buf,long n)10063ff48bf5SDavid du Colombier w_option(Ctlr* ctlr, char* buf, long n)
10073ff48bf5SDavid du Colombier {
10083ff48bf5SDavid du Colombier 	char *p;
10093ff48bf5SDavid du Colombier 	int i, r;
10103ff48bf5SDavid du Colombier 	Cmdbuf *cb;
10113ff48bf5SDavid du Colombier 
10123ff48bf5SDavid du Colombier 	r = 0;
10133ff48bf5SDavid du Colombier 
10143ff48bf5SDavid du Colombier 	cb = parsecmd(buf, n);
10153ff48bf5SDavid du Colombier 	if(cb->nf < 2)
10163ff48bf5SDavid du Colombier 		r = -1;
10173ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "essid") == 0){
10183ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1],"default") == 0)
10193ff48bf5SDavid du Colombier 			p = "";
10203ff48bf5SDavid du Colombier 		else
10213ff48bf5SDavid du Colombier 			p = cb->f[1];
102215763c87SDavid du Colombier 		if(ctlr->ptype == WPTypeAdHoc){
10233ff48bf5SDavid du Colombier 			memset(ctlr->netname, 0, sizeof(ctlr->netname));
10243ff48bf5SDavid du Colombier 			strncpy(ctlr->netname, p, WNameLen);
10253ff48bf5SDavid du Colombier 		}
10263ff48bf5SDavid du Colombier 		else{
10273ff48bf5SDavid du Colombier 			memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
10283ff48bf5SDavid du Colombier 			strncpy(ctlr->wantname, p, WNameLen);
10293ff48bf5SDavid du Colombier 		}
10303ff48bf5SDavid du Colombier 	}
10313ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "station") == 0){
10323ff48bf5SDavid du Colombier 		memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
10333ff48bf5SDavid du Colombier 		strncpy(ctlr->nodename, cb->f[1], WNameLen);
10343ff48bf5SDavid du Colombier 	}
10353ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "channel") == 0){
10363ff48bf5SDavid du Colombier 		if((i = atoi(cb->f[1])) >= 1 && i <= 16)
10373ff48bf5SDavid du Colombier 			ctlr->chan = i;
10383ff48bf5SDavid du Colombier 		else
10393ff48bf5SDavid du Colombier 			r = -1;
10403ff48bf5SDavid du Colombier 	}
10413ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "mode") == 0){
10423ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "managed") == 0)
10433ff48bf5SDavid du Colombier 			ctlr->ptype = WPTypeManaged;
10443ff48bf5SDavid du Colombier 		else if(cistrcmp(cb->f[1], "wds") == 0)
10453ff48bf5SDavid du Colombier 			ctlr->ptype = WPTypeWDS;
10463ff48bf5SDavid du Colombier 		else if(cistrcmp(cb->f[1], "adhoc") == 0)
10473ff48bf5SDavid du Colombier 			ctlr->ptype = WPTypeAdHoc;
104807a38badSDavid du Colombier 		else if((i = atoi(cb->f[1])) >= 0 && i <= 3)
10493ff48bf5SDavid du Colombier 			ctlr->ptype = i;
10503ff48bf5SDavid du Colombier 		else
10513ff48bf5SDavid du Colombier 			r = -1;
10523ff48bf5SDavid du Colombier 	}
10533ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "ibss") == 0){
10543ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "on") == 0)
10553ff48bf5SDavid du Colombier 			ctlr->createibss = 1;
10563ff48bf5SDavid du Colombier 		else
10573ff48bf5SDavid du Colombier 			ctlr->createibss = 0;
10583ff48bf5SDavid du Colombier 	}
10593ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "crypt") == 0){
10603ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "off") == 0)
10613ff48bf5SDavid du Colombier 			ctlr->crypt = 0;
10623ff48bf5SDavid du Colombier 		else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt)
10633ff48bf5SDavid du Colombier 			ctlr->crypt = 1;
10643ff48bf5SDavid du Colombier 		else
10653ff48bf5SDavid du Colombier 			r = -1;
10663ff48bf5SDavid du Colombier 	}
10673ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "clear") == 0){
10683ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "on") == 0)
10693ff48bf5SDavid du Colombier 			ctlr->xclear = 0;
10703ff48bf5SDavid du Colombier 		else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt)
10713ff48bf5SDavid du Colombier 			ctlr->xclear = 1;
10723ff48bf5SDavid du Colombier 		else
10733ff48bf5SDavid du Colombier 			r = -1;
10743ff48bf5SDavid du Colombier 	}
10753ff48bf5SDavid du Colombier 	else if(cistrncmp(cb->f[0], "key", 3) == 0){
10763ff48bf5SDavid du Colombier 		if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){
10773ff48bf5SDavid du Colombier 			ctlr->txkey = i-1;
10787c881178SDavid du Colombier 			if(parsekey(&ctlr->keys.keys[ctlr->txkey], cb->f[1]))
10797c881178SDavid du Colombier 				r = -1;
10803ff48bf5SDavid du Colombier 		}
10813ff48bf5SDavid du Colombier 		else
10823ff48bf5SDavid du Colombier 			r = -1;
10833ff48bf5SDavid du Colombier 	}
10843ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "txkey") == 0){
10853ff48bf5SDavid du Colombier 		if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys)
10863ff48bf5SDavid du Colombier 			ctlr->txkey = i-1;
10873ff48bf5SDavid du Colombier 		else
10883ff48bf5SDavid du Colombier 			r = -1;
10893ff48bf5SDavid du Colombier 	}
10903ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "pm") == 0){
10913ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "off") == 0)
10923ff48bf5SDavid du Colombier 			ctlr->pmena = 0;
10933ff48bf5SDavid du Colombier 		else if(cistrcmp(cb->f[1], "on") == 0){
10943ff48bf5SDavid du Colombier 			ctlr->pmena = 1;
10953ff48bf5SDavid du Colombier 			if(cb->nf == 3){
10963ff48bf5SDavid du Colombier 				i = atoi(cb->f[2]);
10973ff48bf5SDavid du Colombier 				// check range here? what are the units?
10983ff48bf5SDavid du Colombier 				ctlr->pmwait = i;
10993ff48bf5SDavid du Colombier 			}
11003ff48bf5SDavid du Colombier 		}
11013ff48bf5SDavid du Colombier 		else
11023ff48bf5SDavid du Colombier 			r = -1;
11033ff48bf5SDavid du Colombier 	}
11043ff48bf5SDavid du Colombier 	else
11053ff48bf5SDavid du Colombier 		r = -2;
11063ff48bf5SDavid du Colombier 	free(cb);
11073ff48bf5SDavid du Colombier 
11083ff48bf5SDavid du Colombier 	return r;
11093ff48bf5SDavid du Colombier }
11103ff48bf5SDavid du Colombier 
11113ff48bf5SDavid du Colombier long
w_ctl(Ether * ether,void * buf,long n)11123ff48bf5SDavid du Colombier w_ctl(Ether* ether, void* buf, long n)
11133ff48bf5SDavid du Colombier {
11143ff48bf5SDavid du Colombier 	Ctlr *ctlr;
11153ff48bf5SDavid du Colombier 
11163ff48bf5SDavid du Colombier 	if((ctlr = ether->ctlr) == nil)
11173ff48bf5SDavid du Colombier 		error(Enonexist);
11183ff48bf5SDavid du Colombier 	if((ctlr->state & Attached) == 0)
11193ff48bf5SDavid du Colombier 		error(Eshutdown);
11203ff48bf5SDavid du Colombier 
11213ff48bf5SDavid du Colombier 	ilock(ctlr);
11223ff48bf5SDavid du Colombier 	if(w_option(ctlr, buf, n)){
11233ff48bf5SDavid du Colombier 		iunlock(ctlr);
11243ff48bf5SDavid du Colombier 		error(Ebadctl);
11253ff48bf5SDavid du Colombier 	}
11263ff48bf5SDavid du Colombier 	if(ctlr->txbusy)
11273ff48bf5SDavid du Colombier 		w_txdone(ctlr, WTxErrEv);
11283ff48bf5SDavid du Colombier 	w_enable(ether);
11293ff48bf5SDavid du Colombier 	w_txstart(ether);
11303ff48bf5SDavid du Colombier 	iunlock(ctlr);
11313ff48bf5SDavid du Colombier 
11323ff48bf5SDavid du Colombier 	return n;
11333ff48bf5SDavid du Colombier }
11343ff48bf5SDavid du Colombier 
11353ff48bf5SDavid du Colombier void
w_transmit(Ether * ether)11363ff48bf5SDavid du Colombier w_transmit(Ether* ether)
11373ff48bf5SDavid du Colombier {
11383ff48bf5SDavid du Colombier 	Ctlr* ctlr = ether->ctlr;
11393ff48bf5SDavid du Colombier 
11403ff48bf5SDavid du Colombier 	if(ctlr == 0)
11413ff48bf5SDavid du Colombier 		return;
11423ff48bf5SDavid du Colombier 
11433ff48bf5SDavid du Colombier 	ilock(ctlr);
11443ff48bf5SDavid du Colombier 	ctlr->ntxrq++;
11453ff48bf5SDavid du Colombier 	w_txstart(ether);
11463ff48bf5SDavid du Colombier 	iunlock(ctlr);
11473ff48bf5SDavid du Colombier }
11483ff48bf5SDavid du Colombier 
11493ff48bf5SDavid du Colombier void
w_promiscuous(void * arg,int on)11503ff48bf5SDavid du Colombier w_promiscuous(void* arg, int on)
11513ff48bf5SDavid du Colombier {
11523ff48bf5SDavid du Colombier 	Ether* ether = (Ether*)arg;
11533ff48bf5SDavid du Colombier 	Ctlr* ctlr = ether->ctlr;
11543ff48bf5SDavid du Colombier 
11553ff48bf5SDavid du Colombier 	if(ctlr == nil)
11563ff48bf5SDavid du Colombier 		error("card not found");
11573ff48bf5SDavid du Colombier 	if((ctlr->state & Attached) == 0)
11583ff48bf5SDavid du Colombier 		error("card not attached");
11593ff48bf5SDavid du Colombier 	ilock(ctlr);
11603ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_Prom, (on?1:0));
11613ff48bf5SDavid du Colombier 	iunlock(ctlr);
11623ff48bf5SDavid du Colombier }
11633ff48bf5SDavid du Colombier 
11643ff48bf5SDavid du Colombier void
w_interrupt(Ureg *,void * arg)11653ff48bf5SDavid du Colombier w_interrupt(Ureg* ,void* arg)
11663ff48bf5SDavid du Colombier {
11673ff48bf5SDavid du Colombier 	Ether* ether = (Ether*) arg;
11683ff48bf5SDavid du Colombier 	Ctlr* ctlr = (Ctlr*) ether->ctlr;
11693ff48bf5SDavid du Colombier 
11703ff48bf5SDavid du Colombier 	if(ctlr == 0)
11713ff48bf5SDavid du Colombier 		return;
11723ff48bf5SDavid du Colombier 	ilock(ctlr);
11733ff48bf5SDavid du Colombier 	ctlr->nints++;
11743ff48bf5SDavid du Colombier 	w_intr(ether);
11753ff48bf5SDavid du Colombier 	iunlock(ctlr);
11763ff48bf5SDavid du Colombier }
11773ff48bf5SDavid du Colombier 
117898bee55eSDavid du Colombier static void
w_shutdown(Ether * ether)117998bee55eSDavid du Colombier w_shutdown(Ether* ether)
118098bee55eSDavid du Colombier {
118198bee55eSDavid du Colombier 	Ctlr *ctlr;
118298bee55eSDavid du Colombier 
118398bee55eSDavid du Colombier 	ctlr = ether->ctlr;
118498bee55eSDavid du Colombier 	w_intdis(ctlr);
118598bee55eSDavid du Colombier 	if(w_cmd(ctlr,WCmdIni,0))
118698bee55eSDavid du Colombier 		iprint("#l%d: init failed\n", ether->ctlrno);
118798bee55eSDavid du Colombier 	w_intdis(ctlr);
118898bee55eSDavid du Colombier }
118998bee55eSDavid du Colombier 
11903ff48bf5SDavid du Colombier int
wavelanreset(Ether * ether,Ctlr * ctlr)11913ff48bf5SDavid du Colombier wavelanreset(Ether* ether, Ctlr *ctlr)
11923ff48bf5SDavid du Colombier {
11933ff48bf5SDavid du Colombier 	Wltv ltv;
11943ff48bf5SDavid du Colombier 
11953ff48bf5SDavid du Colombier 	iprint("wavelanreset, iob 0x%ux\n", ctlr->iob);
11963ff48bf5SDavid du Colombier 	w_intdis(ctlr);
11973ff48bf5SDavid du Colombier 	if(w_cmd(ctlr,WCmdIni,0)){
11983ff48bf5SDavid du Colombier 		iprint("#l%d: init failed\n", ether->ctlrno);
11993ff48bf5SDavid du Colombier 		return -1;
12003ff48bf5SDavid du Colombier 	}
12013ff48bf5SDavid du Colombier 	w_intdis(ctlr);
12023ff48bf5SDavid du Colombier 	ltv_outs(ctlr, WTyp_Tick, 8);
12033ff48bf5SDavid du Colombier 
12043ff48bf5SDavid du Colombier 	ctlr->chan = 0;
12053ff48bf5SDavid du Colombier 	ctlr->ptype = WDfltPType;
12063ff48bf5SDavid du Colombier 	ctlr->txkey = 0;
12073ff48bf5SDavid du Colombier 	ctlr->createibss = 0;
12083ff48bf5SDavid du Colombier 	ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1;
12093ff48bf5SDavid du Colombier 	ctlr->keys.type = WTyp_Keys;
12103ff48bf5SDavid du Colombier 	if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt))
12113ff48bf5SDavid du Colombier 		ctlr->crypt = 1;
12123ff48bf5SDavid du Colombier 	*ctlr->netname = *ctlr->wantname = 0;
12133ff48bf5SDavid du Colombier 	strcpy(ctlr->nodename, "Plan 9 STA");
12143ff48bf5SDavid du Colombier 
12153ff48bf5SDavid du Colombier 	ctlr->netname[WNameLen-1] = 0;
12163ff48bf5SDavid du Colombier 	ctlr->wantname[WNameLen-1] = 0;
12173ff48bf5SDavid du Colombier 	ctlr->nodename[WNameLen-1] =0;
12183ff48bf5SDavid du Colombier 
12193ff48bf5SDavid du Colombier 	ltv.type = WTyp_Mac;
12203ff48bf5SDavid du Colombier 	ltv.len	= 4;
12213ff48bf5SDavid du Colombier 	if(w_inltv(ctlr, &ltv)){
12223ff48bf5SDavid du Colombier 		iprint("#l%d: unable to read mac addr\n",
12233ff48bf5SDavid du Colombier 			ether->ctlrno);
12243ff48bf5SDavid du Colombier 		return -1;
12253ff48bf5SDavid du Colombier 	}
12263ff48bf5SDavid du Colombier 	memmove(ether->ea, ltv.addr, Eaddrlen);
12273ff48bf5SDavid du Colombier 
12283ff48bf5SDavid du Colombier 	if(ctlr->chan == 0)
12293ff48bf5SDavid du Colombier 		ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
12303ff48bf5SDavid du Colombier 	ctlr->apdensity = WDfltApDens;
12313ff48bf5SDavid du Colombier 	ctlr->rtsthres = WDfltRtsThres;
12323ff48bf5SDavid du Colombier 	ctlr->txrate = WDfltTxRate;
12333ff48bf5SDavid du Colombier 	ctlr->maxlen = WMaxLen;
12343ff48bf5SDavid du Colombier 	ctlr->pmena = 0;
12353ff48bf5SDavid du Colombier 	ctlr->pmwait = 100;
12363ff48bf5SDavid du Colombier 	ctlr->signal = 1;
12373ff48bf5SDavid du Colombier 	ctlr->noise = 1;
12383ff48bf5SDavid du Colombier 	ctlr->state |= Power;
12393ff48bf5SDavid du Colombier 
12403ff48bf5SDavid du Colombier 	// free old Ctlr struct if resetting after suspend
12413ff48bf5SDavid du Colombier 	if(ether->ctlr && ether->ctlr != ctlr)
12423ff48bf5SDavid du Colombier 		free(ether->ctlr);
12433ff48bf5SDavid du Colombier 
12443ff48bf5SDavid du Colombier 	// link to ether
12453ff48bf5SDavid du Colombier 	ether->ctlr = ctlr;
12463ff48bf5SDavid du Colombier 	ether->mbps = 10;
12473ff48bf5SDavid du Colombier 	ether->attach = w_attach;
12483ff48bf5SDavid du Colombier 	ether->detach = w_detach;
12493ff48bf5SDavid du Colombier 	ether->interrupt = w_interrupt;
12503ff48bf5SDavid du Colombier 	ether->transmit = w_transmit;
12513ff48bf5SDavid du Colombier 	ether->ifstat = w_ifstat;
12523ff48bf5SDavid du Colombier 	ether->ctl = w_ctl;
12533ff48bf5SDavid du Colombier 	ether->power = w_power;
12543ff48bf5SDavid du Colombier 	ether->promiscuous = w_promiscuous;
12553ff48bf5SDavid du Colombier 	ether->multicast = w_multicast;
125698bee55eSDavid du Colombier 	ether->shutdown = w_shutdown;
125715763c87SDavid du Colombier 	ether->scanbs = w_scanbs;
12583ff48bf5SDavid du Colombier 	ether->arg = ether;
12593ff48bf5SDavid du Colombier 
1260fb7f0c93SDavid du Colombier 	DEBUG("#l%d: irq %d port %lx type %s",
12613ff48bf5SDavid du Colombier 		ether->ctlrno, ether->irq, ether->port,	ether->type);
12626063170eSDavid du Colombier 	DEBUG(" %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux\n",
12633ff48bf5SDavid du Colombier 		ether->ea[0], ether->ea[1], ether->ea[2],
12643ff48bf5SDavid du Colombier 		ether->ea[3], ether->ea[4], ether->ea[5]);
12653ff48bf5SDavid du Colombier 
12663ff48bf5SDavid du Colombier 	return 0;
12673ff48bf5SDavid du Colombier }
12683ff48bf5SDavid du Colombier 
12693ff48bf5SDavid du Colombier char* wavenames[] = {
12703ff48bf5SDavid du Colombier 	"WaveLAN/IEEE",
12713ff48bf5SDavid du Colombier 	"TrueMobile 1150",
12723ff48bf5SDavid du Colombier 	"Instant Wireless ; Network PC CARD",
1273d9306527SDavid du Colombier 	"Instant Wireless Network PC Card",
12743ff48bf5SDavid du Colombier 	"Avaya Wireless PC Card",
12753ff48bf5SDavid du Colombier 	"AirLancer MC-11",
1276e059317eSDavid du Colombier 	"INTERSIL;HFA384x/IEEE;Version 01.02;",
12773ff48bf5SDavid du Colombier 	nil,
12783ff48bf5SDavid du Colombier };
1279