~kris/9p

9hist

40c366cbc53c1099cf894cfdbe251a965bb0438d — David du Colombier 25 years ago 9577320
Plan 9 from Bell Labs 2001-01-21
2 files changed, 333 insertions(+), 150 deletions(-)

M pc/dat.h
M pc/etherwavelan.c
M pc/dat.h => pc/dat.h +1 -1
@@ 246,7 246,7 @@ struct PCArch
/*
 *  a parsed plan9.ini line
 */
#define ISAOPTLEN	16
#define ISAOPTLEN	28
#define NISAOPT		8

struct ISAConf {

M pc/etherwavelan.c => pc/etherwavelan.c +332 -149
@@ 10,7 10,7 @@
	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.

	WEP is not tested (see BUGs too).
 */

#include "u.h"


@@ 26,11 26,14 @@

#define DEBUG	if(1)print

#define SEEKEYS 1

typedef struct Ctlr 	Ctlr;
typedef struct Wltv 	Wltv;
typedef struct WFrame	WFrame;
typedef struct Stats	Stats;
typedef struct WStats	WStats;
typedef struct WKey		WKey;

struct WStats 
{


@@ 97,6 100,8 @@ enum
	WTyp_PM		=	0xfc09,
	WTyp_PMWait	=	0xfc0c,
	WTyp_NodeName= 	0xfc0e,
	WTyp_Crypt	=	0xfc20,
	WTyp_XClear=	0xfc22,
	WTyp_Tick 	= 	0xfce0,
	WTyp_RtsThres= 	0xfc83,
	WTyp_TxRate	= 	0xfc84,


@@ 104,6 109,9 @@ enum
		WTx2Mbps	= 	0x1,
		WTxAuto		= 	0x3,
	WTyp_Prom	=	0xfc85,
	WTyp_Keys	=	0xfcb0,
	WTyp_TxKey	=	0xfcb1,
	WTyp_HasCrypt=	0xfd4f,
};




@@ 130,6 138,10 @@ enum
	WMaxLen		= 2304,
	WNameLen	= 32,

	WNKeys		= 4,
	WKeyLen		= 14,
	WMinKeyLen	= 5,

	// Wavelan hermes registers
	WR_Cmd		= 0x00,
		WCmdIni		= 0x0000,


@@ 189,12 201,19 @@ enum

	WF_802_11_Off	= 0x44,
	WF_802_3_Off 	= 0x2e,

};

#define csr_outs(ctlr,r,arg) 	outs((ctlr)->iob+(r),(arg))
#define csr_ins(ctlr,r)			ins((ctlr)->iob+(r))
#define csr_ack(ctlr,ev)		outs((ctlr)->iob+WR_EvAck,(ev))

struct WKey
{
	ushort	len;
	char	dat[WKeyLen];
};

struct Wltv
{
	ushort	len;


@@ 214,6 233,9 @@ struct Wltv
		struct {
			char	name[WNameLen];
		};
		struct {
			WKey	key[WNKeys];
		};
	};
};



@@ 259,6 281,11 @@ struct Ctlr
	WFrame	txf;
	uchar	txbuf[1536];
	int		txlen;

	int		keyid;			// 0 if not using WEP
	int		xclear;
	WKey	key[WNKeys];

	Stats;
	WStats;
};


@@ 406,23 433,73 @@ ltv_outstr(Ctlr* ctlr, int type, char *val)
	w_outltv(ctlr, &l);
}

static char	Unkname[] = "who knows";
static char Nilname[] = "card does not tell";

static char*
ltv_inname(Ctlr* ctlr, int type)
{
	static char	unk[] = "unknown";
	static Wltv l;
	
	memset(&l,0,sizeof(l));
	l.type = type;
	l.len  = WNameLen/2+2;
	if (w_inltv(ctlr, &l))
		return unk;
		return Unkname;
	if (l.name[2] == 0)
		return unk;
		return Nilname;
	return l.name+2;
}


static char*
ltv_inkey(Ctlr* ctlr, int id)
{
	static char k[WKeyLen+1];
	Wltv l;
	int	len;
	int i;
	id--; 
	memset(&l, 0, sizeof(l));
	l.type = WTyp_Keys;
	l.len = sizeof(l.key)/2 + 2;	// BUG? 
	if (w_inltv(ctlr, &l))
		return Unkname;
	if (id <0 || id >=WNKeys){
		DEBUG("wavelan: ltv_inkey: bad key id\n");
		return Unkname;
	}
	for (i=0 ; i <WNKeys; i++){
		// BUG: despite the checks, this seems to get nothing;
		// although it could be that the card
		// does not allow reading keys for security.
		if (l.key[id].len != 0){
			DEBUG("len=%d ", l.key[id].len);
		}
		if (l.key[id].dat[1] != 0){
			DEBUG("dat1=%s ", l.key[id].dat+1);
		} 
		if (l.key[id].dat[2] != 0){
			DEBUG("dat2=%s ", l.key[id].dat+2);
		} 
		if (l.key[id].dat[0] != 0){
			DEBUG("%s ", l.key[id].dat);
		} 
		else 
			DEBUG("none ");
	}
	DEBUG("\n");
	if (l.key[id].dat[0] == 0)
		return Nilname;
	len = l.key[id].len;
	if (len > WKeyLen)
		len = WKeyLen;
	memset(k, 0, sizeof(k));
	memmove(k, l.key[id].dat, len);
	k[WKeyLen] = 0;
	return k;
}

static int
w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
{


@@ 501,6 578,7 @@ w_enable(Ether* ether)
{
	Wltv	l;
	Ctlr*	ctlr = (Ctlr*) ether->ctlr;
	ushort	wep;

	if (!ctlr)
		return -1;


@@ 534,6 612,27 @@ w_enable(Ether* ether)

	ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));

	wep = ltv_ins(ctlr, WTyp_HasCrypt);
iprint("wep %d\n", wep);
	if (wep)
		if (ctlr->keyid == 0)
			ltv_outs(ctlr, WTyp_Crypt, 0);
		else {
			// BUG?
			// I think it's ok, but don't know if
			// the card needs a WEPing access point
			// just to admit the keys.
			ltv_outs(ctlr, WTyp_TxKey, ctlr->keyid);
			memset(&l, 0, sizeof(l));
			l.len = sizeof(ctlr->key[0])*WNKeys/2 + 1;
			l.type= WTyp_Keys;
			memmove(l.key, &ctlr->key[0], 
					sizeof(l.key[0])*WNKeys);
			w_outltv(ctlr, &l);
			ltv_outs(ctlr, WTyp_Crypt, wep);
			ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
		}

	// BUG: set multicast addresses

	if (w_cmd(ctlr, WCmdEna, 0)){


@@ 814,8 913,9 @@ w_timer(void* arg)
			if (ctlr->txtmout && --ctlr->txtmout == 0){
				ctlr->nwatchdogs++;
				w_txdone(ctlr, WTxErrEv|1); // 1: keep it busy
				if (w_enable(ether))
				if (w_enable(ether)){
					DEBUG("wavelan: wdog enable failed\n");
				}
				if (ctlr->txbusy) 
					w_txstart(ether,1);
			}


@@ 870,149 970,116 @@ attach(Ether* ether)
	} 
}

#define PRINTSTAT(fmt,val)	l += snprint(p+l, READSTR-l, (fmt), (val))
#define PRINTSTR(fmt)	l += snprint(p+l, READSTR-l, (fmt))

static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
	Ctlr*	ctlr = (Ctlr*) ether->ctlr;
	char*	p;
	int		l;
	int		l, i;
	int		txid;
	char*	k;

	if (n == 0 || ctlr == 0){
		return 0;
	}
	p = malloc(READSTR);
	l = snprint(p, READSTR, "Interrupts: %lud\n",
				ctlr->nints);
	l+= snprint(p+l, READSTR-l, "TxPackets: %lud\n",
				ctlr->ntx);
	l+= snprint(p+l, READSTR-l, "RxPackets: %lud\n",
				ctlr->nrx);
	l+= snprint(p+l, READSTR-l, "TxErrors: %lud\n",
				ctlr->ntxerr);
	l+= snprint(p+l, READSTR-l, "RxErrors: %lud\n",
				ctlr->nrxerr);
	l+= snprint(p+l, READSTR-l, "TxRequests: %lud\n",
				ctlr->ntxrq);
	l+= snprint(p+l, READSTR-l, "AllocEvs: %lud\n",
				ctlr->nalloc);
	l+= snprint(p+l, READSTR-l, "InfoEvs: %lud\n",
				ctlr->ninfo);
	l+= snprint(p+l, READSTR-l, "InfoDrop: %lud\n",
				ctlr->nidrop);
	l+= snprint(p+l, READSTR-l, "WatchDogs: %lud\n",
				ctlr->nwatchdogs);
	if (ctlr->attached)
		l+= snprint(p+l, READSTR-l, "Card attached");
	else
		l+= snprint(p+l, READSTR-l, "Card not attached");
	if (ctlr->txbusy)
		l+= snprint(p+l, READSTR-l, ", tx busy\n");
	else
		l+= snprint(p+l, READSTR-l, "\n");
	l = 0;
	PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
	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);
	k = ((ctlr->attached) ? "attached" : "not attached");
	PRINTSTAT("Card %s", k);
	k = ((ctlr->txbusy)? ", txbusy" : "");
	PRINTSTAT("%s\n", k);

	if (ctlr->keyid){
		PRINTSTR("Keys: ");
		for (i = 0; i < WNKeys; i++)
			if (ctlr->key[i].len == 0)
				PRINTSTR("none ");
			else {
				if (SEEKEYS == 0)
					PRINTSTR("set ");
				else {
					PRINTSTAT("%s ", ctlr->key[i].dat);
				}
			}
		PRINTSTR("\n");
	}

	// real card stats
	ilock(&ctlr->Lock);
	l+= snprint(p+l, READSTR-l, "\nCard stats: \n");
	l+= snprint(p+l, READSTR-l, "Status: %ux\n",
		csr_ins(ctlr, WR_Sts));
	l+= snprint(p+l, READSTR-l, "Event status: %ux\n",
		csr_ins(ctlr, WR_EvSts));
	l+= snprint(p+l, READSTR-l, "Port type: %d\n",
		ltv_ins(ctlr, WTyp_Ptype));
	l+= snprint(p+l, READSTR-l, "Transmit rate: %d\n",
		ltv_ins(ctlr, WTyp_TxRate));
	l+= snprint(p+l, READSTR-l, "Channel: %d\n",
		ltv_ins(ctlr, WTyp_Chan));
	l+= snprint(p+l, READSTR-l, "AP density: %d\n",
		ltv_ins(ctlr, WTyp_ApDens));
	l+= snprint(p+l, READSTR-l, "Promiscuous mode: %d\n",
		ltv_ins(ctlr, WTyp_Prom));

	l+= snprint(p+l, READSTR-l, "SSID name: %s\n", 
		ltv_inname(ctlr, WTyp_NetName));
	l+= snprint(p+l, READSTR-l, "Net name: %s\n",
		ltv_inname(ctlr, WTyp_WantName));
	l+= snprint(p+l, READSTR-l, "Node name: %s\n",
		ltv_inname(ctlr, WTyp_NodeName));
	PRINTSTR("\nCard stats: \n");
	PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
	PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
	PRINTSTAT("Port type: %d\n", ltv_ins(ctlr, WTyp_Ptype));
	PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
	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));
	PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
	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");
			i = ltv_ins(ctlr, WTyp_XClear);
			k = ((i) ? "excluded" : "included");
			PRINTSTAT("Clear packets: %s\n", k);
			txid = ltv_ins(ctlr, WTyp_TxKey);
			PRINTSTAT("Transmit key id: %d\n", txid);
			if (txid >= 1 && txid <= WNKeys){
				k = ltv_inkey(ctlr, txid);
				if (SEEKEYS && k != nil)
					PRINTSTAT("Transmit key: %s\n", k);
			}
		}
	}
	iunlock(&ctlr->Lock);

	l+= snprint(p+l, READSTR-l, "ntxuframes: %lud\n",
				ctlr->ntxuframes);
	l+= snprint(p+l, READSTR-l, "ntxmframes: %lud\n",
				ctlr->ntxmframes);
	l+= snprint(p+l, READSTR-l, "ntxfrags: %lud\n",
				ctlr->ntxfrags);
	l+= snprint(p+l, READSTR-l, "ntxubytes: %lud\n",
				ctlr->ntxubytes);
	l+= snprint(p+l, READSTR-l, "ntxmbytes: %lud\n",
				ctlr->ntxmbytes);
	l+= snprint(p+l, READSTR-l, "ntxdeferred: %lud\n",
				ctlr->ntxdeferred);
	l+= snprint(p+l, READSTR-l, "ntxsretries: %lud\n",
				ctlr->ntxsretries);
	l+= snprint(p+l, READSTR-l, "ntxmultiretries: %lud\n",
				ctlr->ntxmultiretries);
	l+= snprint(p+l, READSTR-l, "ntxretrylimit: %lud\n",
				ctlr->ntxretrylimit);
	l+= snprint(p+l, READSTR-l, "ntxdiscards: %lud\n",
				ctlr->ntxdiscards);
	l+= snprint(p+l, READSTR-l, "nrxuframes: %lud\n",
				ctlr->nrxuframes);
	l+= snprint(p+l, READSTR-l, "nrxmframes: %lud\n",
				ctlr->nrxmframes);
	l+= snprint(p+l, READSTR-l, "nrxfrags: %lud\n",
				ctlr->nrxfrags);
	l+= snprint(p+l, READSTR-l, "nrxubytes: %lud\n",
				ctlr->nrxubytes);
	l+= snprint(p+l, READSTR-l, "nrxmbytes: %lud\n",
				ctlr->nrxmbytes);
	l+= snprint(p+l, READSTR-l, "nrxfcserr: %lud\n",
				ctlr->nrxfcserr);
	l+= snprint(p+l, READSTR-l, "nrxdropnobuf: %lud\n",
				ctlr->nrxdropnobuf);
	l+= snprint(p+l, READSTR-l, "nrxdropnosa: %lud\n",
				ctlr->nrxdropnosa);
	l+= snprint(p+l, READSTR-l, "nrxcantdecrypt: %lud\n",
				ctlr->nrxcantdecrypt);
	l+= snprint(p+l, READSTR-l, "nrxmsgfrag: %lud\n",
				ctlr->nrxmsgfrag);
	snprint(p+l, READSTR-l, "nrxmsgbadfrag: %lud\n",
				ctlr->nrxmsgbadfrag);
	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

/* from ../port/netif.c
 * BUG?: make me a library function.
 */
static char*
matchtoken(char *p, char *token)
{
	int n;

	n = strlen(token);
	if(strncmp(p, token, n))
		return 0;
	p += n;
	if(*p == 0)
		return p;
	if(*p != ' ' && *p != '\t' && *p != '\n')
		return 0;
	while(*p == ' ' || *p == '\t' || *p == '\n')
		p++;
	return p;
}

static void
termtoken(char *tok)
{
	char *p = tok;

	while(*p != 0 && *p != ' ' && *p != '\t' && *p != '\n')
		p++;
	*p = 0;
}
		
#define min(a,b) (((a)<(b))?(a):(b))
static long 


@@ 1021,6 1088,11 @@ ctl(Ether* ether, void* buf, long n)
	int i;
	Ctlr *ctlr;
	Cmdbuf *cb;
	char *nessid;
	WKey *kp;
	char k[64];
	char *kn= &k[3];
	int	 key;

	if((ctlr = ether->ctlr) == nil)
		error(Enonexist);


@@ 1028,6 1100,7 @@ ctl(Ether* ether, void* buf, long n)
		error(Eshutdown);

	cb = parsecmd(buf, n);

	if(cb->nf < 2)
		error(Ebadctl);



@@ 1038,28 1111,68 @@ ctl(Ether* ether, void* buf, long n)
		nexterror();
	}

	if(strcmp(cb->f[0], "ssid") == 0)
		strncpy(ctlr->netname, cb->f[1], WNameLen);
	else if(strcmp(cb->f[0], "net") == 0)
		strncpy(ctlr->wantname, cb->f[1], WNameLen);
	else if(strcmp(cb->f[0], "node") == 0)
	if(strcmp(cb->f[0], "essid") == 0){
		if (strcmp(cb->f[1],"default") == 0)
			nessid = "";
		else
			nessid = cb->f[1];
		if (ctlr->ptype == 3){
			memset(ctlr->netname, 0, sizeof(ctlr->netname));
			strncpy(ctlr->netname, nessid, WNameLen);
		}
		else {
			memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
			strncpy(ctlr->wantname, nessid, WNameLen);
		}
	} 
	else if(strcmp(cb->f[0], "station_name") == 0){
		memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
		strncpy(ctlr->nodename, cb->f[1], WNameLen);
	else if(strcmp(cb->f[0], "chan") == 0){
	}
	else if(strcmp(cb->f[0], "channel") == 0){
		i = atoi(cb->f[1]);
		if(i < 1 || i > 16 )
			error("invalid wavelan channel");
			error("channel not in [1-16]\n");
		ctlr->chan = i;
	}
	else if(strcmp(cb->f[0], "ptype") == 0){
		i = atoi(cb->f[1]);
		if(i < 1 || i > 3 )
			error("invalid wavelan port type");
			error("port type not in [1-3]");
		ctlr->ptype = i;
	}
	else if(strncmp(cb->f[0], "key", 3) == 0){
		strncpy(k,cb->f[0],64);
		if (strcmp(cb->f[1], "off") == 0)
			ctlr->keyid = 0;
		else if (strcmp(cb->f[1], "exclude") == 0){
			ctlr->xclear = 1;
		}
		else if (strcmp(cb->f[1], "include") == 0){
			ctlr->xclear = 0;
		}
		else {
			key = atoi(kn);
			if (key < 1 || key > WNKeys)
				error("key not in [1-4]");
			ctlr->keyid = key;
			kp = &ctlr->key[key-1];
			kp->len = strlen(cb->f[1]);
			if (kp->len > WKeyLen)
				kp->len = WKeyLen;
			memset(kp->dat, 0, sizeof(kp->dat));
			strncpy(kp->dat, cb->f[1], kp->len);
			if (kp->len > WMinKeyLen)
				kp->len = WKeyLen;
			else if (kp->len > 0)
				kp->len = WMinKeyLen;
		}
	} 
	else
		error(Ebadctl);

	if(ctlr->txbusy)
		w_txdone(ctlr, WTxErrEv|1);	// retry later.
		w_txdone(ctlr, WTxErrEv|1);	// retry tx later.
	w_enable(ether);

	iunlock(&ctlr->Lock);


@@ 1114,6 1227,77 @@ interrupt(Ureg* ,void* arg)
	iunlock(&ctlr->Lock);
}

static void
setopt(Ctlr* ctlr, char* opt, int no)
{
	int i;
	char k[64];
	char *kn = &k[3];
	WKey *kp;
	char *ke;
	int key;

	if (strncmp(opt,"essid=",6) == 0){
		if (ctlr->ptype == 3)
			strncpy(ctlr->netname,opt+6,WNameLen);
		else
			strncpy(ctlr->wantname,opt+6,WNameLen);
iprint("essid %d %s\n", ctlr->ptype, opt+6);
	} 
	else if (strncmp(opt,"station_name=",13) == 0){
		// BUG?
		// In my kernel, it seems that the max length of
		// opt is 16, and I get "na" as node name
		// when I say station_name=nautilus.
		strncpy(ctlr->nodename, opt+13,WNameLen);
iprint("nodename %s\n", ctlr->nodename);
	} 
	else if (strncmp(opt, "channel=",8) == 0){
		i = atoi(opt+8);
		if(i < 1 || i > 16 )
			print("#l%d: channel (%d) not in [1-16]\n",no,i);
		else
			ctlr->chan = i;
iprint("chan %d\n", i);
	} 
	else if (strncmp(opt, "ptype=",6) == 0){
		i = atoi(opt+6);
		if(i < 1 || i > 3 )
			print("#l%d: port type (%d) not in [1-3]\n",no,i);
		else
			ctlr->ptype = i;
iprint("ptype %d\n", i);
	}
	else if (strncmp(opt, "key", 3) == 0){
		if (opt[3] == 0 || opt[4] != '='){
			print("#l%d: key option is not keyX=xxxx\n", no);
			return;
		}
		strncpy(k,opt,64);
		key = atoi(kn);
		if (key < 1 || key > WNKeys){
			print("#l%d: key number (%d) not in [1-%d]\n",
					no, key, WNKeys);
			return;
		}
		ctlr->keyid = key;
		kp = &ctlr->key[key-1];
		ke = opt+5;
		while (*ke && *ke != ' ' && *ke != '\n' && *ke != '\t')
			ke++;
		kp->len = ke - (opt+5);
		if (kp->len > WKeyLen)
			kp->len = WKeyLen;
		memset(kp->dat, 0, sizeof(kp->dat));
		strncpy(kp->dat, opt+5, kp->len);
		if (kp->len > WMinKeyLen)
			kp->len = WKeyLen;
		else if (kp->len > 0)
			kp->len = WMinKeyLen;
iprint("key %d <%s> len %d\n", key, kp->dat, kp->len);
	}
}

static int
reset(Ether* ether)
{


@@ 1140,16 1324,15 @@ reset(Ether* ether)
	}
	DEBUG("#l%d: port=0x%lx irq=%ld\n", 
			ether->ctlrno, ether->port, ether->irq);
 
  
	ctlr->chan = 0;
	ctlr->ptype= WDfltPType;
	ctlr->keyid= 0;
	*ctlr->netname = *ctlr->wantname = *ctlr->nodename = 0;
	for (i=0; i < ether->nopt; i++){
		if (strncmp(ether->opt[i],"ssid=",4) == 0)
			strncpy(ctlr->netname,&ether->opt[i][4],WNameLen);
		if (strncmp(ether->opt[i],"net=",4) == 0)
			strncpy(ctlr->wantname,&ether->opt[i][4],WNameLen);
		if (strncmp(ether->opt[i],"node=",5) == 0)
			strncpy(ctlr->nodename,&ether->opt[i][5],WNameLen);
	}

	for (i=0; i < ether->nopt; i++)
		setopt(ctlr, ether->opt[i], ether->ctlrno);

	ctlr->netname[WNameLen-1] = 0;
	ctlr->wantname[WNameLen-1] = 0;
	ctlr->nodename[WNameLen-1] =0;


@@ 1181,9 1364,8 @@ reset(Ether* ether)
			ether->ea[0], ether->ea[1], ether->ea[2],
			ether->ea[3], ether->ea[4], ether->ea[5]);

	ctlr->chan = ltv_ins(ctlr, WTyp_Chan);

	ctlr->ptype = WDfltPType;
	if (ctlr->chan == 0)
		ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
	ctlr->apdensity = WDfltApDens;
	ctlr->rtsthres = WDfltRtsThres;
	ctlr->txrate = WDfltTxRate;


@@ 1223,3 1405,4 @@ etherwavelanlink(void)
{
	addethercard("wavelan", reset);
}