plan 9 kernel history: overview | file list | diff list

2003/0407/bitsy/wavelan.c (diff list | history)

2003/0407/sys/src/9/bitsy/wavelan.c:1,12172003/0508/sys/src/9/bitsy/wavelan.c:1,1228 (short | long | prev)
2002/1112    
/* 
2003/0311    
	Lucent Wavelan IEEE 802.11 pcmcia. 
2002/1112    
	There is almost no documentation for the card. 
	the driver is done using both the FreeBSD, Linux and 
	original Plan 9 drivers as `documentation'. 
 
	Has been used with the card plugged in during all up time. 
	no cards removals/insertions yet. 
 
	For known BUGS see the comments below. Besides, 
	the driver keeps interrupts disabled for just too 
	long. When it gets robust, locks should be revisited. 
 
	BUGS: check endian, alignment and mem/io issues; 
	      multicast; 
	      receive watchdog interrupts. 
	TODO: automatic power management; 
	      improve locking. 
 */ 
#include "u.h" 
#include "../port/lib.h" 
#include "mem.h" 
#include "dat.h" 
#include "fns.h" 
#include "io.h" 
#include "../port/error.h" 
#include "../port/netif.h" 
#include "etherif.h" 
#include "wavelan.h" 
 
2003/0311    
enum 
{ 
	MSperTick=	50,	/* ms between ticks of kproc */ 
}; 
 
2002/1112    
/* 
 * When we're using a PCI device and memory-mapped I/O,  
 * the registers are spaced out as though each takes 32 bits, 
 * even though they are only 16-bit registers.  Thus,  
 * ctlr->mmb[reg] is the right way to access register reg, 
 * even though a priori you'd expect to use ctlr->mmb[reg/2]. 
 */ 
void 
csr_outs(Ctlr *ctlr, int reg, ushort arg) 
{ 
	if(ctlr->mmb) 
		ctlr->mmb[reg] = arg; 
	else 
		outs(ctlr->iob+reg, arg); 
} 
 
ushort 
csr_ins(Ctlr *ctlr, int reg) 
{ 
	if(ctlr->mmb) 
		return ctlr->mmb[reg]; 
	else 
		return ins(ctlr->iob+reg); 
} 
 
static void 
csr_ack(Ctlr *ctlr, int ev) 
{ 
	csr_outs(ctlr, WR_EvAck, ev); 
} 
 
static void 
csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat) 
{ 
	ushort *rp, *wp; 
 
	if(ctlr->mmb){ 
		rp = &ctlr->mmb[reg]; 
		wp = dat; 
		while(ndat-- > 0) 
			*wp++ = *rp; 
	}else 
		inss(ctlr->iob+reg, dat, ndat); 
} 
 
static void 
csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat) 
{ 
	ushort *rp, *wp; 
 
	if(ctlr->mmb){ 
		rp = dat; 
		wp = &ctlr->mmb[reg]; 
		while(ndat-- > 0) 
			*wp = *rp++; 
	}else 
		outss(ctlr->iob+reg, dat, ndat); 
} 
 
// w_... routines do not ilock the Ctlr and should 
// be called locked. 
 
void 
w_intdis(Ctlr* ctlr) 
{ 
	csr_outs(ctlr, WR_IntEna, 0); 
	csr_ack(ctlr, 0xffff); 
} 
 
static void 
w_intena(Ctlr* ctlr) 
{ 
	csr_outs(ctlr, WR_IntEna, WEvs); 
} 
 
int 
w_cmd(Ctlr *ctlr, ushort cmd, ushort arg) 
{ 
	int i, rc; 
 
	for(i=0; i<WTmOut; i++) 
		if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0) 
			break; 
	if(i==WTmOut){ 
		print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd)); 
		return -1; 
	} 
 
	csr_outs(ctlr, WR_Parm0, arg); 
	csr_outs(ctlr, WR_Cmd, cmd); 
 
	for(i=0; i<WTmOut; i++) 
		if(csr_ins(ctlr, WR_EvSts)&WCmdEv) 
			break; 
	if(i==WTmOut){ 
		/* 
		 * WCmdIni can take a really long time. 
		 */ 
		enum { IniTmOut = 2000 }; 
		for(i=0; i<IniTmOut; i++){ 
			if(csr_ins(ctlr, WR_EvSts)&WCmdEv) 
				break; 
			microdelay(100); 
		} 
		if(i < IniTmOut) 
			if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i); 
		if(i == IniTmOut){ 
			print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts)); 
			return -1; 
		} 
	} 
	rc = csr_ins(ctlr, WR_Sts); 
	csr_ack(ctlr, WCmdEv); 
 
	if((rc&WCmdMsk) != (cmd&WCmdMsk)){ 
		print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); 
		return -1; 
	} 
	if(rc&WResSts){ 
		/* 
		 * Don't print; this happens on every WCmdAccWr for some reason. 
		 */ 
		if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); 
		return -1; 
	} 
	return 0; 
} 
 
static int 
w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan) 
{ 
	int i, rc; 
	static ushort sel[] = { WR_Sel0, WR_Sel1 }; 
	static ushort off[] = { WR_Off0, WR_Off1 }; 
 
	if(chan != 0 && chan != 1) 
		panic("wavelan: bad chan\n"); 
	csr_outs(ctlr, sel[chan], id); 
	csr_outs(ctlr, off[chan], offset); 
	for (i=0; i<WTmOut; i++){ 
		rc = csr_ins(ctlr, off[chan]); 
		if((rc & (WBusyOff|WErrOff)) == 0) 
			return 0; 
	} 
	return -1; 
} 
 
int 
w_inltv(Ctlr* ctlr, Wltv* ltv) 
{ 
	int len; 
	ushort code; 
 
	if(w_cmd(ctlr, WCmdAccRd, ltv->type)){ 
		DEBUG("wavelan: access read failed\n"); 
		return -1; 
	} 
	if(w_seek(ctlr,ltv->type,0,1)){ 
		DEBUG("wavelan: seek failed\n"); 
		return -1; 
	} 
	len = csr_ins(ctlr, WR_Data1); 
	if(len > ltv->len) 
		return -1; 
	ltv->len = len; 
	if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){ 
		USED(code); 
		DEBUG("wavelan: type %x != code %x\n",ltv->type,code); 
		return -1; 
	} 
	if(ltv->len > 0) 
		csr_inss(ctlr, WR_Data1, <v->val, ltv->len-1); 
 
	return 0; 
} 
 
static void 
w_outltv(Ctlr* ctlr, Wltv* ltv) 
{ 
	if(w_seek(ctlr,ltv->type, 0, 1)) 
		return; 
	csr_outss(ctlr, WR_Data1, ltv, ltv->len+1); 
	w_cmd(ctlr, WCmdAccWr, ltv->type); 
} 
 
void 
ltv_outs(Ctlr* ctlr, int type, ushort val) 
{ 
	Wltv ltv; 
 
	ltv.len = 2; 
	ltv.type = type; 
	ltv.val = val; 
	w_outltv(ctlr, <v); 
} 
 
int 
ltv_ins(Ctlr* ctlr, int type) 
{ 
	Wltv ltv; 
 
	ltv.len = 2; 
	ltv.type = type; 
	ltv.val = 0; 
	if(w_inltv(ctlr, <v)) 
		return -1; 
	return ltv.val; 
} 
 
static void 
ltv_outstr(Ctlr* ctlr, int type, char* val) 
{ 
	Wltv ltv; 
	int len; 
 
	len = strlen(val); 
	if(len > sizeof(ltv.s)) 
		len = sizeof(ltv.s); 
	memset(<v, 0, sizeof(ltv)); 
	ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2; 
	ltv.type = type; 
 
//	This should be ltv.slen = len; according to Axel Belinfante 
	ltv.slen = len;	 
 
	strncpy(ltv.s, val, len); 
	w_outltv(ctlr, <v); 
} 
 
static char Unkname[] = "who knows"; 
static char Nilname[] = "card does not tell"; 
 
static char* 
ltv_inname(Ctlr* ctlr, int type) 
{ 
	static Wltv ltv; 
	int len; 
 
	memset(<v,0,sizeof(ltv)); 
	ltv.len = WNameLen/2+2; 
	ltv.type = type; 
	if(w_inltv(ctlr, <v)) 
		return Unkname; 
	len = ltv.slen; 
	if(len == 0 || ltv.s[0] == 0) 
		return Nilname; 
	if(len >= sizeof ltv.s) 
		len = sizeof ltv.s - 1; 
	ltv.s[len] = '\0'; 
	return ltv.s; 
} 
 
static int 
w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) 
{ 
	if(w_seek(ctlr, type, off, 1)){ 
		DEBUG("wavelan: w_read: seek failed"); 
		return 0; 
	} 
	csr_inss(ctlr, WR_Data1, buf, len/2); 
 
	return len; 
} 
 
static int 
w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) 
{ 
	int tries; 
 
	for (tries=0; tries < WTmOut; tries++){ 
		if(w_seek(ctlr, type, off, 0)){ 
			DEBUG("wavelan: w_write: seek failed\n"); 
			return 0; 
		} 
 
		csr_outss(ctlr, WR_Data0, buf, len/2); 
 
		csr_outs(ctlr, WR_Data0, 0xdead); 
		csr_outs(ctlr, WR_Data0, 0xbeef); 
		if(w_seek(ctlr, type, off + len, 0)){ 
			DEBUG("wavelan: write seek failed\n"); 
			return 0; 
		} 
		if(csr_ins(ctlr, WR_Data0) == 0xdead) 
		if(csr_ins(ctlr, WR_Data0) == 0xbeef) 
			return len; 
		DEBUG("wavelan: Hermes bug byte.\n"); 
		return 0; 
	} 
	DEBUG("wavelan: tx timeout\n"); 
	return 0; 
} 
 
static int 
w_alloc(Ctlr* ctlr, int len) 
{ 
	int rc; 
	int i,j; 
 
	if(w_cmd(ctlr, WCmdMalloc, len)==0) 
		for (i = 0; i<WTmOut; i++) 
			if(csr_ins(ctlr, WR_EvSts) & WAllocEv){ 
				csr_ack(ctlr, WAllocEv); 
				rc=csr_ins(ctlr, WR_Alloc); 
				if(w_seek(ctlr, rc, 0, 0)) 
					return -1; 
				len = len/2; 
				for (j=0; j<len; j++) 
					csr_outs(ctlr, WR_Data0, 0); 
				return rc; 
			} 
	return -1; 
} 
 
static int 
w_enable(Ether* ether) 
{ 
	Wltv ltv; 
	Ctlr* ctlr = (Ctlr*) ether->ctlr; 
 
	if(!ctlr) 
		return -1; 
 
	w_intdis(ctlr); 
	w_cmd(ctlr, WCmdDis, 0); 
	w_intdis(ctlr); 
	if(w_cmd(ctlr, WCmdIni, 0)) 
		return -1; 
	w_intdis(ctlr); 
 
	ltv_outs(ctlr, WTyp_Tick, 8); 
	ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen); 
	ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype); 
 	ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss); 
	ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres); 
	ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); 
	ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); 
	ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); 
	ltv_outs(ctlr, WTyp_PM, ctlr->pmena); 
	if(*ctlr->netname) 
		ltv_outstr(ctlr, WTyp_NetName, ctlr->netname); 
	if(*ctlr->wantname) 
		ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname); 
	ltv_outs(ctlr, WTyp_Chan, ctlr->chan); 
	if(*ctlr->nodename) 
		ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename); 
	ltv.len = 4; 
	ltv.type = WTyp_Mac; 
	memmove(ltv.addr, ether->ea, Eaddrlen); 
	w_outltv(ctlr, <v); 
 
	ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); 
 
2003/0311    
	if(ctlr->hascrypt && ctlr->crypt){ 
2002/1112    
		ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt); 
		ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey); 
		w_outltv(ctlr, &ctlr->keys); 
		ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); 
	} 
 
	// BUG: set multicast addresses 
 
	if(w_cmd(ctlr, WCmdEna, 0)){ 
		DEBUG("wavelan: Enable failed"); 
		return -1; 
	} 
	ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); 
	ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); 
	if(ctlr->txdid == -1 || ctlr->txmid == -1) 
		DEBUG("wavelan: alloc failed"); 
	ctlr->txbusy = 0; 
	w_intena(ctlr); 
	return 0; 
} 
 
static void 
w_rxdone(Ether* ether) 
{ 
	Ctlr* ctlr = (Ctlr*) ether->ctlr; 
	int len, sp; 
	WFrame f; 
	Block* bp=0; 
	Etherpkt* ep; 
 
	sp = csr_ins(ctlr, WR_RXId); 
	len = w_read(ctlr, sp, 0, &f, sizeof(f)); 
	if(len == 0){ 
		DEBUG("wavelan: read frame error\n"); 
		goto rxerror; 
	} 
	if(f.sts&WF_Err){ 
		goto rxerror; 
	} 
	switch(f.sts){ 
	case WF_1042: 
	case WF_Tunnel: 
	case WF_WMP: 
		len = f.dlen + WSnapHdrLen; 
		bp = iallocb(ETHERHDRSIZE + len + 2); 
		if(!bp) 
			goto rxerror; 
		ep = (Etherpkt*) bp->wp; 
		memmove(ep->d, f.addr1, Eaddrlen); 
		memmove(ep->s, f.addr2, Eaddrlen); 
		memmove(ep->type,&f.type,2); 
		bp->wp += ETHERHDRSIZE; 
		if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){ 
			DEBUG("wavelan: read 802.11 error\n"); 
			goto rxerror; 
		} 
		bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen); 
		break; 
	default: 
		len = ETHERHDRSIZE + f.dlen + 2; 
		bp = iallocb(len); 
		if(!bp) 
			goto rxerror; 
		if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){ 
			DEBUG("wavelan: read 800.3 error\n"); 
			goto rxerror; 
		} 
		bp->wp += len; 
	} 
 
	ctlr->nrx++; 
	etheriq(ether,bp,1); 
	ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16; 
	ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16; 
	return; 
 
rxerror: 
	freeb(bp); 
	ctlr->nrxerr++; 
} 
 
static void 
w_txstart(Ether* ether) 
{ 
	Etherpkt *pkt; 
	Ctlr *ctlr; 
	Block *bp; 
	int len, off; 
 
	if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy) 
		return; 
 
	if((bp = qget(ether->oq)) == nil) 
		return; 
	pkt = (Etherpkt*)bp->rp; 
 
	// 
	// If the packet header type field is > 1500 it is an IP or 
	// ARP datagram, otherwise it is an 802.3 packet. See RFC1042. 
	// 
	memset(&ctlr->txf, 0, sizeof(ctlr->txf)); 
	if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){ 
		ctlr->txf.framectl = WF_Data; 
		memmove(ctlr->txf.addr1, pkt->d, Eaddrlen); 
		memmove(ctlr->txf.addr2, pkt->s, Eaddrlen); 
		memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen); 
		memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen); 
		memmove(&ctlr->txf.type, pkt->type, 2); 
		bp->rp += ETHERHDRSIZE; 
		len = BLEN(bp); 
		off = WF_802_11_Off; 
		ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen; 
		hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); 
		hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); 
		hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen); 
	} 
	else{ 
		len = BLEN(bp); 
		off = WF_802_3_Off; 
		ctlr->txf.dlen = len; 
	} 
	w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf)); 
	w_write(ctlr, ctlr->txdid, off, bp->rp, len+2); 
 
	if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){ 
		DEBUG("wavelan: transmit failed\n"); 
		ctlr->ntxerr++; 
	} 
	else{ 
		ctlr->txbusy = 1; 
		ctlr->txtmout = 2; 
	} 
	freeb(bp); 
} 
 
static void 
w_txdone(Ctlr* ctlr, int sts) 
{ 
	ctlr->txbusy = 0; 
	ctlr->txtmout = 0; 
	if(sts & WTxErrEv) 
		ctlr->ntxerr++; 
	else 
		ctlr->ntx++; 
} 
 
2003/0311    
/* save the stats info in the ctlr struct */ 
static void 
w_stats(Ctlr* ctlr, int len) 
2002/1112    
{ 
2003/0311    
	int i, rc; 
2002/1112    
	ulong* p = (ulong*)&ctlr->WStats; 
	ulong* pend = (ulong*)&ctlr->end; 
 
2003/0311    
	for (i = 0; i < len && p < pend; i++){ 
		rc = csr_ins(ctlr, WR_Data1); 
		if(rc > 0xf000) 
			rc = ~rc & 0xffff; 
		p[i] += rc; 
	} 
} 
 
/* send the base station scan info to any readers */ 
static void 
w_scaninfo(Ether* ether, Ctlr *ctlr, int len) 
{ 
	int i, j; 
	Netfile **ep, *f, **fp; 
	Block *bp; 
	WScan *wsp; 
2003/0508    
	ushort *scanbuf; 
2003/0311    
 
2003/0508    
	scanbuf = malloc(len*2); 
	if(scanbuf == nil) 
		return; 
	 
2003/0311    
	for (i = 0; i < len ; i++) 
		ctlr->scanbuf[i] = csr_ins(ctlr, WR_Data1); 
2003/0508    
		scanbuf[i] = csr_ins(ctlr, WR_Data1); 
2003/0311    
 
	len *= 2; 
2003/0508    
	/* calculate number of samples */ 
	len /= 25; 
	if(len == 0) 
		goto out; 
 
2003/0311    
	i = ether->scan; 
	ep = ðer->f[Ntypes]; 
	for(fp = ether->f; fp < ep && i > 0; fp++){ 
		f = *fp; 
		if(f == nil || f->scan == 0) 
			continue; 
 
		bp = iallocb(2048); 
2003/0508    
		bp = iallocb(100*len); 
2003/0311    
		if(bp == nil) 
			break; 
		for(j = 0; j < len/(2*25); j++){ 
			wsp = (WScan*)(&ctlr->scanbuf[j*25]); 
2003/0508    
		for(j = 0; j < len; j++){ 
			wsp = (WScan*)(&scanbuf[j*25]); 
2003/0311    
			if(wsp->ssid_len > 32) 
				wsp->ssid_len = 32; 
			bp->wp += snprint((char*)bp->wp, 2048, 
2003/0508    
			bp->wp = (uchar*)seprint((char*)bp->wp, (char*)bp->lim, 
2003/0311    
				"ssid=%.*s;bssid=%E;signal=%d;noise=%d;chan=%d%s\n", 
				wsp->ssid_len, wsp->ssid, wsp->bssid, wsp->signal, 
				wsp->noise, wsp->chan, (wsp->capinfo&(1<<4))?";wep":""); 
		} 
		qpass(f->in, bp); 
		i--; 
	} 
2003/0508    
out: 
	free(scanbuf); 
2003/0311    
} 
 
static int 
w_info(Ether *ether, Ctlr* ctlr) 
{ 
	int sp; 
	Wltv ltv; 
 
2002/1112    
	sp = csr_ins(ctlr, WR_InfoId); 
	ltv.len = ltv.type = 0; 
	w_read(ctlr, sp, 0, <v, 4); 
2003/0311    
	ltv.len--; 
	switch(ltv.type){ 
	case WTyp_Stats: 
		w_stats(ctlr, ltv.len); 
2002/1112    
		return 0; 
2003/0311    
	case WTyp_Scan: 
		w_scaninfo(ether, ctlr, ltv.len); 
		return 0; 
2002/1112    
	} 
	return -1; 
} 
 
2003/0311    
/* set scanning interval */ 
2002/1112    
static void 
2003/0311    
w_scanbs(void *a, uint secs) 
{ 
	Ether *ether = a; 
	Ctlr* ctlr = (Ctlr*) ether->ctlr; 
 
	ctlr->scanticks = secs*(1000/MSperTick); 
} 
 
static void 
2002/1112    
w_intr(Ether *ether) 
{ 
	int rc, txid; 
	Ctlr* ctlr = (Ctlr*) ether->ctlr; 
 
	if((ctlr->state & Power) == 0) 
		return; 
 
	if((ctlr->state & Attached) == 0){ 
		csr_ack(ctlr, 0xffff); 
		csr_outs(ctlr, WR_IntEna, 0); 
		return; 
	} 
 
	rc = csr_ins(ctlr, WR_EvSts); 
	csr_ack(ctlr, ~WEvs);	// Not interested in them 
	if(rc & WRXEv){ 
		w_rxdone(ether); 
		csr_ack(ctlr, WRXEv); 
	} 
	if(rc & WTXEv){ 
		w_txdone(ctlr, rc); 
		csr_ack(ctlr, WTXEv); 
	} 
	if(rc & WAllocEv){ 
		ctlr->nalloc++; 
		txid = csr_ins(ctlr, WR_Alloc); 
		csr_ack(ctlr, WAllocEv); 
		if(txid == ctlr->txdid){ 
			if((rc & WTXEv) == 0) 
				w_txdone(ctlr, rc); 
		} 
	} 
	if(rc & WInfoEv){ 
		ctlr->ninfo++; 
2003/0311    
		w_info(ether, ctlr); 
2002/1112    
		csr_ack(ctlr, WInfoEv); 
	} 
	if(rc & WTxErrEv){ 
		w_txdone(ctlr, rc); 
		csr_ack(ctlr, WTxErrEv); 
	} 
	if(rc & WIDropEv){ 
		ctlr->nidrop++; 
		csr_ack(ctlr, WIDropEv); 
	} 
	w_txstart(ether); 
} 
 
// Watcher to ensure that the card still works properly and 
// to request WStats updates once a minute. 
// BUG: it runs much more often, see the comment below. 
 
static void 
w_timer(void* arg) 
{ 
	Ether* ether = (Ether*) arg; 
	Ctlr* ctlr = (Ctlr*)ether->ctlr; 
 
	ctlr->timerproc = up; 
	for(;;){ 
2003/0407    
		tsleep(&up->sleep, return0, 0, MSperTick); 
2002/1112    
		ctlr = (Ctlr*)ether->ctlr; 
		if(ctlr == 0) 
			break; 
		if((ctlr->state & (Attached|Power)) != (Attached|Power)) 
			continue; 
		ctlr->ticks++; 
 
		ilock(ctlr); 
 
		// Seems that the card gets frames BUT does 
		// not send the interrupt; this is a problem because 
		// I suspect it runs out of receive buffers and 
		// stops receiving until a transmit watchdog 
		// reenables the card. 
		// The problem is serious because it leads to 
		// poor rtts. 
		// This can be seen clearly by commenting out 
		// the next if and doing a ping: it will stop 
		// receiving (although the icmp replies are being 
		// issued from the remote) after a few seconds. 
		// Of course this `bug' could be because I'm reading 
		// the card frames in the wrong way; due to the 
		// lack of documentation I cannot know. 
 
		if(csr_ins(ctlr, WR_EvSts)&WEvs){ 
			ctlr->tickintr++; 
			w_intr(ether); 
		} 
 
		if((ctlr->ticks % 10) == 0) { 
			if(ctlr->txtmout && --ctlr->txtmout == 0){ 
				ctlr->nwatchdogs++; 
				w_txdone(ctlr, WTxErrEv); 
				if(w_enable(ether)){ 
					DEBUG("wavelan: wdog enable failed\n"); 
				} 
				w_txstart(ether); 
			} 
			if((ctlr->ticks % 120) == 0) 
			if(ctlr->txbusy == 0) 
2003/0201    
				w_cmd(ctlr, WCmdEnquire, WTyp_Stats); 
2003/0311    
			if(ctlr->scanticks > 0) 
			if((ctlr->ticks % ctlr->scanticks) == 0) 
			if(ctlr->txbusy == 0) 
				w_cmd(ctlr, WCmdEnquire, WTyp_Scan); 
2002/1112    
		} 
		iunlock(ctlr); 
	} 
	pexit("terminated", 0); 
} 
 
void 
w_multicast(void*, uchar*, int) 
{ 
	// BUG: to be added. 
} 
 
void 
w_attach(Ether* ether) 
{ 
	Ctlr* ctlr; 
	char name[64]; 
	int rc; 
 
	if(ether->ctlr == 0) 
		return; 
 
	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); 
	ctlr = (Ctlr*) ether->ctlr; 
	if((ctlr->state & Attached) == 0){ 
		ilock(ctlr); 
		rc = w_enable(ether); 
		iunlock(ctlr); 
		if(rc == 0){ 
			ctlr->state |= Attached; 
			kproc(name, w_timer, ether); 
		} else 
			print("#l%d: enable failed\n",ether->ctlrno); 
	} 
} 
 
void 
w_detach(Ether* ether) 
{ 
	Ctlr* ctlr; 
	char name[64]; 
 
	if(ether->ctlr == nil) 
		return; 
 
	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); 
	ctlr = (Ctlr*) ether->ctlr; 
	if(ctlr->state & Attached){ 
		ilock(ctlr); 
		w_intdis(ctlr); 
		if(ctlr->timerproc){ 
			if(!postnote(ctlr->timerproc, 1, "kill", NExit)) 
				print("timerproc note not posted\n"); 
			print("w_detach, killing 0x%p\n", ctlr->timerproc); 
		} 
		ctlr->state &= ~Attached; 
		iunlock(ctlr); 
	} 
	ether->ctlr = nil; 
} 
 
void 
w_power(Ether* ether, int on) 
{ 
	Ctlr *ctlr; 
 
	ctlr = (Ctlr*) ether->ctlr; 
	ilock(ctlr); 
iprint("w_power %d\n", on); 
	if(on){ 
		if((ctlr->state & Power) == 0){ 
			if (wavelanreset(ether, ctlr) < 0){ 
				iprint("w_power: reset failed\n"); 
				iunlock(ctlr); 
				w_detach(ether); 
				free(ctlr); 
				return; 
			} 
			if(ctlr->state & Attached) 
				w_enable(ether); 
			ctlr->state |= Power; 
		} 
	}else{ 
		if(ctlr->state & Power){ 
			if(ctlr->state & Attached) 
				w_intdis(ctlr); 
			ctlr->state &= ~Power; 
		} 
	} 
	iunlock(ctlr); 
} 
 
#define PRINTSTAT(fmt,val)	l += snprint(p+l, READSTR-l, (fmt), (val)) 
#define PRINTSTR(fmt)		l += snprint(p+l, READSTR-l, (fmt)) 
 
long 
w_ifstat(Ether* ether, void* a, long n, ulong offset) 
{ 
	Ctlr *ctlr = (Ctlr*) ether->ctlr; 
	char *k, *p; 
	int i, l, txid; 
 
	ether->oerrs = ctlr->ntxerr; 
	ether->crcs = ctlr->nrxfcserr; 
	ether->frames = 0; 
	ether->buffs = ctlr->nrxdropnobuf; 
	ether->overflows = 0; 
 
	// 
	// Offset must be zero or there's a possibility the 
	// new data won't match the previous read. 
	// 
	if(n == 0 || offset != 0) 
		return 0; 
 
	p = malloc(READSTR); 
	l = 0; 
 
	PRINTSTAT("Signal: %d\n", ctlr->signal-149); 
	PRINTSTAT("Noise: %d\n", ctlr->noise-149); 
	PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise); 
	PRINTSTAT("Interrupts: %lud\n", ctlr->nints); 
	PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint); 
	PRINTSTAT("TxPackets: %lud\n", ctlr->ntx); 
	PRINTSTAT("RxPackets: %lud\n", ctlr->nrx); 
	PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr); 
	PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr); 
	PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq); 
	PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc); 
	PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo); 
	PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop); 
	PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs); 
	PRINTSTAT("Ticks: %ud\n", ctlr->ticks); 
	PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr); 
	k = ((ctlr->state & Attached) ? "attached" : "not attached"); 
	PRINTSTAT("Card %s", k); 
	k = ((ctlr->state & Power) ? "on" : "off"); 
2003/0311    
	PRINTSTAT(", power %s", k); 
2002/1112    
	k = ((ctlr->txbusy)? ", txbusy" : ""); 
	PRINTSTAT("%s\n", k); 
 
	if(ctlr->hascrypt){ 
		PRINTSTR("Keys: "); 
		for (i = 0; i < WNKeys; i++){ 
			if(ctlr->keys.keys[i].len == 0) 
				PRINTSTR("none "); 
			else if(SEEKEYS == 0) 
				PRINTSTR("set "); 
			else 
				PRINTSTAT("%s ", ctlr->keys.keys[i].dat); 
		} 
		PRINTSTR("\n"); 
	} 
 
	// real card stats 
	ilock(ctlr); 
	PRINTSTR("\nCard stats: \n"); 
	PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts)); 
	PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts)); 
	i = ltv_ins(ctlr, WTyp_Ptype); 
	PRINTSTAT("Port type: %d\n", i); 
	PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); 
	PRINTSTAT("Current Transmit rate: %d\n", 
		ltv_ins(ctlr, WTyp_CurTxRate)); 
	PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan)); 
	PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens)); 
	PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom)); 
2003/0311    
	if(i == WPTypeAdHoc) 
2002/1112    
		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); 
	else { 
		Wltv ltv; 
		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName)); 
		ltv.type = WTyp_BaseID; 
		ltv.len = 4; 
		if(w_inltv(ctlr, <v)) 
			print("#l%d: unable to read base station mac addr\n", ether->ctlrno); 
		l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", 
			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); 
	} 
	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName)); 
	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName)); 
	if(ltv_ins(ctlr, WTyp_HasCrypt) == 0) 
		PRINTSTR("WEP: not supported\n"); 
	else { 
		if(ltv_ins(ctlr, WTyp_Crypt) == 0) 
			PRINTSTR("WEP: disabled\n"); 
		else{ 
			PRINTSTR("WEP: enabled\n"); 
			k = ((ctlr->xclear)? "excluded": "included"); 
			PRINTSTAT("Clear packets: %s\n", k); 
			txid = ltv_ins(ctlr, WTyp_TxKey); 
			PRINTSTAT("Transmit key id: %d\n", txid); 
		} 
	} 
	iunlock(ctlr); 
 
	PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes); 
	PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes); 
	PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags); 
	PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes); 
	PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes); 
	PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred); 
	PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries); 
	PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries); 
	PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit); 
	PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards); 
	PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes); 
	PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes); 
	PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags); 
	PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes); 
	PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes); 
	PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr); 
	PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf); 
	PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa); 
	PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt); 
	PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag); 
	PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag); 
	USED(l); 
	n = readstr(offset, a, n, p); 
	free(p); 
	return n; 
} 
#undef PRINTSTR 
#undef PRINTSTAT 
 
int 
w_option(Ctlr* ctlr, char* buf, long n) 
{ 
	char *p; 
	int i, r; 
	WKey *key; 
	Cmdbuf *cb; 
 
	r = 0; 
 
2003/0311    
	cb = parsecmd(buf, n); 
2002/1112    
	if(cb->nf < 2) 
		r = -1; 
	else if(cistrcmp(cb->f[0], "essid") == 0){ 
		if(cistrcmp(cb->f[1],"default") == 0) 
			p = ""; 
		else 
			p = cb->f[1]; 
2003/0311    
		if(ctlr->ptype == WPTypeAdHoc){ 
2002/1112    
			memset(ctlr->netname, 0, sizeof(ctlr->netname)); 
			strncpy(ctlr->netname, p, WNameLen); 
		} 
		else{ 
			memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); 
			strncpy(ctlr->wantname, p, WNameLen); 
		} 
	} 
	else if(cistrcmp(cb->f[0], "station") == 0){ 
		memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); 
		strncpy(ctlr->nodename, cb->f[1], WNameLen); 
	} 
	else if(cistrcmp(cb->f[0], "channel") == 0){ 
		if((i = atoi(cb->f[1])) >= 1 && i <= 16) 
			ctlr->chan = i; 
		else 
			r = -1; 
	} 
	else if(cistrcmp(cb->f[0], "mode") == 0){ 
		if(cistrcmp(cb->f[1], "managed") == 0) 
			ctlr->ptype = WPTypeManaged; 
		else if(cistrcmp(cb->f[1], "wds") == 0) 
			ctlr->ptype = WPTypeWDS; 
		else if(cistrcmp(cb->f[1], "adhoc") == 0) 
			ctlr->ptype = WPTypeAdHoc; 
		else if((i = atoi(cb->f[1])) >= 1 && i <= 3) 
			ctlr->ptype = i; 
		else 
			r = -1; 
	} 
	else if(cistrcmp(cb->f[0], "ibss") == 0){ 
		if(cistrcmp(cb->f[1], "on") == 0) 
			ctlr->createibss = 1; 
		else 
			ctlr->createibss = 0; 
	} 
	else if(cistrcmp(cb->f[0], "crypt") == 0){ 
		if(cistrcmp(cb->f[1], "off") == 0) 
			ctlr->crypt = 0; 
		else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt) 
			ctlr->crypt = 1; 
		else 
			r = -1; 
	} 
	else if(cistrcmp(cb->f[0], "clear") == 0){ 
		if(cistrcmp(cb->f[1], "on") == 0) 
			ctlr->xclear = 0; 
		else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt) 
			ctlr->xclear = 1; 
		else 
			r = -1; 
	} 
	else if(cistrncmp(cb->f[0], "key", 3) == 0){ 
		if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){ 
			ctlr->txkey = i-1; 
			key = &ctlr->keys.keys[ctlr->txkey]; 
			key->len = strlen(cb->f[1]); 
			if(key->len > WKeyLen) 
				key->len = WKeyLen; 
			memset(key->dat, 0, sizeof(key->dat)); 
			memmove(key->dat, cb->f[1], key->len); 
		} 
		else 
			r = -1; 
	} 
	else if(cistrcmp(cb->f[0], "txkey") == 0){ 
		if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys) 
			ctlr->txkey = i-1; 
		else 
			r = -1; 
	} 
	else if(cistrcmp(cb->f[0], "pm") == 0){ 
		if(cistrcmp(cb->f[1], "off") == 0) 
			ctlr->pmena = 0; 
		else if(cistrcmp(cb->f[1], "on") == 0){ 
			ctlr->pmena = 1; 
			if(cb->nf == 3){ 
				i = atoi(cb->f[2]); 
				// check range here? what are the units? 
				ctlr->pmwait = i; 
			} 
		} 
		else 
			r = -1; 
	} 
	else 
		r = -2; 
	free(cb); 
 
	return r; 
} 
 
long 
w_ctl(Ether* ether, void* buf, long n) 
{ 
	Ctlr *ctlr; 
 
	if((ctlr = ether->ctlr) == nil) 
		error(Enonexist); 
	if((ctlr->state & Attached) == 0) 
		error(Eshutdown); 
 
	ilock(ctlr); 
	if(w_option(ctlr, buf, n)){ 
		iunlock(ctlr); 
		error(Ebadctl); 
	} 
	if(ctlr->txbusy) 
		w_txdone(ctlr, WTxErrEv); 
	w_enable(ether); 
	w_txstart(ether); 
	iunlock(ctlr); 
 
	return n; 
} 
 
void 
w_transmit(Ether* ether) 
{ 
	Ctlr* ctlr = ether->ctlr; 
 
	if(ctlr == 0) 
		return; 
 
	ilock(ctlr); 
	ctlr->ntxrq++; 
	w_txstart(ether); 
	iunlock(ctlr); 
} 
 
void 
w_promiscuous(void* arg, int on) 
{ 
	Ether* ether = (Ether*)arg; 
	Ctlr* ctlr = ether->ctlr; 
 
	if(ctlr == nil) 
		error("card not found"); 
	if((ctlr->state & Attached) == 0) 
		error("card not attached"); 
	ilock(ctlr); 
	ltv_outs(ctlr, WTyp_Prom, (on?1:0)); 
	iunlock(ctlr); 
} 
 
void 
w_interrupt(Ureg* ,void* arg) 
{ 
	Ether* ether = (Ether*) arg; 
	Ctlr* ctlr = (Ctlr*) ether->ctlr; 
 
	if(ctlr == 0) 
		return; 
	ilock(ctlr); 
	ctlr->nints++; 
	w_intr(ether); 
	iunlock(ctlr); 
} 
 
int 
wavelanreset(Ether* ether, Ctlr *ctlr) 
{ 
	Wltv ltv; 
 
	iprint("wavelanreset, iob 0x%ux\n", ctlr->iob); 
	w_intdis(ctlr); 
	if(w_cmd(ctlr,WCmdIni,0)){ 
		iprint("#l%d: init failed\n", ether->ctlrno); 
		return -1; 
	} 
	w_intdis(ctlr); 
	ltv_outs(ctlr, WTyp_Tick, 8); 
 
	ctlr->chan = 0; 
	ctlr->ptype = WDfltPType; 
	ctlr->txkey = 0; 
	ctlr->createibss = 0; 
	ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1; 
	ctlr->keys.type = WTyp_Keys; 
	if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) 
		ctlr->crypt = 1; 
	*ctlr->netname = *ctlr->wantname = 0; 
	strcpy(ctlr->nodename, "Plan 9 STA"); 
 
	ctlr->netname[WNameLen-1] = 0; 
	ctlr->wantname[WNameLen-1] = 0; 
	ctlr->nodename[WNameLen-1] =0; 
 
	ltv.type = WTyp_Mac; 
	ltv.len	= 4; 
	if(w_inltv(ctlr, <v)){ 
		iprint("#l%d: unable to read mac addr\n", 
			ether->ctlrno); 
		return -1; 
	} 
	memmove(ether->ea, ltv.addr, Eaddrlen); 
 
	if(ctlr->chan == 0) 
		ctlr->chan = ltv_ins(ctlr, WTyp_Chan); 
	ctlr->apdensity = WDfltApDens; 
	ctlr->rtsthres = WDfltRtsThres; 
	ctlr->txrate = WDfltTxRate; 
	ctlr->maxlen = WMaxLen; 
	ctlr->pmena = 0; 
	ctlr->pmwait = 100; 
	ctlr->signal = 1; 
	ctlr->noise = 1; 
	ctlr->state |= Power; 
 
	// free old Ctlr struct if resetting after suspend 
	if(ether->ctlr && ether->ctlr != ctlr) 
		free(ether->ctlr); 
 
	// link to ether 
	ether->ctlr = ctlr; 
	ether->mbps = 10; 
	ether->attach = w_attach; 
	ether->detach = w_detach; 
	ether->interrupt = w_interrupt; 
	ether->transmit = w_transmit; 
	ether->ifstat = w_ifstat; 
	ether->ctl = w_ctl; 
	ether->power = w_power; 
	ether->promiscuous = w_promiscuous; 
	ether->multicast = w_multicast; 
2003/0311    
	ether->scanbs = w_scanbs; 
2002/1112    
	ether->arg = ether; 
 
2003/0311    
	DEBUG("#l%d: irq %ld port %lx type %s", 
2003/0508    
	DEBUG("#l%d: irq %lud port %lx type %s", 
2002/1112    
		ether->ctlrno, ether->intnum, ether->ports[0].port,	ether->type); 
2003/0311    
	DEBUG(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", 
2002/1112    
		ether->ea[0], ether->ea[1], ether->ea[2], 
		ether->ea[3], ether->ea[4], ether->ea[5]); 
 
	return 0; 
} 
 
char* wavenames[] = { 
	"WaveLAN/IEEE", 
	"TrueMobile 1150", 
	"Instant Wireless ; Network PC CARD", 
2003/0311    
	"Instant Wireless Network PC Card", 
2002/1112    
	"Avaya Wireless PC Card", 
	"AirLancer MC-11", 
	nil, 
}; 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)