~kris/9p

9hist

3f3316bcf09577f12a5e814315769e0498d2fda1 — David du Colombier 25 years ago 682e636
Plan 9 from Bell Labs 2001-01-18
3 files changed, 1151 insertions(+), 772 deletions(-)

M ip/devip.c
M ip/tcp.c
M pc/etherwavelan.c
M ip/devip.c => ip/devip.c +39 -3
@@ 648,6 648,37 @@ setladdr(Conv* c)
}

/*
 *  set a local port making sure the quad of raddr,rport,laddr,lport is unique
 */
static void
setluniqueport(Conv* c, int lport)
{
	Proto *p;
	Conv *xp;
	int x;

	p = c->p;

	qlock(p);
	for(x = 0; x < p->nc; x++){
		xp = p->conv[x];
		if(xp == nil)
			break;
		if((xp->state == Connected || xp->state == Announced)
		&& xp->lport == lport
		&& xp->rport == c->rport
		&& ipcmp(xp->raddr, c->raddr) == 0
		&& ipcmp(xp->laddr, c->laddr) == 0){
			qunlock(p);
			error("address in use");
		}
	}
	c->lport = lport;
	qunlock(p);
}


/*
 *  pick a local port and set it
 */
static void


@@ 707,6 738,7 @@ static void
setladdrport(Conv* c, char* str, int announcing)
{
	char *p;
	ushort lport;

	/*
	 *  ignore restricted part if it exists.  it's


@@ 725,8 757,10 @@ setladdrport(Conv* c, char* str, int announcing)

	c->lport = 0;
	if(*p != '*'){
		c->lport = atoi(p);
		if(c->lport == 0)
		lport = atoi(p);
		if(lport != 0)
			setluniqueport(c, lport);
		else
			setlport(c);
	}
}


@@ 819,6 853,8 @@ connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb)
char*
Fsstdannounce(Conv* c, char* argv[], int argc)
{
	memset(c->raddr, 0, sizeof(c->raddr));
	c->rport = 0;
	switch(argc){
	default:
		return "bad args to announce";


@@ 866,7 902,7 @@ announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb)
}

/*
 *  called by protocol bide routine to set addresses
 *  called by protocol bind routine to set addresses
 */
char*
Fsstdbind(Conv* c, char* argv[], int argc)

M ip/tcp.c => ip/tcp.c +33 -21
@@ 333,9 333,9 @@ tcpstate(Conv *c, char *state, int n)
	s = (Tcpctl*)(c->ptcl);

	return snprint(state, n,
		"%s srtt %d mdev %d cwin %d swin %d timer.start %d timer.count %d\n",
		"%s srtt %d mdev %d cwin %d swin %d rwin %d timer.start %d timer.count %d\n",
		tcpstates[s->state], s->srtt, s->mdev,
		s->cwind, s->snd.wnd,
		s->cwind, s->snd.wnd, s->rcv.wnd,
		s->timer.start, s->timer.count);
}



@@ 1082,6 1082,7 @@ update(Conv *s, Tcp *seg)
	tpriv = s->p->priv;
	tcb = (Tcpctl*)s->ptcl;

	/* if everything has been acked, force output(?) */
	if(seq_gt(seg->ack, tcb->snd.nxt)) {
		tcb->flags |= FORCE;
		return;


@@ 1092,8 1093,8 @@ update(Conv *s, Tcp *seg)
	    seg->len == 0 && seg->wnd == tcb->snd.wnd ) {

		/* this is a pure ack w/o window update */
//		print("dupack %lud ack %lud sndwnd %d advwin %d\n",
//		tcb->snd.dupacks, seg->ack, tcb->snd.wnd, seg->wnd);
		netlog(s->p->f, Logtcpmsg, "dupack %lud ack %lud sndwnd %d advwin %d\n",
			tcb->snd.dupacks, seg->ack, tcb->snd.wnd, seg->wnd);

		if(++tcb->snd.dupacks == TCPREXMTTHRESH) {
			/*


@@ 1102,15 1103,21 @@ update(Conv *s, Tcp *seg)
			 */
			tcb->snd.recovery = 1;
			tcb->snd.rxt = tcb->snd.nxt;
//			print("fast rxt %lud, nxt %lud\n", tcb->snd.una, tcb->snd.nxt);
			netlog(s->p->f, Logtcpmsg, "fast rxt %lud, nxt %lud\n", tcb->snd.una, tcb->snd.nxt);
			tcprxmit(s);
		} else {
			/* do reno tcp here. */
		}
	}

	/*
	 *  update our send window if this is a new ack (ignore old packets
	 *  even if the ack is new)
	 */
	if(seq_ge(seg->ack,tcb->snd.wl2))
	if(seq_gt(seg->seq,tcb->snd.wl1) || (seg->seq == tcb->snd.wl1)) {
	if(seq_ge(seg->seq,tcb->snd.wl1)) {

		/* a closed window opened, start retransmitting.  why? - presotto */
		if(seg->wnd != 0 && tcb->snd.wnd == 0)
			tcb->snd.ptr = tcb->snd.una;



@@ 1129,9 1136,8 @@ update(Conv *s, Tcp *seg)
	if(!tcb->snd.recovery || seq_ge(seg->ack, tcb->snd.rxt)) {
		tcb->snd.dupacks = 0;
		tcb->snd.recovery = 0;
	} else {
//		print("rxt next %lud, cwin %ud\n", seg->ack, tcb->cwind);
	}
	} else
		netlog(s->p->f, Logtcp, "rxt next %lud, cwin %ud\n", seg->ack, tcb->cwind);

	/* Compute the new send window size */
	acked = seg->ack - tcb->snd.una;


@@ 1554,16 1560,23 @@ tcpiput(Proto *tcp, uchar*, Block *bp)
					bp = nil;
				}
				tcb->rcv.nxt += length;

				/*
				 *  update our rcv window
				 */
				tcprcvwin(s);

				/*
				 *  force an ack if we've got 2 segs
				 *  and the user isn't backing up
				 *  force an ack if we've got 2 segs since we
				 *  last acked.
				 */
				if(tcb->rcv.nxt - tcb->last_ack >= 2*tcb->mss &&
				   qlen(s->rq) < 8*tcb->mss){
				if(tcb->rcv.nxt - tcb->last_ack >= 2*tcb->mss)
					tcb->flags |= FORCE;
				}

				/*
				 *  turn on the acktimer if there's something
				 *  to ack
				 */
				if(tcb->acktimer.state != TimerON)
					tcpgo(tpriv, &tcb->acktimer);



@@ 1711,6 1724,9 @@ tcpoutput(Conv *s)
			usable -= sent;
		}
		ssize = sndcnt-sent;
		if(ssize && usable < 2)
			netlog(s->p->f, Logtcp, "throttled snd.wnd 0x%ux cwind 0x%ux\n",
				tcb->snd.wnd, tcb->cwind);
		if(usable < ssize)
			ssize = usable;
		if(tcb->mss < ssize)


@@ 1782,8 1798,8 @@ tcpoutput(Conv *s)
				seg.flags |= FIN;
				dsize--;
			}
			netlog(f, Logtcp, "qcopy: dlen %d blen %d sndcnt %d qlen %d sent %d rp[0] %d\n",
				dsize, BLEN(bp), sndcnt, qlen(s->wq), sent, bp->rp[0]);
/*			netlog(f, Logtcp, "qcopy: dlen %d blen %d sndcnt %d qlen %d sent %d rp[0] %d\n",
				dsize, BLEN(bp), sndcnt, qlen(s->wq), sent, bp->rp[0]); */
		}

		if(sent+dsize == sndcnt)


@@ 1989,11 2005,6 @@ tcprxmit(Conv *s)
	 *  We should be halving the slow start thershhold (down to one
	 *  mss) but leaving it at mss seems to work well enough
	 */
//	win = (tcb->cwind<tcb->snd.wnd)?tcb->cwind:tcb->snd.wnd/ tcb->mss;
//	win = win/2;
//	if ( win < 2 )
//		win = 2;
//	tcb->ssthresh = win * tcb->mss;
 	tcb->ssthresh = tcb->mss;

	/*


@@ 2032,6 2043,7 @@ tcptimeout(void *arg)
			localclose(s, Etimedout);
			break;
		}
		netlog(s->p->f, Logtcp, "timeout rexmit 0x%lux\n", tcb->snd.una);
		tcprxmit(s);
		tpriv->stats[RetransTimeouts]++;
		tcb->snd.dupacks = 0;

M pc/etherwavelan.c => pc/etherwavelan.c +1079 -748
@@ 1,10 1,16 @@
/*
 * Port for WaveLAN I PCMCIA cards running on 2.4 GHz
 * important: only works for WaveLAN I and PCMCIA 2.4 GHz cards
 * Based on Linux driver by Anthony D. Joseph MIT a.o.
 * We have not added the frequency, encryption and NWID selection stuff, this
 * can be done with the WaveLAN provided DOS programs: instconf.exe, setconf.exe, wfreqsel.exe, etc.
 * Gerard Smit 07/22/98
	Lucent Wavelan IEEE 802.11 pcmcia.
	There is almost no documentation for the card.
	the driver is done using both the freebsd and the linux
	driver 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.

 */

#include "u.h"


@@ 16,880 22,1205 @@
#include "../port/error.h"
#include "../port/netif.h"
#include "etherif.h"
#include "etherwavelan.h"


static void wavelan_receive(Ether *ether);
static int txstart(Ether *ether);
#define DEBUG	if(1)print

static void 
hacr_write_slow(int base, uchar hacr)
typedef struct Ctlr 	Ctlr;
typedef struct Wltv 	Wltv;
typedef struct WFrame	WFrame;
typedef struct Stats	Stats;
typedef struct WStats	WStats;

struct WStats 
{
	outb(HACR(base), hacr);
	/* delay might only be needed sometimes */
	delay(1);
} /* hacr_write_slow */
	ulong	ntxuframes;			// unicast frames
	ulong	ntxmframes;			// multicast frames
	ulong	ntxfrags;			// fragments
	ulong	ntxubytes;			// unicast bytes
	ulong	ntxmbytes;			// multicast bytes
	ulong	ntxdeferred;		// deferred transmits
	ulong	ntxsretries;		// single retries
	ulong	ntxmultiretries;	// multiple retries
	ulong	ntxretrylimit;
	ulong	ntxdiscards;
	ulong	nrxuframes;			// unicast frames
	ulong	nrxmframes;			// multicast frames
	ulong	nrxfrags;			// fragments
	ulong	nrxubytes;			// unicast bytes
	ulong	nrxmbytes;			// multicast bytes
	ulong	nrxfcserr;
	ulong	nrxdropnobuf;
	ulong	nrxdropnosa;
	ulong	nrxcantdecrypt;
	ulong	nrxmsgfrag;
	ulong	nrxmsgbadfrag;
	ulong	end;
};

static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{	
	Ctlr *ctlr;
	int len;
	char *p;
struct WFrame
{
	ushort		sts;
	ushort		rsvd0;
	ushort		rsvd1;
	ushort		qinfo;
	ushort		rsvd2;
	ushort		rsvd3;
	ushort		txctl;
	ushort		framectl;
	ushort		id;
	uchar		addr1[Eaddrlen];
	uchar		addr2[Eaddrlen];
	uchar		addr3[Eaddrlen];
	ushort		seqctl;
	uchar		addr4[Eaddrlen];
	ushort		dlen;
	uchar		dstaddr[Eaddrlen];
	uchar		srcaddr[Eaddrlen];
	ushort		len;
	ushort		dat[3];
	ushort		type;
};

	ctlr = ether->ctlr;
	p = malloc(READSTR);
	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
	len += snprint(p+len, READSTR-len, "upinterrupts: %lud\n", ctlr->upinterrupts);
	len += snprint(p+len, READSTR-len, "dninterrupts: %lud\n", ctlr->dninterrupts);
	len += snprint(p+len, READSTR-len, "int_errors: %lud\n", ctlr->int_errors);
	len += snprint(p+len, READSTR-len, "read_errors: %lud\n", ctlr->read_errors);
	len += snprint(p+len, READSTR-len, "out_packets: %lud\n", ctlr->out_packets);
	len += snprint(p+len, READSTR-len, "tx_too_long: %lud\n", ctlr->tx_too_long);
	len += snprint(p+len, READSTR-len, "tx_DMA_underrun: %lud\n", ctlr->tx_DMA_underrun);
	len += snprint(p+len, READSTR-len, "tx_carrier_error: %lud\n", ctlr->tx_carrier_error);
	len += snprint(p+len, READSTR-len, "tx_congestion: %lud\n", ctlr->tx_congestion);
	len += snprint(p+len, READSTR-len, "tx_heart_beat: %lud\n", ctlr->tx_heart_beat);
	len += snprint(p+len, READSTR-len, "rx_overflow: %lud\n", ctlr->rx_overflow);
	len += snprint(p+len, READSTR-len, "rx_overrun: %lud\n", ctlr->rx_overrun);
	len += snprint(p+len, READSTR-len, "rx_crc_error: %lud\n", ctlr->rx_crc);
	len += snprint(p+len, READSTR-len, "rx_no_sfd: %lud\n", ctlr->rx_no_sfd);
	len += snprint(p+len, READSTR-len, "rx_dropped: %lud\n", ctlr->rx_dropped);
	len += snprint(p+len, READSTR-len, "tx_packets: %lud\n", ctlr->tx_packets);
	len += snprint(p+len, READSTR-len, "rx_packets: %lud\n", ctlr->rx_packets);
	snprint(p+len, READSTR-len, "in_packets: %lud\n", ctlr->in_packets);
// Lucent's Length-Type-Value records  to talk to the wavelan.
// most operational parameters are read/set using this. 
enum 
{
	WTyp_Stats	=	0xf100,
	WTyp_Ptype	= 	0xfc00,
	WTyp_Mac	= 	0xfc01,
	WTyp_WantName= 	0xfc02,
	WTyp_Chan	= 	0xfc03,
	WTyp_NetName = 	0xfc04,
	WTyp_ApDens	= 	0xfc06,
	WTyp_MaxLen	= 	0xfc07,
	WTyp_PM		=	0xfc09,
	WTyp_PMWait	=	0xfc0c,
	WTyp_NodeName= 	0xfc0e,
	WTyp_Tick 	= 	0xfce0,
	WTyp_RtsThres= 	0xfc83,
	WTyp_TxRate	= 	0xfc84,
		WTx1Mbps	= 	0x0,
		WTx2Mbps	= 	0x1,
		WTxAuto		= 	0x3,
	WTyp_Prom	=	0xfc85,
};

	n = readstr(offset, a, n, p);
	free(p);
	return n;
}

static void
attach(Ether* ether)

// Controller
enum
{
	Ctlr *ctlr;
	WDfltIRQ = 3,		// default irq
	WDfltIOB = 0x100,	// default IO base

	WIOLen = 0x40,	// Hermes IO length

	WTmOut = 65536,	// Cmd time out

	WPTypeManaged	= 1,
	WPTypeWDS	= 2,
	WPTypeAdHoc	= 3,
	WDfltPType	= WPTypeManaged,

	WDfltApDens	= 1,
	WDfltRtsThres	= 2347,		// == disabled
	WDfltTxRate	= WTxAuto,		// 2Mbps

	WMaxLen		= 2304,
	WNameLen	= 32,

	// Wavelan hermes registers
	WR_Cmd		= 0x00,
		WCmdIni		= 0x0000,
		WCmdEna		= 0x0001,
		WCmdDis		= 0x0002,
		WCmdMalloc	= 0x000a,
		WCmdAskStats= 0x0011,
		WCmdMsk		= 0x003f,
		WCmdAccRd	= 0x0021,
		WCmdAccWr	= 0x0121,
		WCmdTxFree	= 0x000b|0x0100,
	WR_Parm0	= 0x02,
	WR_Parm1	= 0x04,
	WR_Parm2	= 0x06,
	WR_Sts		= 0x08,
	WR_InfoId	= 0x10,
	WR_Sel0		= 0x18,
	WR_Sel1		= 0x1a,
	WR_Off0		= 0x1c,
	WR_Off1		= 0x1e,
		WBusyOff	= 0x8000,
		WErrOff		= 0x4000,
		WResSts		= 0x7f00,
	WR_RXId		= 0x20,
	WR_Alloc	= 0x22,
	WR_EvSts	= 0x30,
	WR_IntEna	= 0x32,
		WCmdEv		= 0x0010,
		WRXEv		= 0x0001,
		WTXEv		= 0x0002,
		WTxErrEv	= 0x0004,
		WAllocEv	= 0x0008,
		WInfoEv		= 0x0080,
		WIDropEv	= 0x2000,
		WTickEv		= 0x8000,
		WEvs		= WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv,

	WR_EvAck	= 0x34,
	WR_Data0	= 0x36,
	WR_Data1	= 0x38,

	// Frame stuff

	WF_Err		= 0x0003,
	WF_1042		= 0x2000,
	WF_Tunnel	= 0x4000,
	WF_WMP		= 0x6000,

	WF_Data		= 0x0008,

	WSnapK1		= 0xaa,
	WSnapK2		= 0x00,
	WSnapCtlr	= 0x03,
	WSnap0		= (WSnapK1|(WSnapK1<<8)),
	WSnap1		= (WSnapK2|(WSnapCtlr<<8)),
	WSnapHdrLen	= 6,

	WF_802_11_Off	= 0x44,
	WF_802_3_Off 	= 0x2e,
};

	ctlr = ether->ctlr;
	ilock(&ctlr->wlock);
	if(ctlr->attached){
		iunlock(&ctlr->wlock);
		return;
	}
	ctlr->attached = 1;
	iunlock(&ctlr->wlock);
}
#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))

static void
interrupt_handler(Ether *ether, uchar status)
struct Wltv
{
	Ctlr *ctlr;
	int status0;
	int tx_status;
	int base;
	ctlr = ether->ctlr;
	base = ctlr->port;
	ushort	len;
	ushort	type;
	union 
	{
		struct {
			ushort	val;
			ushort	pad;
		};
		struct {
			uchar	addr[8];
		};
		struct {
			char	s[17*2];
		};
		struct {
			char	name[WNameLen];
		};
	};
};

	status0 = status;
// What the driver thinks. Not what the card thinks.
struct Stats
{
	ulong	nints;
	ulong	nrx;
	ulong	ntx;
	ulong	ntxrq;
	ulong	nrxerr;
	ulong	ntxerr;
	ulong	nalloc;		// allocation (reclaim) events
	ulong	ninfo;
	ulong	nidrop;
	ulong	nwatchdogs;	// transmits time outs, actually
};

	/* Return if no actual interrupt from i82593 */
	if(!(status0 & SR0_INTERRUPT)) {
		print("Wavelan: Interrupt from dead card\n");
		return;
	}
	ctlr->status = status0;	/* Save current status (for commands) */
	if (status0 & SR0_RECEPTION) {
		if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) {
			print("wavelan: receive buffer overflow\n");
			ctlr->rx_overflow++;
			outb(LCCR(base), CR0_INT_ACK | OP0_NOP);	/* Acknowledge the interrupt */
			return;
		}
		wavelan_receive(ether);
		if (status0 & SR0_EXECUTION)
			print("wavelan_cs: interrupt is both rx and tx, status0 = %x\n",
				status0);
		outb(LCCR(base), CR0_INT_ACK | OP0_NOP);	/* Acknowledge the interrupt */
		return;
	}
	if (!(status0 & SR0_EXECUTION)) {
		print("wavelan_cs: interrupt is neither rx or tx, status0 = %x\n",
			status0);
		outb(LCCR(base), CR0_INT_ACK | OP0_NOP);	/* Acknowledge the interrupt */
		return;
	}
	/* interrupt due to configure_done or IA_setup_done */
	if ((status0 & SR0_EVENT_MASK) == SR0_CONFIGURE_DONE ||
			(status0 & SR0_EVENT_MASK) == SR0_IA_SETUP_DONE) {
		outb(LCCR(base), CR0_INT_ACK | OP0_NOP);	/* Acknowledge the interrupt */
		return;
	}
	/* so a transmit interrupt is remaining */
	if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
			(status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE) {
		tx_status = inb(LCSR(base));
		tx_status |= (inb(LCSR(base)) << 8);
		if (!(tx_status & TX_OK)) {
			if (tx_status & TX_FRTL) {
				print("wavelan_cs: frame too long\n");
				ctlr->tx_too_long++;
			}
			if (tx_status & TX_UND_RUN) {
				/* print("wavelan_csd: DMA underrun\n"); */
				ctlr->tx_DMA_underrun++;
			}
			if (tx_status & TX_LOST_CTS) {
				/* print("wavelan: no CTS\n"); */
				ctlr->tx_carrier_error++;
			}
			if (tx_status & TX_LOST_CRS) {
				/* print("wavelan: lost CRS\n"); */
				ctlr->tx_carrier_error++;
			}
			if (tx_status & TX_DEFER) {
				/* print("wavelan: channel jammed\n"); */
				ctlr->tx_congestion++;
			}
			if (tx_status & TX_COLL) {
				if (tx_status & TX_MAX_COL) {
					/* print("wavelan_cs: channel congestion\n"); */
					ctlr->tx_congestion++;
				}
			}
			if (tx_status & TX_HRT_BEAT) {
 				/* print("wavelan_cs: heart beat\n"); */
				ctlr->tx_heart_beat++;
			}
		}
		
		ctlr->tx_packets++;
		ctlr->txbusy = 0;
      		outb(LCCR(base), CR0_INT_ACK | OP0_NOP);	/* Acknowledge the interrupt */
		txstart(ether);					/* start new transfer if any */
		return;
	}
	print("wavelan: unknown interrupt\n");
	outb(LCCR(base), CR0_INT_ACK | OP0_NOP);	/* Acknowledge the interrupt */
struct Ctlr 
{
	Lock;
	Rendez	timer;

	int		attached;
	int		slot;
	int		iob;
	int		ptype;
	int		apdensity;
	int		rtsthres;
	int		txbusy;
	int		txrate;
	int		txdid;
	int		txmid;
	int		txtmout;
	int		maxlen;
	int		chan;
	int		pmena;
	int		pmwait;

	char	netname[WNameLen];
	char	wantname[WNameLen];
	char	nodename[WNameLen];
	WFrame	txf;
	uchar	txbuf[1536];
	int		txlen;
	Stats;
	WStats;
};

}

// w_... routines do not ilock the Ctlr and should 
// be called locked.

static void 
w_intdis(Ctlr* ctlr)
{
	csr_outs(ctlr, WR_IntEna, 0); 
	csr_ack(ctlr, 0xffff);
}

static Block*
rbpalloc(Block* (*f)(int))
static void
w_intena(Ctlr* ctlr)
{
	Block *bp;
	ulong addr;
	csr_outs(ctlr, WR_IntEna, WEvs); 
}

	/*
	 * The receive buffers must be on a 32-byte
	 * boundary for EISA busmastering.
	 */
	if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
		addr = (ulong)bp->base;
		addr = ROUNDUP(addr, 32);
		bp->rp = (uchar*)addr;
static int
w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
{
	int i;
	int rc;
	csr_outs(ctlr, WR_Parm0, arg);
	csr_outs(ctlr, WR_Cmd, cmd);
	for (i = 0; i<WTmOut; i++){
		rc = csr_ins(ctlr, WR_EvSts);
		if ( rc&WCmdEv ){
			rc = csr_ins(ctlr, WR_Sts);
			csr_ack(ctlr, WCmdEv);
			if ((rc&WCmdMsk) != (cmd&WCmdMsk))
				break;
			if (rc&WResSts)
				break;
			return 0;
		}
	}

	return bp;
	return -1;
}

static int 
wavelan_cmd(Ether *ether, int base, char *str, int cmd, int result)
static int
w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
{
	int status;
	unsigned long spin;
	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;
}

	/* Spin until the chip finishes executing its current command (if any) */
	do {
		outb(LCCR(base), OP0_NOP | CR0_STATUS_3);
		status = inb(LCSR(base));
	} while ((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE);
static int
w_inltv(Ctlr* ctlr, Wltv* l)
{
	int i, len;
	ushort *p,code;

	outb(LCCR(base), cmd);			/* Send the command */
	if(result == SR0_NO_RESULT) {		/* Return immediately, if the command
					   	doesn't return a result */
		return(TRUE);
	if (w_cmd(ctlr, WCmdAccRd, l->type)){
		DEBUG("wavelan: access read failed\n");
		return -1;
	}

	/* Busy wait while the LAN controller executes the command.
	* Interrupts had better be enabled (or it will be a long wait).
	*  (We could enable just the WaveLAN's IRQ..., we do not bother this is only for setup commands)
	 */
	for(spin = 0; (spin < 10000000); spin++)
		;
	outb(LCCR(base), CR0_STATUS_0 | OP0_NOP);
	status = inb(LCSR(base));
	if(status & SR0_INTERRUPT){
		if (((status & SR0_EVENT_MASK) == SR0_CONFIGURE_DONE) ||
				((status & SR0_EVENT_MASK) == SR0_IA_SETUP_DONE) ||
				((status & SR0_EVENT_MASK) == SR0_EXECUTION_ABORTED) ||
				((status & SR0_EVENT_MASK) == SR0_DIAGNOSE_PASSED))
			outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* acknowledge interrupt */
		else 
			interrupt_handler(ether, status);
	} else {
		print("wavelan_cmd: %s timeout, status0 = 0x%uX\n", str, status);
  		outb(OP0_ABORT, LCCR(base));
		spin = 0;
		while(spin++ < 250)	/* wait for the command to execute */
		delay(1);
		return 0;
	if (w_seek(ctlr,l->type,0,1)){
		DEBUG("wavelan: seek failed\n");
		return -1;
	}
	if((status & SR0_EVENT_MASK) != result){
		print("wavelan_cmd: %s failed, status0 = 0x%uX\n", str, status);
		return 0;  
	len = csr_ins(ctlr, WR_Data1);
	if (len > l->len)
		return -1;
	l->len = len;
	if ((code=csr_ins(ctlr, WR_Data1)) != l->type){
		DEBUG("wavelan: type %x != code %x\n",l->type,code);
		return -1;
	}
	return 1;
} /* wavelan_cmd */
	p = &l->val;
	len--;
	for (i=0; i<len; i++)
		p[i] = csr_ins(ctlr, WR_Data1);
	return 0;
}

static uchar 
mmc_in(int base, uchar o)
static void
w_outltv(Ctlr* ctlr, Wltv* l)
{
	while (inb(HASR(base)) & HASR_MMI_BUSY) ;	/* Wait for MMC to go idle */
	outb(MMR(base), o << 1);			/* Set the read address */
	outb(MMD(base), 0);				/* Required dummy write */
	while (inb(HASR(base)) & HASR_MMI_BUSY) ;	/* Wait for MMC to go idle */
	return((uchar) (inb(MMD(base))));	
	int i,len;
	ushort *p;

	if (w_seek(ctlr,l->type,0,1))
		return;
	csr_outs(ctlr, WR_Data1, l->len);
	csr_outs(ctlr, WR_Data1, l->type);
	p = &l->val;
	len = l->len-1;
	for (i=0; i<len; i++)
		csr_outs(ctlr, WR_Data1, p[i]);
	w_cmd(ctlr,WCmdAccWr,l->type);
}

static int 
read_ringbuf(Ether *ether, int addr, uchar *buf, int len)
{	Ctlr *ctlr;
	int base;
	int ring_ptr = addr;
	int chunk_len;
	uchar *buf_ptr = buf;
	ctlr = ether->ctlr;
	base = ctlr->port;

	/* If buf is NULL, just increment the ring buffer pointer */
	if (buf == 0)
	return((ring_ptr - RX_BASE + len) % RX_SIZE + RX_BASE);
	while (len > 0) {
		/* Position the Program I/O Register at the ring buffer pointer */
		outb(PIORL(base), ring_ptr & 0xff);
		outb(PIORH(base), ((ring_ptr >> 8) & PIORH_MASK));
		/* First, determine how much we can read without wrapping around the
 		ring buffer */
		if ((addr + len) < (RX_BASE + RX_SIZE))
			chunk_len = len;
		else
			chunk_len = RX_BASE + RX_SIZE - addr;
		insb(PIOP(base), buf_ptr, chunk_len);
		buf_ptr += chunk_len;
		len -= chunk_len;
		ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE;
	}
	return(ring_ptr);
} /* read_ringbuf */
static void
ltv_outs(Ctlr* ctlr, int type, ushort val)
{
	Wltv l;

static void 
wavelan_hardware_send_packet(Ether *ether, void *buf, short length)
	l.type = type;
	l.val = val;
	l.len = 2;
	w_outltv(ctlr, &l);
}

static ushort
ltv_ins(Ctlr* ctlr, int type)
{
	Ctlr *ctlr;
	int base;
	register ushort xmtdata_base = TX_BASE;
	ctlr = ether->ctlr;
	base = ctlr->port;
	Wltv l;

	outb(PIORL(base), xmtdata_base & 0xff);
	outb(PIORH(base), ((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX);
	outb(PIOP(base), length & 0xff);		/* lsb */
	outb(PIOP(base), length >> 8);  		/* msb */
	outsb(PIOP(base), buf, length);		/* Send the data */
	outb(PIOP(base), OP0_NOP);		/* Indicate end of transmit chain */
	l.type = type;
	l.len = 2;
	l.val = 0;
	w_inltv(ctlr, &l);
	return l.val;
}

 	/* Reset the transmit DMA pointer */
	hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
	outb(HACR(base), HACR_DEFAULT);
	/* Send the transmit command */
	wavelan_cmd(ether, base, "wavelan_hardware_send_packet(): transmit", OP0_TRANSMIT,
	      SR0_NO_RESULT);
} /* wavelan_hardware_send_packet */
static void
ltv_outstr(Ctlr* ctlr, int type, char *val)
{
	Wltv l;
	int len;

	len = (strlen(val)+1)&~1;
	memset(&l,0,sizeof(l));
	l.type = type;
	l.len = (len/2)+2;
	l.val = len;					// l.s[0] and l.s[1]
	strncpy(l.s+2,val,strlen(val));
	w_outltv(ctlr, &l);
}

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;
	if (l.name[2] == 0)
		return unk;
	return l.name+2;
}

static int 
txstart(Ether *ether)
{	
	Ctlr *ctlr;
	Block *bp;
	int base, length;
	int status;
	ctlr = ether->ctlr;
	base = ctlr->port;

	for(;;) { 
		if(ctlr->txbp){
			bp = ctlr->txbp;
			ctlr->txbp = 0;
		}
		else{
			bp = qget(ether->oq);
			if(bp == nil)
				break;
		}

		length = BLEN(bp);
		length = (ETH_ZLEN < length) ? length : ETH_ZLEN;
		outb(LCCR(base), OP0_NOP | CR0_STATUS_3);
		status = inb(LCSR(base));
		if ((status & SR3_EXEC_STATE_MASK) == SR3_EXEC_IDLE) {
			wavelan_hardware_send_packet(ether, bp->rp, length);
			freeb(bp);
			ctlr->out_packets++;
static int
w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
{
	ushort *p = (ushort*)buf;
	int		i, n;

	n=0;
	if (w_seek(ctlr, type, off, 1) == 0){
		len /= 2;
		for (i = 0; i < len; i++){
			p[i] = csr_ins(ctlr, WR_Data1);
			n += 2;
		}
		else{
			ctlr->txbp = bp;
			if(ctlr->txbusy == 0){
				ctlr->txbusy = 1;
			}
			break;
	} else
		DEBUG("wavelan: w_read: seek failed");

	return n;
}


static int
w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
{
	ushort *p = (ushort*)buf;
	ulong	l = len / 2;
	int		i,tries;

	for (tries=0; tries < WTmOut; tries++){	
		if (w_seek(ctlr, type, off, 0)){
			DEBUG("wavelan: w_write: seek failed\n");
			return 0;
		}

		for (i = 0; i < l; i++)
			csr_outs(ctlr, WR_Data0, p[i]);
		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;
} /* wavelan txstart */
}

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 
wavelan_start_of_frame(Ether *ether, int rfp, int wrap)
w_enable(Ether* ether)
{
	Ctlr *ctlr;
	int base;
	int rp, len;
	Wltv	l;
	Ctlr*	ctlr = (Ctlr*) ether->ctlr;

	ctlr = ether->ctlr;
	base = ctlr->port;
	if (!ctlr)
		return -1;

	rp = (rfp - 5 + RX_SIZE) % RX_SIZE;
	outb(PIORL(base), rp & 0xff);
	outb(PIORH(base), ((rp >> 8) & PIORH_MASK));
	len = inb(PIOP(base));
	len |= inb(PIOP(base)) << 8;
	w_intdis(ctlr);
	w_cmd(ctlr, WCmdDis, 0);
	w_intdis(ctlr);
	if(w_cmd(ctlr, WCmdIni, 0))
		return -1;

	if (len > 1600) {		/* Sanity check on size */
		print("wavelan_cs: Received frame too large, rfp %d rp %d len 0x%x\n",
			rfp, rp, len);
	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_RtsThres, ctlr->rtsthres);
	ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
	ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
	ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
	ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
	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);
	l.type = WTyp_Mac;
	l.len = 4;
	memmove(l.addr, ether->ea, Eaddrlen);
	w_outltv(ctlr, &l);

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

	// BUG: set multicast addresses

	if (w_cmd(ctlr, WCmdEna, 0)){
		DEBUG("wavelan: Enable failed");
		return -1;
	}
  
	if(len < 7){
		print("wavelan_start_of_frame: Received null frame, rfp %d len 0x%x\n", rfp, len);
		return(-1);
	}
	/* Wrap around buffer */
	if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE)) {	/* magic formula ! */
		print("wavelan_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n",wrap, rfp, len);
		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;

	return((rp - len + RX_SIZE) % RX_SIZE);
} /* wv_start_of_frame */
}

static void wavelan_read(Ether *ether, int fd_p, int sksize)
{
	Ctlr *ctlr;
	Block *bp;
	uchar stats[3];

	ctlr = ether->ctlr;
static void
w_rxdone(Ether* ether)
{
	Ctlr*	ctlr = (Ctlr*) ether->ctlr;
	ushort	sp;
	WFrame	f;
	Block*	bp=0;
	ulong	l;
	Etherpkt* ep;

	sp = csr_ins(ctlr, WR_RXId);
	l = w_read(ctlr, sp, 0, &f, sizeof(f));
	if (l == 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:
		l = f.dlen + WSnapHdrLen;
		bp = iallocb(ETHERHDRSIZE + l + 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, l+2) == 0){
			DEBUG("wavelan: read 802.11 error\n");
			goto rxerror;
		}
		bp->wp +=  l+2;
		bp = trimblock(bp, 0, f.dlen+ETHERHDRSIZE);
		break;
	default:
		l = ETHERHDRSIZE + f.dlen + 2;
		bp = iallocb(l);
		if (!bp)
			goto rxerror;
		if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, l) == 0){
			DEBUG("wavelan: read 800.3 error\n");
			goto rxerror;
		}
		bp->wp += l;
	}

	ctlr->rx_packets++;
	ctlr->nrx++;
	etheriq(ether,bp,1);
	return;

	if ((bp = rbpalloc(allocb)) == 0){		
 		print("wavelan: could not rbpalloc(%d).\n", sksize);
		ctlr->rx_dropped++;
		return;
	} else {
		fd_p = read_ringbuf(ether, fd_p, ctlr->rbp->rp, sksize);
		ctlr->rbp->wp = ctlr->rbp->rp + sksize;
		/* read signal level, silence level and signal quality bytes */
		read_ringbuf(ether, (fd_p+4) % RX_SIZE+RX_BASE, stats, 3);
		/*
		* Hand the packet to the Network Module
		*/
		etheriq(ether, ctlr->rbp, 1);
		ctlr->in_packets++;
		ctlr->rbp = bp;
		return;
	}
} /* wavelan_read */
  rxerror:
	freeb(bp);
	ctlr->nrxerr++;
}

static void 
wavelan_receive(Ether *ether)
static int
w_txstart(Ether* ether, int again)
{
	Ctlr *ctlr;
	int base;
	int newrfp, rp, len, f_start, status;
	int i593_rfp, stat_ptr;
	uchar c[4];

	ctlr = ether->ctlr;
	base = ctlr->port;

	/* Get the new receive frame pointer from the i82593 chip */
	outb(LCCR(base), CR0_STATUS_2 | OP0_NOP);
	i593_rfp = inb(LCSR(base));
	i593_rfp |= inb(LCSR(base)) << 8;
	i593_rfp %= RX_SIZE;

	/* Get the new receive frame pointer from the WaveLAN card.
	* It is 3 bytes more than the increment of the i82593 receive
	* frame pointer, for each packet. This is because it includes the
	* 3 roaming bytes added by the mmc.
	*/
	newrfp = inb(RPLL(base));
	newrfp |= inb(RPLH(base)) << 8;
	newrfp %= RX_SIZE;

// print("wavelan_cs: i593_rfp %d stop %d newrfp %d ctlr->rfp %d\n",
// 	i593_rfp, ctlr->stop, newrfp, ctlr->rfp);

	if (newrfp == ctlr->rfp)
		print("wavelan_cs: odd RFPs:  i593_rfp %d stop %d newrfp %d ctlr->rfp %d\n",
			i593_rfp, ctlr->stop, newrfp, ctlr->rfp);

	while(newrfp != ctlr->rfp) {
		rp = newrfp;
		/* Find the first frame by skipping backwards over the frames */
		while (((f_start = wavelan_start_of_frame(ether,rp, newrfp)) != ctlr->rfp) && (f_start != -1))
			rp = f_start;
		if(f_start == -1){
			print("wavelan_cs: cannot find start of frame ");
			print(" i593_rfp %d stop %d newrfp %d ctlr->rfp %d\n",
				i593_rfp, ctlr->stop, newrfp, ctlr->rfp);
			ctlr->rfp = rp;
			continue;
		}
		stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE;
		read_ringbuf(ether, stat_ptr, c, 4);
		status = c[0] | (c[1] << 8);
		len = c[2] | (c[3] << 8);

		if(!(status & RX_RCV_OK)) {
			if(status & RX_NO_SFD) ctlr->rx_no_sfd++;
			if(status & RX_CRC_ERR) ctlr->rx_crc++;
			if(status & RX_OVRRUN) ctlr->rx_overrun++;

  			print("wavelan_cs: packet not received ok, status = 0x%x\n", status);
		} else	wavelan_read(ether, f_start, len - 2);
	Etherpkt* ep;
	Ctlr*	ctlr = (Ctlr*) ether->ctlr;
	Block*	bp; 
	int		txid;
	if (ctlr == 0 || ctlr->attached == 0 )
		return -1;
	if (ctlr->txbusy && again==0)
		return -1;

		ctlr->rfp = rp;		/* one packet processed, skip it */
	txid = ctlr->txdid;
	if (again){		
		bp = 0;		// a watchdog reenabled the card. 
		goto retry;	// must retry a previously failed tx. 
	}

	/*
	* Update the frame stop register, but set it to less than
	* the full 8K to allow space for 3 bytes of signal strength
	* per packet.
	*/
	ctlr->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
	outb(LCCR(base), OP0_SWIT_TO_PORT_1 | CR0_CHNL);
	outb(LCCR(base), CR1_STOP_REG_UPDATE | (ctlr->stop >> RX_SIZE_SHIFT));
	outb(LCCR(base), OP1_SWIT_TO_PORT_0);
} /* wavelan_receive */
	bp = qget(ether->oq);
	if (bp == 0)
		return 0;
	ep = (Etherpkt*) bp->rp;
	ctlr->txbusy = 1;
	
	// BUG: only  IP/ARP/RARP seem to be ok for 802.3
	// Other packets should be just copied to the board.
	// The driver is not doing so, though. 
	// Besides, the Block should be used instead of txbuf,
	// to save a memory copy.
	memset(ctlr->txbuf,0,sizeof(ctlr->txbuf));
	memset(&ctlr->txf,0,sizeof(ctlr->txf));
	ctlr->txf.framectl = WF_Data;
	memmove(ctlr->txf.addr1, ep->d, Eaddrlen);
	memmove(ctlr->txf.addr2, ep->s, Eaddrlen);
	memmove(ctlr->txf.dstaddr, ep->d, Eaddrlen);
	memmove(ctlr->txf.srcaddr, ep->s, Eaddrlen);
	memmove(&ctlr->txf.type,ep->type,2);
	ctlr->txlen = BLEN(bp);
	ctlr->txf.dlen = ctlr->txlen - WSnapHdrLen;
	hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
	hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
	hnputs((uchar*)&ctlr->txf.len, ctlr->txlen - WSnapHdrLen);
	if (ctlr->txlen - ETHERHDRSIZE > 1536){
		print("wavelan: txbuf overflow");
		freeb(bp);
		return -1;
	}
	memmove(ctlr->txbuf, bp->rp+sizeof(ETHERHDRSIZE)+10, 
			ctlr->txlen - ETHERHDRSIZE  );
retry:
	w_write(ctlr, txid, 0, &ctlr->txf, sizeof(ctlr->txf)); 

	w_write(ctlr, txid, WF_802_11_Off, ctlr->txbuf,
			ctlr->txlen - ETHERHDRSIZE + 2);
	if (w_cmd(ctlr, WCmdTxFree, txid)){
		DEBUG("wavelan: transmit failed\n");
		ctlr->txbusy=0;	// added 
		ctlr->ntxerr++;
		freeb(bp);
		return -1;
	}
	ctlr->txtmout = 2;
	freeb(bp);
	return 0;
}

static void
interrupt(Ureg*, void* arg)
w_txdone(Ctlr* ctlr, int sts)
{
	Ether *ether;
	Ctlr *ctlr;
	int base;

	ether = arg;
	ctlr = ether->ctlr;
	base = ctlr->port;

	ilock(&ctlr->wlock);

	ctlr->interrupts++;
	int b = ctlr->txbusy;

	ctlr->txbusy = 0;
	ctlr->txtmout= 0;
	if (sts & WTxErrEv){
		ctlr->ntxerr++;
		if (sts&1) // it was a watchdog, stay busy to retry.
			ctlr->txbusy = b;
	} else
		ctlr->ntx++;
}

	outb(LCCR(base), CR0_STATUS_0 | OP0_NOP);
	interrupt_handler(ether, inb(LCSR(base)));
		
	iunlock(&ctlr->wlock);
static int
w_stats(Ctlr* ctlr)
{
	int		sp,i;
	ushort	rc;
	Wltv	l;
	ulong*	p = (ulong*)&ctlr->WStats;
	ulong*	pend= (ulong*)&ctlr->end;

	sp = csr_ins(ctlr, WR_InfoId);
	l.type = l.len = 0;
	w_read(ctlr, sp, 0, &l, 4);
	if (l.type == WTyp_Stats){
		l.len--;
		for (i = 0; i < l.len && p < pend ; i++){
			rc = csr_ins(ctlr, WR_Data1);
			if (rc > 0xf000)
				rc = ~rc & 0xffff;
			p[i] += rc;
		}
		return 0;
	}
	return -1;
}

}; /* wavelan interrupt */
static void
w_intr(Ether *ether)
{
	int 	rc, txid, i;
	Ctlr*	ctlr = (Ctlr*) ether->ctlr;

	if (ctlr->attached == 0){
		csr_ack(ctlr, 0xffff);
		csr_outs(ctlr, WR_IntEna, 0); 
		return;
	}
	for(i=0; i<7; i++){
		csr_outs(ctlr, WR_IntEna, 0);
		rc = csr_ins(ctlr, WR_EvSts);
		csr_ack(ctlr, ~WEvs);	// Not interested on 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++;
			w_stats(ctlr);
			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_intena(ctlr);
		w_txstart(ether,0);
	}
 }

// 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
promiscuous()
w_timer(void* arg)
{
	;
};

static void
multicast()
{	
	;
};
	Ether*	ether = (Ether*) arg;
	Ctlr*	ctlr = (Ctlr*)ether->ctlr;
	int tick=0;

	for(;;){
		tsleep(&ctlr->timer, return0, 0, 50);
		ctlr = (Ctlr*)ether->ctlr;
		if (ctlr == 0)
			break;
		if (ctlr->attached == 0)
			continue;
		tick++;

		ilock(&ctlr->Lock);

		// 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)
				w_intr(ether);

		if (tick % 10 == 0) {
			if (ctlr->txtmout && --ctlr->txtmout == 0){
				ctlr->nwatchdogs++;
				w_txdone(ctlr, WTxErrEv|1); // 1: keep it busy
				if (w_enable(ether))
					DEBUG("wavelan: wdog enable failed\n");
				if (ctlr->txbusy) 
					w_txstart(ether,1);
			}
			if (tick % 120 == 0)
			if (ctlr->txbusy == 0)
				w_cmd(ctlr, WCmdAskStats, WTyp_Stats);
		}
		iunlock(&ctlr->Lock);
	} 
	pexit("terminated",0);
}

static void 
mmc_read(int base, uchar o, uchar *b, int n)
static void*
emalloc(ulong size)
{
	while (n-- > 0) {
		while (inb(HASR(base)) & HASR_MMI_BUSY) ;	/* Wait for MMC to go idle */
		outb(MMR(base), o << 1);			/* Set the read address */
		o++;
	void *r=malloc(size);
	if (!r)
		error(Enomem);
	memset(r,0,size);
	return r;
}

		outb(MMD(base), 0);				/* Required dummy write */
		while (inb(HASR(base)) & HASR_MMI_BUSY) ;	/* Wait for MMC to go idle */
		*b++ = (uchar)(inb(MMD(base)));			/* Now do the actual read */
	}
} /* mmc_read */


static void
fee_wait(int base, int del, int numb)
{	int count = 0;
	while ((count++ < numb) && (mmc_in(base, MMC_EECTRL) & MMR_FEE_STATUS_BUSY))
		delay(del);
	if (count==numb) print("Wavelan: fee wait timed out\n");
multicast(void*, uchar*, int)
{
	// BUG: to be added. 
}

static void 
mmc_write_b(int base, uchar o, uchar b)
static void
attach(Ether* ether)
{
	while (inb(HASR(base)) & HASR_MMI_BUSY) ;	/* Wait for MMC to go idle */
	outb(MMR(base), (uchar)((o << 1) | MMR_MMI_WR));
	outb(MMD(base), (uchar)(b));
} /* mmc_write_b */
	Ctlr*	ctlr;
	char	name[NAMELEN];
	int		rc;

static void 
mmc_write(int base, uchar o, uchar *b, int n)
{
	o += n;
	b += n;
	while (n-- > 0 ) 
		mmc_write_b(base, --o, *(--b));
} /* mmc_write */
	if (ether->ctlr == 0)
		return;

	snprint(name, NAMELEN, "#l%dtimer", ether->ctlrno);
	ctlr = (Ctlr*) ether->ctlr;
	if (ctlr->attached == 0){
		ilock(&ctlr->Lock);
		rc = w_enable(ether);
		iunlock(&ctlr->Lock);
		if(rc == 0){
			ctlr->attached = 1;
			kproc(name, w_timer, ether);
		} else
			print("#l%d: enable failed\n",ether->ctlrno);
	} 
}

static void wavelan_mmc_init(Ether *ether, int port)
static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
	Ctlr *ctlr;
	mmw_t	m;
	ctlr = ether->ctlr;

	memset(&m, 0x00, sizeof(m));

	/*
	* Set default modem control parameters.
	* See NCR document 407-0024326 Rev. A.
	*/
	m.mmw_jabber_enable = 0x01;
	m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
	m.mmw_ifs = 0x20;
	m.mmw_mod_delay = 0x04;
	m.mmw_jam_time = 0x38;
	m.mmw_encr_enable = 0;
	m.mmw_des_io_invert = 0;
	m.mmw_freeze = 0;
	m.mmw_decay_prm = 0;
	m.mmw_decay_updat_prm = 0;
	m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
	m.mmw_thr_pre_set = 0x04;   /* PCMCIA */
	m.mmw_quality_thr = 0x03;
	m.mmw_netw_id_l = ctlr->nwid[1];		/* use nwid of PSA memory */
	m.mmw_netw_id_h = ctlr->nwid[0];
  
	mmc_write(port, 0, (uchar *)&m, 37); 		/* size of mmw_t == 37 */

	/* Start the modem's receive unit on version 2.00 		     */
						/* 2.4 Gz: half-card ver     */
						/* 2.4 Gz		     */
						/* 2.4 Gz: position ch #     */
	mmc_write_b(port, MMC_EEADDR, 0x0f);	/* 2.4 Gz: named ch, wc=16   */
	mmc_write_b(port, MMC_EECTRL,MMC_EECTRL_DWLD |	/* 2.4 Gz: Download Synths   */
			MMC_EECTRL_EEOP_READ);	/* 2.4 Gz: Read EEPROM	     */
	fee_wait(port, 10, 100);		/* 2.4 Gz: wait for download */
						/* 2.4 Gz	      */
	mmc_write_b(port, MMC_EEADDR,0x61);		/* 2.4 Gz: default pwr, wc=2 */
	mmc_write_b(port, MMC_EECTRL,MMC_EECTRL_DWLD |	/* 2.4 Gz: Download Xmit Pwr */
			MMC_EECTRL_EEOP_READ);	/* 2.4 Gz: Read EEPROM	     */
	fee_wait(port, 10, 100);		/* 2.4 Gz: wait for download */

} /* wavelan_mmc_init */


int wavelan_diag(Ether *ether, int port)
{
  	if (wavelan_cmd(ether, port, "wavelan_diag(): diagnose", OP0_DIAGNOSE,
		  SR0_DIAGNOSE_PASSED)){
	Ctlr*	ctlr = (Ctlr*) ether->ctlr;
	char*	p;
	int		l;

	if (n == 0 || ctlr == 0){
		return 0;
	}
	print("wavelan_cs: i82593 Self Test failed!\n");
	return 1;
} /* wavelan_diag */



int wavelan_hw_config(int base, Ether *ether)
{
	struct i82593_conf_block cfblk;

	memset(&cfblk, 0x00, sizeof(struct i82593_conf_block));
	cfblk.d6mod = FALSE;	/* Run in i82593 advanced mode */
	cfblk.fifo_limit = 6;	/* = 48 bytes rx and tx fifo thresholds */
	cfblk.forgnesi = FALSE;	/* 0=82C501, 1=AMD7992B compatibility */
	cfblk.fifo_32 = 0;
	cfblk.throttle_enb = TRUE;
	cfblk.contin = TRUE;	/* enable continuous mode */
	cfblk.cntrxint = FALSE;	/* enable continuous mode receive interrupts */
	cfblk.addr_len = WAVELAN_ADDR_SIZE;
	cfblk.acloc = TRUE;	/* Disable source addr insertion by i82593 */
	cfblk.preamb_len = 2;	/* 7 byte preamble */
	cfblk.loopback = FALSE;
	cfblk.lin_prio = 0;	/* conform to 802.3 backoff algoritm */
	cfblk.exp_prio = 0;	/* conform to 802.3 backoff algoritm */
	cfblk.bof_met = 0;	/* conform to 802.3 backoff algoritm */
	cfblk.ifrm_spc = 6;	/* 96 bit times interframe spacing */
	cfblk.slottim_low = 0x10 & 0x7;	/* 512 bit times slot time */
	cfblk.slottim_hi = 0x10 >> 3;
	cfblk.max_retr = 15;	
	cfblk.prmisc = FALSE;	/* Promiscuous mode ?? */
	cfblk.bc_dis = FALSE;	/* Enable broadcast reception */
	cfblk.crs_1 = TRUE;	/* Transmit without carrier sense */
	cfblk.nocrc_ins = FALSE; /* i82593 generates CRC */	
	cfblk.crc_1632 = FALSE;	/* 32-bit Autodin-II CRC */
 	cfblk.crs_cdt = FALSE;	/* CD not to be interpreted as CS */
	cfblk.cs_filter = 0;  	/* CS is recognized immediately */
	cfblk.crs_src = FALSE;	/* External carrier sense */
	cfblk.cd_filter = 0;  	/* CD is recognized immediately */
	cfblk.min_fr_len = 64 >> 2;	/* Minimum frame length 64 bytes */
	cfblk.lng_typ = FALSE;	/* Length field > 1500 = type field */
	cfblk.lng_fld = TRUE; 	/* Disable 802.3 length field check */
	cfblk.rxcrc_xf = TRUE;	/* Don't transfer CRC to memory */
	cfblk.artx = TRUE;	/* Disable automatic retransmission */
	cfblk.sarec = TRUE;	/* Disable source addr trig of CD */
	cfblk.tx_jabber = TRUE;	/* Disable jabber jam sequence */
	cfblk.hash_1 = FALSE; 	/* Use bits 0-5 in mc address hash */
	cfblk.lbpkpol = TRUE; 	/* Loopback pin active high */
	cfblk.fdx = FALSE;	/* Disable full duplex operation */
	cfblk.dummy_6 = 0x3f; 	/* all ones */
	cfblk.mult_ia = FALSE;	/* No multiple individual addresses */
	cfblk.dis_bof = FALSE;	/* Disable the backoff algorithm ?! */
	cfblk.dummy_1 = TRUE; 	/* set to 1 */
	cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */
	cfblk.mc_all = FALSE;	/* No multicast all mode */	
	cfblk.rcv_mon = 0;	/* Monitor mode disabled */
	cfblk.frag_acpt = TRUE;/* Do not accept fragments */
	cfblk.tstrttrs = FALSE;	/* No start transmission threshold */
	cfblk.fretx = TRUE;	/* FIFO automatic retransmission */
	cfblk.syncrqs = TRUE; 	/* Synchronous DRQ deassertion... */
	cfblk.sttlen = TRUE;  	/* 6 byte status registers */
	cfblk.rx_eop = TRUE;  	/* Signal EOP on packet reception */
	cfblk.tx_eop = TRUE;  	/* Signal EOP on packet transmission */
	cfblk.rbuf_size = RX_SIZE>>11;	/* Set receive buffer size */
	cfblk.rcvstop = TRUE; 	/* Enable Receive Stop Register */

	outb(PIORL(base), (TX_BASE & 0xff));
	outb(PIORH(base), (((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX));
	outb(PIOP(base), (sizeof(struct i82593_conf_block) & 0xff));    /* lsb */
	outb(PIOP(base), (sizeof(struct i82593_conf_block) >> 8));	/* msb */
	outsb(PIOP(base), ((char *) &cfblk), sizeof(struct i82593_conf_block));

	/* reset transmit DMA pointer */
	hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
	outb(HACR(base), HACR_DEFAULT);
	if(!wavelan_cmd(ether, base, "wavelan_hw_config(): configure", OP0_CONFIGURE,
		  SR0_CONFIGURE_DONE))
		return(FALSE);

	/* Initialize adapter's ethernet MAC address */
	outb(PIORL(base), (TX_BASE & 0xff));
	outb(PIORH(base), (((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX));
	outb(PIOP(base), WAVELAN_ADDR_SIZE);	/* byte count lsb */
	outb(PIOP(base), 0);			/* byte count msb */
	outsb(PIOP(base), &ether->ea[0], WAVELAN_ADDR_SIZE);
	/* reset transmit DMA pointer */
	hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
	outb(HACR(base), HACR_DEFAULT);
	if(!wavelan_cmd(ether, base, "wavelan_hw_config(): ia-setup", OP0_IA_SETUP, SR0_IA_SETUP_DONE))
		return(FALSE);
	return(TRUE);
} /* wavelan_hw_config */
	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");

	// 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));
	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);
	n = readstr(offset, a, n, p);
	free(p);
	return n;
}

static void 
wavelan_graceful_shutdown(Ether *ether, int base)
{
  int status;
  
  /* First, send the LAN controller a stop receive command */
  wavelan_cmd(ether, base, "wavelan_graceful_shutdown(): stop-rcv", OP0_STOP_RCV,
	      SR0_NO_RESULT);
  /* Then, spin until the receive unit goes idle */
  do {
    outb(LCCR(base), (OP0_NOP | CR0_STATUS_3));
    status = inb(LCSR(base));
  } while((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE);
		       
  /* Now, spin until the chip finishes executing its current command */
  do {
    outb(LCCR(base), (OP0_NOP | CR0_STATUS_3));
    status = inb(LCSR(base));
  } while ((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE);
} /* wavelan_graceful_shutdown */
/* 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 
wavelan_ru_start(Ether *ether, int base)
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 
ctl(Ether* ether, void* buf, long n)
{
	int i;
	char *p;
	Ctlr *ctlr;
	ctlr = ether->ctlr;
	Cmdbuf *cb;

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

	cb = parsecmd(buf, n);
	if(cb->nf < 2)
		error(Ebadctl);

	ilock(&ctlr->Lock);
	if(waserror()){
		iunlock(&ctlr->Lock);
		free(cb);
		nexterror();
	}

	/*
	* We need to start from a quiescent state. To do so, we could check
	* if the card is already running, but instead we just try to shut
	* it down. First, we disable reception (in case it was already enabled).
	*/
	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)
		strncpy(ctlr->nodename, cb->f[1], WNameLen);
	else if(strcmp(cb->f[0], "chan") == 0){
		i = atoi(cb->f[1]);
		if (i < 1 || i > 16 )
			error("invalid wavelan channel");
		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");
		ctlr->ptype = i;
	}
	else
		error(Ebadctl);
	if(ctlr->txbusy)
		w_txdone(ctlr, WTxErrEv|1);	// retry later.
	w_enable(ether);

	wavelan_graceful_shutdown(ether, base);
	iunlock(&ctlr->Lock);
	poperror();
	free(cb);

	/* Now we know that no command is being executed. */
	return n;
}

	/* Set the receive frame pointer and stop pointer */
	ctlr->rfp = 0;
	outb(LCCR(base), OP0_SWIT_TO_PORT_1 | CR0_CHNL);
static void
transmit(Ether* ether)
{
	Ctlr* ctlr = ether->ctlr;

	/* Reset ring management.  This sets the receive frame pointer to 1 */
	outb(LCCR(base), OP1_RESET_RING_MNGMT);
	ctlr->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
	outb(LCCR(base), CR1_STOP_REG_UPDATE | (ctlr->stop >> RX_SIZE_SHIFT));
	outb(LCCR(base), OP1_INT_ENABLE);
	outb(LCCR(base), OP1_SWIT_TO_PORT_0);
	if (ctlr == 0)
		return;

	/* Reset receive DMA pointer */
	outb(HACR(base), HACR_PWR_STAT | HACR_RX_DMA_RESET);
	delay(100);
	outb(HACR(base), HACR_PWR_STAT);
	delay(100);
	ilock(&ctlr->Lock);
	ctlr->ntxrq++;
	w_txstart(ether,0);
	iunlock(&ctlr->Lock);
}

	/* Receive DMA on channel 1 */
	wavelan_cmd(ether, base, "wavelan_ru_start(): rcv-enable",
	      (CR0_CHNL | OP0_RCV_ENABLE), SR0_NO_RESULT);
static void
promiscuous(void* arg, int on)
{
	Ether*	ether = (Ether*)arg;
	Ctlr*	ctlr = ether->ctlr;

	if (ctlr == nil)
		error("card not found");
	if (ctlr->attached == 0)
		error("card not attached");
	ilock(&ctlr->Lock);
	ltv_outs(ctlr, WTyp_Prom, (on?1:0));
	iunlock(&ctlr->Lock);
}

} /* wavelan_ru_start */

static void
transmit(Ether* ether)

static void 
interrupt(Ureg* ,void* arg)
{
	Ctlr *ctlr;
	ctlr = ether->ctlr;
	Ether*	ether = (Ether*) arg;
	Ctlr*	ctlr = (Ctlr*) ether->ctlr;

	ilock(&ctlr->wlock);
	txstart(ether);	
	iunlock(&ctlr->wlock);
	if (ctlr == 0)
		return;
	ilock(&ctlr->Lock);
	ctlr->nints++;
	w_intr(ether);
	iunlock(&ctlr->Lock);
}


static int
reset(Ether* ether)
{
	int slot, p;
	int port;
	char *pp;
	Ctlr *ctlr;
	PCMmap *m;

	ctlr = ether->ctlr = malloc(sizeof(Ctlr)); 
	ilock(&ctlr->wlock);
	Ctlr*	ctlr;
	Wltv	ltv;
	int		i;

	if(ether->port == 0)
		ether->port = 0x280;
	port = ether->port;

	if((slot = pcmspecial(ether->type, ether)) < 0) {
		print("could not find the PCMCIA WaveLAN card.\n"); 
	if (ether->ctlr){
		print("#l%d: only one card supported\n", ether->ctlrno);
		return -1;
	}

	if(ioalloc(port, 0x20, 0, "wavelan") < 0){
		print("wavelan: port %d in use\n", port);
		return -1;
	ether->arg = ctlr = (Ctlr*)emalloc(sizeof(Ctlr));
	ilock(&ctlr->Lock);
	
	if (ether->port==0)
		ether->port=WDfltIOB;
	ctlr->iob = ether->port;
	if (ether->irq==0)
		ether->irq=WDfltIRQ;
	if ((ctlr->slot = pcmspecial("WaveLAN/IEEE", ether))<0){
		DEBUG("no wavelan found\n");
		goto abort;
	}

	print("#l%dWaveLAN: slot %d, port 0x%ulX irq %ld type %s\n", ether->ctlrno, slot, ether->port, ether->irq, ether->type);

	/* create a receive buffer */
	ctlr->rbp = rbpalloc(allocb);

/* map a piece of memory (Attribute memory) first */
	m = pcmmap(slot, 0, 0x5000, 1);
	if (m==0) {
		iofree(port);
		return 1;
	DEBUG("#l%d: port=0x%lx irq=%ld\n", 
			ether->ctlrno, ether->port, ether->irq);
 
	*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);
	}
/* read ethernet address from the card and put in ether->ea */
	pp = (char*)(KZERO|m->isa) + 0x0E00 + 2*0x10;
	for(p = 0; p<sizeof(ether->ea); p++)
		ether->ea[p] = (uchar) (*(pp+2*p))&0xFF;

//	print("wavelan: dump of PSA memory\n");
//	pp = (char *) (KZERO|m->isa) + 0x0E00;
//	for(p=0; p<64; p++) {
//		print("%2uX ", (*(pp+2*p)&0xFF));
//		if (p%16==15) print("\n");
//	}

/* read nwid from PSA into ctlr->nwid */
	pp = (char*)(KZERO|m->isa) + 0x0E00;
	ctlr->nwid[0] = *(pp+2*0x23); 
	ctlr->nwid[1] = *(pp+2*0x24); 
	
/* access the configuration option register 	*/
	pp = (char *)(KZERO|m->isa) + 0x4000;	
	*pp = *pp | COR_SW_RESET; 		
	delay(5); 				
	*pp = (COR_LEVEL_IRQ | COR_CONFIG); 	
	delay(5); 				

	hacr_write_slow(port, HACR_RESET);
	outb(HACR(port), HACR_DEFAULT);

	if(inb(HASR(port)) & HASR_NO_CLK) {
		iofree(port);
		print("wavelan: modem not connected\n");
		return 1;
	ctlr->netname[WNameLen-1] = 0;
	ctlr->wantname[WNameLen-1] = 0;
	ctlr->nodename[WNameLen-1] =0;

	if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
		print("#l%d: port 0x%lx in use\n", 
				ether->ctlrno, ether->port);
		goto abort;
	}

	wavelan_mmc_init(ether, port);		/* initialize modem */

	outb(LCCR(port), OP0_RESET);	/* reset the LAN controller */
	delay(10);

	if (wavelan_hw_config(port, ether) == FALSE){
		iofree(port);
		return 1;
	w_intdis(ctlr);
	if (w_cmd(ctlr,WCmdIni,0)){
		print("#l%d: init failed\n", ether->ctlrno);
		goto abort;
	}

	if (wavelan_diag(ether, port) == 1){
		iofree(port);
		return 1;
	w_intdis(ctlr);
	ltv_outs(ctlr, WTyp_Tick, 8);

	ltv.type = WTyp_Mac;
	ltv.len	= 4;
	if (w_inltv(ctlr, &ltv)){
		print("#l%d: unable to read mac addr\n", 
			ether->ctlrno);
		goto abort;
	}
	wavelan_ru_start(ether, port);

	print("wavelan: init done; receiver started\n");

	iunlock(&ctlr->wlock);	

	ctlr->port = port;
	ether->port = port;
	ether->mbps = 2;		/* 2 Mpbs */
	memmove(ether->ea, ltv.addr, Eaddrlen);
	DEBUG("#l%d: %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", 
			ether->ctlrno,
			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;
	ctlr->apdensity = WDfltApDens;
	ctlr->rtsthres = WDfltRtsThres;
	ctlr->txrate = WDfltTxRate;
	ctlr->maxlen = WMaxLen;
	ctlr->pmena = 0;
	ctlr->pmwait= 100;
	// link to ether
	ether->ctlr = ctlr;
	ether->mbps = 10;	
	ether->attach = attach;
	ether->transmit = transmit;
	ether->interrupt = interrupt;
	ether->transmit = transmit;
	ether->ifstat = ifstat;

	ether->ctl = ctl;
	ether->promiscuous = promiscuous;
	ether->multicast = multicast;
	ether->arg = ether;

	return 0;			/* reset succeeded */
	DEBUG("#l%d: irq %ld port %lx type %s",
		ether->ctlrno, ether->irq, ether->port,	ether->type);
	DEBUG(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n",
		ether->ea[0], ether->ea[1], ether->ea[2],
		ether->ea[3], ether->ea[4], ether->ea[5]);

	iunlock(&ctlr->Lock);
	return 0; 

abort:
	iunlock(&ctlr->Lock);
	free(ctlr);
	ether->ctlr = nil;
	return -1;
}

void
etherwavelanlink(void)
{
	addethercard("WaveLAN", reset);
	addethercard("wavelan", reset);
}