~kris/9p

9hist

a10c2ddd6cbfc8c9f4d97ef189cc6d18fba74838 — David du Colombier 33 years ago f269e91
Plan 9 from Bell Labs 1993-02-12
9 files changed, 926 insertions(+), 475 deletions(-)

M pc/devether.c
M pc/ether.h
M pc/ether2000.c
M pc/ether503.c
M pc/ether509.c
M pc/ether8003.c
M pc/ether8390.c
M pc/l.s
M power/trap.c
M pc/devether.c => pc/devether.c +51 -30
@@ 15,20 15,20 @@
 *	handle multiple controllers
 *	much tidying
 *	set ethernet address
 *	need a ctl file passed down to card drivers
 *	  so we can set options.
 */
extern Board ether8003;
extern Board ether503;
extern Board ether2000;
extern Board ether509;
extern Card ether8003, ether503, ether2000;
extern Card ether509;

/*
 * The ordering here is important for those boards
 * using the DP8390 (WD8003, 3COM503 and NE2000) as
 * attempting to determine if a board is a NE2000
 * The ordering here is important for those cards
 * using the DP8390 (WD8003, 3Com503 and NE2000) as
 * attempting to determine if a card is a NE2000
 * cannot be done passively, so it must be last to
 * prevent scrogging one of the others.
 */
static Board *boards[] = {
static Card *cards[] = {
	&ether8003,
	&ether503,
	&ether2000,


@@ 41,7 41,7 @@ enum {
	NCtlr		= 1,
};

static struct Ctlr *softctlr[NCtlr];
/*static */struct Ctlr *softctlr[NCtlr];
static int nctlr;

Chan*


@@ 123,7 123,7 @@ etheroput(Queue *q, Block *bp)
	Type *type;
	Etherpkt *pkt;
	RingBuf *ring;
	int len, n;
	int len, n, s;
	Block *nbp;

	type = q->ptr;


@@ 141,7 141,7 @@ etheroput(Queue *q, Block *bp)
			type->prom = 1;
			ctlr->prom++;
			if(ctlr->prom == 1)
				(*ctlr->board->mode)(ctlr, 1);
				(*ctlr->card.mode)(ctlr, 1);
		}
		qunlock(ctlr);
		freeb(bp);


@@ 179,8 179,8 @@ etheroput(Queue *q, Block *bp)
	 */
	qlock(&ctlr->tlock);
	if(waserror()){
		freeb(bp);
		qunlock(&ctlr->tlock);
		freeb(bp);
		nexterror();
	}



@@ 188,7 188,15 @@ etheroput(Queue *q, Block *bp)
	 * Wait till we get an output buffer.
	 * should try to restart.
	 */
	sleep(&ctlr->tr, isobuf, ctlr);
	if(isobuf(ctlr) == 0){
		tsleep(&ctlr->tr, isobuf, ctlr, 3*1000);
		if(isobuf(ctlr) == 0){
			qunlock(&ctlr->tlock);
			freeb(bp);
			poperror();
			return;
		}
	}

	ring = &ctlr->tb[ctlr->th];



@@ 217,14 225,15 @@ etheroput(Queue *q, Block *bp)
	 * Set up the transmit buffer and 
	 * start the transmission.
	 */
	ctlr->outpackets++;
	s = splhi();
	ring->len = len;
	ring->owner = Interface;
	ctlr->th = NEXT(ctlr->th, ctlr->ntb);
	(*ctlr->board->transmit)(ctlr);
	(*ctlr->card.transmit)(ctlr);
	splx(s);

	freeb(bp);
	qunlock(&ctlr->tlock);
	freeb(bp);
	poperror();
}



@@ 267,7 276,7 @@ etherstclose(Queue *q)
		qlock(ctlr);
		ctlr->prom--;
		if(ctlr->prom == 0)
			(*ctlr->board->mode)(ctlr, 0);
			(*ctlr->card.mode)(ctlr, 0);
		qunlock(ctlr);
	}
	if(type->type == -1){


@@ 407,8 416,8 @@ etherkproc(void *arg)
	if(waserror()){
		print("%s noted\n", ctlr->name);
		/* fix
		if(ctlr->board->reset)
			(*ctlr->board->reset)(ctlr);
		if(ctlr->card.reset)
			(*ctlr->card.reset)(ctlr);
		 */
		ctlr->kproc = 0;
		nexterror();


@@ 416,8 425,8 @@ etherkproc(void *arg)

	for(;;){
		tsleep(&ctlr->rr, isinput, ctlr, 500);
		if(ctlr->board->watch)
			(*ctlr->board->watch)(ctlr);
		if(ctlr->card.watch)
			(*ctlr->card.watch)(ctlr);

		/*
		 * Process any internal loopback packets.


@@ 460,22 469,25 @@ etherintr(Ureg *ur)
	Ctlr *ctlr = softctlr[0];

	USED(ur);
	(*ctlr->board->intr)(ctlr);
	(*ctlr->card.intr)(ctlr);
}

void
etherreset(void)
{
	Ctlr *ctlr;
	Board **board;
	Card **card;
	int i;

	if(nctlr >= NCtlr)
		return;
	if(softctlr[nctlr] == 0)
		softctlr[nctlr] = xalloc(sizeof(Ctlr));
	ctlr = softctlr[nctlr];
	for(board = boards; *board; board++){
		ctlr->board = *board;
		if((*ctlr->board->reset)(ctlr) == 0){
	for(card = cards; *card; card++){
		memset(ctlr, 0, sizeof(Ctlr));
		ctlr->card = **card;
		if((*ctlr->card.reset)(ctlr) == 0){
			ctlr->present = 1;

			/*


@@ 483,14 495,23 @@ etherreset(void)
			 * controllers together. A device set to IRQ2 will appear on
			 * the second interrupt controller as IRQ9.
			 */
			if(ctlr->board->irq == 2)
				ctlr->board->irq = 9;
			setvec(Int0vec + ctlr->board->irq, etherintr);
			if(ctlr->card.irq == 2)
				ctlr->card.irq = 9;
			setvec(Int0vec + ctlr->card.irq, etherintr);
			break;
		}
	}
	if(ctlr->present == 0)
		return;

	print("ether%d: %s: I/O addr %lux width %d addr %lux size %d irq %d:",
		nctlr, ctlr->card.id,
		ctlr->card.io, ctlr->card.bit16 ? 16: 8, ctlr->card.ramstart,
		ctlr->card.ramstop-ctlr->card.ramstart, ctlr->card.irq);
	for(i = 0; i < sizeof(ctlr->ea); i++)
		print(" %2.2ux", ctlr->ea[i]);
	print("\n");

	nctlr++;

	if(ctlr->nrb == 0)


@@ 551,7 572,7 @@ etherattach(char *spec)
	 * Enable the interface
	 * and start the kproc.
	 */	
	(*ctlr->board->attach)(ctlr);
	(*ctlr->card.attach)(ctlr);
	if(ctlr->kproc == 0){
		sprint(ctlr->name, "ether%dkproc", ctlrno);
		ctlr->kproc = 1;

M pc/ether.h => pc/ether.h +69 -43
@@ 1,7 1,7 @@
/*
 * All the goo for PC ethernet cards.
 */
typedef struct Board Board;
typedef struct Card Card;
typedef struct RingBuf RingBuf;
typedef struct Type Type;
typedef struct Ctlr Ctlr;


@@ 9,27 9,34 @@ typedef struct Ctlr Ctlr;
/*
 * Hardware interface.
 */
struct Board {
struct Card {
	char	id[NAMELEN];		/* type of card */

	int	(*reset)(Ctlr*);
	void	(*init)(Ctlr*);
	void	(*attach)(Ctlr*);
	void	(*mode)(Ctlr*, int);

	void	*(*read)(Ctlr*, void*, ulong, ulong);
	void	*(*write)(Ctlr*, ulong, void*, ulong);

	void	(*receive)(Ctlr*);
	void	(*transmit)(Ctlr*);
	void	(*intr)(Ctlr*);
	void	(*watch)(Ctlr*);
	void	(*overflow)(Ctlr*);

	ulong	io;			/* interface I/O base address */
	ulong	io;			/* card I/O base address */
	uchar	irq;			/* interrupt level */
	uchar	bit16;			/* true if a 16 bit interface */

	uchar	ram;			/* true if interface has shared memory */
	ulong	ramstart;		/* interface shared memory address start */
	ulong	ramstop;		/* interface shared memory address end */
	uchar	ram;			/* true if card has shared memory */
	ulong	ramstart;		/* card shared memory address start */
	ulong	ramstop;		/* card shared memory address end */

	ulong	dp8390;			/* I/O address of 8390 (if any) */
	ulong	data;			/* I/O data port if no shared memory */
	uchar	nxtpkt;			/* software bndry */
	uchar	tstart;			/* 8390 ring addresses */
	uchar	pstart;
	uchar	pstop;


@@ 40,17 47,17 @@ struct Board {
 */
struct RingBuf {
	uchar	owner;
	uchar	busy;
	uchar	busy;			/* unused */
	ushort	len;
	uchar	pkt[sizeof(Etherpkt)];
};

enum {
	Host		= 0,	/* buffer owned by host */
	Interface	= 1,	/* buffer owned by interface */
	Host		= 0,		/* buffer owned by host */
	Interface	= 1,		/* buffer owned by card */

	Nrb		= 16,	/* default number of receive buffers */
	Ntb		= 4,	/* default number of transmit buffers */
	Nrb		= 16,		/* default number of receive buffers */
	Ntb		= 4,		/* default number of transmit buffers */
};

/*


@@ 58,19 65,19 @@ enum {
 */
struct Type {
	QLock;
	Netprot;		/* stat info */
	int	type;		/* ethernet type */
	int	prom;		/* promiscuous mode */
	Netprot;			/* stat info */
	int	type;			/* ethernet type */
	int	prom;			/* promiscuous mode */
	Queue	*q;
	int	inuse;
	Ctlr	*ctlr;

	Rendez	cr;		/* rendezvous for close */
	Type	*clist;		/* close list */
	Rendez	cr;			/* rendezvous for close */
	Type	*clist;			/* close list */
};

enum {
	NType		= 9,	/* types/interface */
	NType		= 9,		/* types/card */
};

/*


@@ 79,44 86,46 @@ enum {
struct Ctlr {
	QLock;

	Board	*board;
	Card	card;			/* hardware info */
	int	present;
	int	debug;

	ushort	nrb;		/* number of software receive buffers */
	ushort	ntb;		/* number of software transmit buffers */
	RingBuf *rb;		/* software receive buffers */
	RingBuf *tb;		/* software transmit buffers */
	ushort	nrb;			/* number of software receive buffers */
	ushort	ntb;			/* number of software transmit buffers */
	RingBuf *rb;			/* software receive buffers */
	RingBuf *tb;			/* software transmit buffers */

	uchar	ea[6];		/* ethernet address */
	uchar	ba[6];		/* broadcast address */
	uchar	ea[6];			/* ethernet address */
	uchar	ba[6];			/* broadcast address */

	Rendez	rr;		/* rendezvous for a receive buffer */
	ushort	rh;		/* first receive buffer belonging to host */
	ushort	ri;		/* first receive buffer belonging to interface */	
	Rendez	rr;			/* rendezvous for a receive buffer */
	ushort	rh;			/* first receive buffer belonging to host */
	ushort	ri;			/* first receive buffer belonging to card */	

	Rendez	tr;		/* rendezvous for a transmit buffer */
	QLock	tlock;		/* semaphore on th */
	ushort	th;		/* first transmit buffer belonging to host */	
	ushort	ti;		/* first transmit buffer belonging to interface */	
	Rendez	tr;			/* rendezvous for a transmit buffer */
	QLock	tlock;			/* semaphore on th */
	ushort	th;			/* first transmit buffer belonging to host */	
	ushort	ti;			/* first transmit buffer belonging to card */
	int	tbusy;			/* transmitter is busy */	

	Type	type[NType];
	int	all;		/* number of channels listening to all packets */
	Type	*clist;		/* channels waiting to close */
	Lock	clock;		/* lock for clist */
	int	prom;		/* number of promiscuous channels */
	int	kproc;		/* true if kproc started */
	char	name[NAMELEN];	/* name of kproc */
	int	all;			/* number of channels listening to all packets */
	Type	*clist;			/* channels waiting to close */
	Lock	clock;			/* lock for clist */
	int	prom;			/* number of promiscuous channels */
	int	kproc;			/* true if kproc started */
	char	name[NAMELEN];		/* name of kproc */
	Network	net;

	Queue	lbq;		/* software loopback packet queue */
	Queue	lbq;			/* software loopback packet queue */

	int	inpackets;
	int	outpackets;
	int	crcs;		/* input crc errors */
	int	oerrs;		/* output errors */
	int	frames;		/* framing errors */
	int	overflows;	/* packet overflows */
	int	buffs;		/* buffering errors */
	int	crcs;			/* input crc errors */
	int	oerrs;			/* output errors */
	int	frames;			/* framing errors */
	int	overflows;		/* packet overflows */
	int	buffs;			/* buffering errors */
};

#define NEXT(x, l)	(((x)+1)%(l))


@@ 133,9 142,26 @@ extern void dp8390attach(Ctlr*);
extern void dp8390mode(Ctlr*, int);
extern void dp8390setea(Ctlr*);
extern void *dp8390read(Ctlr*, void*, ulong, ulong);
extern void *dp8390write(Ctlr*, ulong, void*, ulong);
extern void dp8390receive(Ctlr*);
extern void dp8390transmit(Ctlr*);
extern void dp8390intr(Ctlr*);
extern void dp8390watch(Ctlr*);
extern void dp8390overflow(Ctlr*);

extern void dp8390debug(Ctlr*);

/*
 * The DP8390 needs some time between successive 
 * chip selects, so we need special I/O routines.
 * See l.s.
 * These routines only need to be used for accessing
 * the chip registers. Data I/O, either through
 * remote DMA or shared memory, can use the normal
 * routines.
 */
extern int dp8390inb(ulong);
extern void dp8390outb(ulong, uchar);

enum {
	Dp8390BufSz	= 256,		/* hardware ring buffer size */

M pc/ether2000.c => pc/ether2000.c +36 -37
@@ 15,9 15,9 @@
 * laptop. The manual says NE2000 compatible.
 * The interface appears to be pretty well described in the National
 * Semiconductor Local Area Network Databook (1992) as one of the
 * AT evaluation boards.
 * AT evaluation cards.
 *
 * The NE2000 is really just a DP8390 plus a data port
 * The NE2000 is really just a DP83901 plus a data port
 * and a reset port.
 */
enum {


@@ 28,7 28,6 @@ enum {
static int
reset(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	ushort buf[16];
	int i;



@@ 36,12 35,12 @@ reset(Ctlr *ctlr)
	 * We look for boards.
	 * We look for boards to make us barf.
	 */
	for(board->io = 0x300; board->io < 0x380; board->io += 0x20){
	for(ctlr->card.io = 0x300; ctlr->card.io < 0x380; ctlr->card.io += 0x20){
		/*
		 * Reset the board. This is done by doing a read
		 * followed by a write to the Reset address.
		 */
		outb(board->io+Reset, inb(board->io+Reset));
		outb(ctlr->card.io+Reset, inb(ctlr->card.io+Reset));

		/*
		 * Init the (possible) chip, then use the (possible)


@@ 52,17 51,16 @@ reset(Ctlr *ctlr)
		 * enough, there are other ethernet boards which could
		 * match.
		 */
		board->dp8390 = board->io;
		board->data = board->io+Data;
		ctlr->card.dp8390 = ctlr->card.io;
		ctlr->card.data = ctlr->card.io+Data;
		dp8390reset(ctlr);
		memset(buf, 0, sizeof(buf));
		dp8390read(ctlr, buf, 0, sizeof(buf));
		if(buf[0x0E] == 0x57 && buf[0x0F] == 0x57)
		if((buf[0x0E] & 0xFF) == 0x57 && (buf[0x0F] & 0xFF) == 0x57)
			break;
	}
	if(board->io >= 0x380)
	if(ctlr->card.io >= 0x380)
		return -1;

	/*
	 * Stupid machine. We asked for shorts, we got shorts,
	 * although the PROM is a byte array.


@@ 72,37 70,38 @@ reset(Ctlr *ctlr)
		ctlr->ea[i] = buf[i];
	dp8390setea(ctlr);

	print("NE2000 I/O addr %lux width %d addr %lux size %d irq %d:",
		board->io, board->bit16 ? 16: 8, board->ramstart,
		board->ramstop-board->ramstart, board->irq);
	for(i = 0; i < sizeof(ctlr->ea); i++)
		print(" %2.2ux", ctlr->ea[i]);
	print("\n");

	return 0;
}

Board ether2000 = {
	reset,
	0,			/* init */
	dp8390attach,
	dp8390mode,
	dp8390receive,
	dp8390transmit,
	dp8390intr,
	0,			/* watch */
Card ether2000 = {
	"NE2000",			/* ident */

	reset,				/* reset */
	0,				/* init */
	dp8390attach,			/* attach */
	dp8390mode,			/* mode */

	dp8390read,			/* read */
	dp8390write,			/* write */

	dp8390receive,			/* receive */
	dp8390transmit,			/* transmit */
	dp8390intr,			/* interrupt */
	dp8390watch,			/* watch */
	dp8390overflow,			/* overflow */

	0x300,			/* addr */
	2,			/* irq */
	1,			/* bit16 */
	0x300,				/* addr */
	2,				/* irq */
	1,				/* bit16 */

	0,			/* ram */
	0x4000,			/* ramstart */
	0x4000+16*1024,		/* ramstop */
	0,				/* ram */
	0x4000,				/* ramstart */
	0x4000+16*1024,			/* ramstop */

	0x300,			/* dp8390 */
	0x300+Data,		/* data */
	0x4000/Dp8390BufSz,	/* tstart */
	0x4600/Dp8390BufSz,	/* pstart */
	0x8000/Dp8390BufSz,	/* pstop */
	0x300,				/* dp8390 */
	0x300+Data,			/* data */
	0,				/* software bndry */
	0x4000/Dp8390BufSz,		/* tstart */
	0x4600/Dp8390BufSz,		/* pstart */
	0x8000/Dp8390BufSz,		/* pstop */
};

M pc/ether503.c => pc/ether503.c +321 -25
@@ 9,35 9,331 @@

#include "ether.h"

/*
 * 3Com 503 (EtherLink II).
 * This driver doesn't work under load, the card gets blasted by
 * overwrite-warning interrupts and eventually just hangs. Always clearing
 * the Ovw bit in the DP8390 Isr along with the Rxe and Prx bits stops
 * the overwrite-warning interrupts, but the card eventually hangs anyway.
 * I really don't understand why this card doesn't work,
 * there is plenty of evidence that other systems manage to use it.
 * The only good thing about this driver is that both shared
 * memory and polled I/O work (given the above restraints), although
 * the polled I/O should be tuned.
 */
enum {					/* Gate Array Registers */
	Pstr		= 0x400,	/* Page Start Register */
	Pspr		= 0x401,	/* Page Stop Register */
	Dqtr		= 0x402,	/* Drq Timer Register */
	Bcfr		= 0x403,	/* Base Configuration Register */
	Pcfr		= 0x404,	/* EPROM Configuration Register */
	Gacfr		= 0x405,	/* GA Configuration Register */
	Ctrl		= 0x406,	/* Control Register */
	Streg		= 0x407,	/* Status Register */
	Idcfr		= 0x408,	/* Interrupt/DMA Configuration Register */
	Damsb		= 0x409,	/* DMA Address Register MSB */
	Dalsb		= 0x40A,	/* DMA Address Register LSB */
	Vptr2		= 0x40B,	/* Vector Pointer Register 2 */
	Vptr1		= 0x40C,	/* Vector Pointer Register 1 */
	Vptr0		= 0x40D,	/* Vector Pointer Register 0 */
	Rfmsb		= 0x40E,	/* Register File Access MSB */
	Rflsb		= 0x40F,	/* Register File Access LSB */
};

enum {					/* Pcfr */
	C8xxx		= 0x10,		/* memory address 0xC8000 */
	CCxxx		= 0x20,		/* memory address 0xCC000 */
	D8xxx		= 0x40,		/* memory address 0xD8000 */
	DCxxx		= 0x80,		/* memory address 0xDC000 */
};

enum {					/* Gacfr */
	Mbs0		= 0x01,		/* memory bank select 0 */
	Mbs1		= 0x02,		/* memory bank select 1 */
	Mbs2		= 0x04,		/* memory bank select 2 */
	Rsel		= 0x08,		/* RAM select */
	Test		= 0x10,		/* test */
	Ows		= 0x20,		/* 0 wait state */
	Tcm		= 0x40,		/* terminal count mask */
	Nim		= 0x80,		/* MIC interrupt mask */
};

enum {					/* Ctrl */
	Rst		= 0x01,		/* software reset */
	Xsel		= 0x02,		/* transceiver select (BNC = 1, AUI = 0) */
	Ealo		= 0x04,		/* ethernet address low */
	Eahi		= 0x08,		/* ethernet address high */
	Share		= 0x10,		/* interrupt share */
	Dbsel		= 0x20,		/* double buffer select */
	Ddir		= 0x40,		/* DMA direction */
	Start		= 0x80,		/* DMA start */
};

enum {					/* Streg */
	Rev0		= 0x01,		/* gate array revision bit 0 */
	Rev1		= 0x02,		/* gate array revision bit 1 */
	Rev2		= 0x04,		/* gate array revision bit 2 */
	Dip		= 0x08,		/* DMA in progress */
	Dtc		= 0x10,		/* DMA terminal count */
	Oflw		= 0x20,		/* overflow */
	Uflw		= 0x40,		/* underflow */
	Dprdy		= 0x80,		/* data port ready */
};

enum {					/* Idcfr */
	Drq1		= 0x01,		/* DMA request 1 */
	Drq2		= 0x02,		/* DMA request 2 */
	Drq3		= 0x04,		/* DMA request 3 */
	Irq2		= 0x10,		/* interrupt request 2 */
	Irq3		= 0x20,		/* interrupt request 3 */
	Irq4		= 0x40,		/* interrupt request 4 */
	Irq5		= 0x80,		/* interrupt request 5 */
};

enum {
	RamOffset	= 0x2000,	/* where the card sees the shared memory */
};

typedef struct {
	ulong	io;			/* I/O base address */
	uchar	irq;			/* interrupt level */
	uchar	bnc;			/* use BNC connector */
} Table;

/*
 * The only configuration info we can get from the card is the
 * I/O base address and the shared-memory address. So let's make
 * a little hard-wired configuration table.
 */
static Table table[] = {
	{ 0x300, Irq2, 0, },		/* external transceiver */
	{ 0x310, Irq3, 0, },
	{ 0x330, Irq4, 0, },
	{ 0x350, Irq5, 0, },

	{ 0x250, Irq2, 1, },		/* BNC */
	{ 0x280, Irq3, 1, },
	{ 0x2A0, Irq4, 1, },
	{ 0x2E0, Irq5, 1, },

	{ 0 },
};

static int
reset(Ctlr *ctlr)
{
	USED(ctlr);
	Table *tp;
	int i;
	uchar cfr;

	/*
	 * The Gate Array Ctrl register bits <3,2> determine what is
	 * mapped at the I/O base address. The options are the DP8390,
	 * PROM bytes 0-15 or PROM bytes 16-31.
	 * At power-on, the I/O base address contains PROM bytes 16-31.
	 */
	for(tp = table; ctlr->card.io = tp->io; tp++){
		/*
		 * Toggle the software reset bit, this should initialise the
		 * Ctrl register to 0x0A (Eahi|Xsel).
		 * Hopefully there is no other card at the gate-array address.
		 */
		outb(ctlr->card.io+Ctrl, Rst);
		delay(1);
		outb(ctlr->card.io+Ctrl, 0);
		if(inb(ctlr->card.io+Ctrl) == (Eahi|Xsel))
			break;
	}
	if(ctlr->card.io == 0)
		return -1;

	/*
	 * Map PROM bytes 0-15 to the I/O base address
	 * and read the ethernet address.
	 * When done, map the DP8390 at the I/O base address
	 * and set the transceiver type and irq.
	 */
	outb(ctlr->card.io+Ctrl, Ealo|Xsel);
	for(i = 0; i < sizeof(ctlr->ea); i++)
		ctlr->ea[i] = inb(ctlr->card.io+i);

	outb(ctlr->card.io+Gacfr, Nim|Tcm);

	outb(ctlr->card.io+Ctrl, 0|tp->bnc);
	outb(ctlr->card.io+Idcfr, tp->irq);

	if(tp->irq == Irq2)
		ctlr->card.irq = 2;
	else if(tp->irq == Irq3)
		ctlr->card.irq = 3;
	else if(tp->irq == Irq4)
		ctlr->card.irq = 4;
	else if(tp->irq == Irq5)
		ctlr->card.irq = 5;
	else
		return -1;

	return -1;
	/*
	 * Try to configure the shared memory.
	 * Assume this is an 8K card.
	 */
	ctlr->card.ram = 1;
	cfr = inb(ctlr->card.io+Pcfr);
	if(cfr & C8xxx)
		ctlr->card.ramstart = KZERO|0xC8000;
	else if(cfr & CCxxx)
		ctlr->card.ramstart = KZERO|0xCC000;
	else if(cfr & D8xxx)
		ctlr->card.ramstart = KZERO|0xD8000;
	else if(cfr & DCxxx)
		ctlr->card.ramstart = KZERO|0xDC000;
	else
		ctlr->card.ram = 0;

	ctlr->card.ramstop = ctlr->card.ramstart + 8*1024;

	/*
	 * Finish initialising the gate-array.
	 * The Pstr and Pspr registers must be the same as those
	 * given to the DP8390.
	 * Gacfr is set to enable shared memory and block DMA
	 * interrupts.
	 */
	outb(ctlr->card.io+Pstr, ctlr->card.pstart);
	outb(ctlr->card.io+Pspr, ctlr->card.pstop);

	outb(ctlr->card.io+Dqtr, 8);
	outb(ctlr->card.io+Damsb, ctlr->card.tstart);
	outb(ctlr->card.io+Dalsb, 0);

	outb(ctlr->card.io+Vptr2, 0xFF);
	outb(ctlr->card.io+Vptr1, 0xFF);
	outb(ctlr->card.io+Vptr0, 0x00);

	/*
	 * Finally, init the 8390, set the ethernet address
	 * and turn on the shared memory if any.
	 */
	ctlr->card.dp8390 = ctlr->card.io;
	dp8390reset(ctlr);
	dp8390setea(ctlr);

	if(ctlr->card.ram)
		outb(ctlr->card.io+Gacfr, Tcm|Rsel|Mbs0);
	else
		outb(ctlr->card.io+Gacfr, Tcm);

	return 0;
}

static void*
read(Ctlr *ctlr, void *to, ulong from, ulong len)
{
	uchar *addr, ctrl;

	/*
	 * If the interface has shared memory, just do a memmove.
	 * In this case, 'from' is an index into the shared memory.
	 * Due to 'hardware considerations', the memory is seen by
	 * the adapter at RamOffset, so we have to adjust.
	 */
	if(ctlr->card.ram)
		return memmove(to, (void*)(ctlr->card.ramstart+from-RamOffset), len);

	/*
	 * Polled I/O. This should be tuned.
	 * Set up the dma registers and pump the fifo.
	 */
	outb(ctlr->card.io+Dalsb, from & 0xFF);
	outb(ctlr->card.io+Damsb, (from>>8) & 0xFF);
	ctrl = inb(ctlr->card.io+Ctrl);
	outb(ctlr->card.io+Ctrl, Start|ctrl);
	addr = to;
	while(len){
		/*
		 * The fifo is 8 bytes deep, we have to wait
		 * until it's ready before stuffing it.
		 * The fifo is in its default configuration,
		 * so we can do word accesses.
		 */
		while((inb(ctlr->card.io+Streg) & Dprdy) == 0)
			;
		if(len >= 8){
			inss(ctlr->card.io+Rfmsb, addr, 4);
			addr += 8;
			len -= 8;
		}
		else{
			insb(ctlr->card.io+Rfmsb, addr, len);
			break;
		}
	}
	outb(ctlr->card.io+Ctrl, ctrl);
	return to;
}

Board ether503 = {
	reset,
	0,			/* init */
	0,			/* attach */
	0,			/* mode */
	0,			/* receive */
	0,			/* transmit */
	0,			/* interrupt */
	0,			/* watch */

	0,			/* addr */
	0,			/* irq */
	0,			/* bit16 */

	0,			/* ram */
	0,			/* ramstart */
	0,			/* ramstop */

	0,			/* dp8390 */
	0,			/* data */
	0,			/* tstart */
	0,			/* pstart */
	0,			/* pstop */
static void*
write(Ctlr *ctlr, ulong to, void *from, ulong len)
{
	uchar *addr, ctrl;

	/*
	 * See the comments in 'read()' above.
	 */
	if(ctlr->card.ram)
		return memmove((void*)(ctlr->card.ramstart+to-RamOffset), from, len);

	outb(ctlr->card.io+Dalsb, to & 0xFF);
	outb(ctlr->card.io+Damsb, (to>>8) & 0xFF);
	ctrl = inb(ctlr->card.io+Ctrl);
	outb(ctlr->card.io+Ctrl, Start|Ddir|ctrl);
	addr = from;
	while(len){
		while((inb(ctlr->card.io+Streg) & Dprdy) == 0)
			;
		if(len >= 8){
			outss(ctlr->card.io+Rfmsb, addr, 4);
			addr += 8;
			len -= 8;
		}
		else{
			outsb(ctlr->card.io+Rfmsb, addr, len);
			break;
		}
	}
	outb(ctlr->card.io+Ctrl, ctrl);
	return (void*)to;
}

Card ether503 = {
	"3Com503",			/* ident */

	reset,				/* reset */
	0,				/* init */
	dp8390attach,			/* attach */
	dp8390mode,			/* mode */

	read,				/* read */
	write,				/* write */

	dp8390receive,			/* receive */
	dp8390transmit,			/* transmit */
	dp8390intr,			/* interrupt */
	dp8390watch,			/* watch */
	dp8390overflow,			/* overflow */

	0,				/* io */
	0,				/* irq */
	0,				/* bit16 */

	0,				/* ram */
	0,				/* ramstart */
	0,				/* ramstop */

	0,				/* dp8390 */
	0,				/* data */
	0,				/* software bndry */
	RamOffset/Dp8390BufSz,		/* tstart */
	(RamOffset+0x600)/Dp8390BufSz,	/* pstart */
	(RamOffset+8*1024)/Dp8390BufSz,	/* pstop */
};

M pc/ether509.c => pc/ether509.c +70 -79
@@ 84,7 84,7 @@ enum {
	RxReceiving	= 0x8000,	/* RX Receiving */
};

#define COMMAND(board, cmd, a)	outs(board->io+Command, ((cmd)<<11)|(a))
#define COMMAND(ctlr, cmd, a)	outs(ctlr->card.io+Command, ((cmd)<<11)|(a))

/*
 * Write two 0 bytes to identify the IDport and then reset the


@@ 116,13 116,12 @@ idseq(void)
static int
reset(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	int i, ea;
	ushort x, acr;

	/*
	 * Do the little configuration dance. We only look
	 * at the first board that responds, if we ever have more
	 * at the first card that responds, if we ever have more
	 * than one we'll need to modify this sequence.
	 *
	 * 2. get to command state, reset, then return to command state


@@ 172,8 171,8 @@ reset(Ctlr *ctlr)
	 *
	 *    Enable the adapter. 
	 */
	board->io = (acr & 0x1F)*0x10 + 0x200;
	outb(board->io+ConfigControl, 0x01);
	ctlr->card.io = (acr & 0x1F)*0x10 + 0x200;
	outb(ctlr->card.io+ConfigControl, 0x01);

	/*
	 * Read the IRQ from the Resource Configuration Register


@@ 181,14 180,14 @@ reset(Ctlr *ctlr)
	 * The EEPROM command is 8bits, the lower 6 bits being
	 * the address offset.
	 */
	board->irq = (ins(board->io+ResourceConfig)>>12) & 0x0F;
	ctlr->card.irq = (ins(ctlr->card.io+ResourceConfig)>>12) & 0x0F;
	for(ea = 0, i = 0; i < 3; i++, ea += 2){
		while(ins(board->io+EEPROMcmd) & 0x8000)
		while(ins(ctlr->card.io+EEPROMcmd) & 0x8000)
			;
		outs(board->io+EEPROMcmd, (2<<6)|i);
		while(ins(board->io+EEPROMcmd) & 0x8000)
		outs(ctlr->card.io+EEPROMcmd, (2<<6)|i);
		while(ins(ctlr->card.io+EEPROMcmd) & 0x8000)
			;
		x = ins(board->io+EEPROMdata);
		x = ins(ctlr->card.io+EEPROMdata);
		ctlr->ea[ea] = (x>>8) & 0xFF;
		ctlr->ea[ea+1] = x & 0xFF;
	}


@@ 199,38 198,31 @@ reset(Ctlr *ctlr)
	 * Commands have the format 'CCCCCAAAAAAAAAAA' where C
	 * is a bit in the command and A is a bit in the argument.
	 */
	COMMAND(board, SelectWindow, 2);
	COMMAND(ctlr, SelectWindow, 2);
	for(i = 0; i < 6; i++)
		outb(board->io+i, ctlr->ea[i]);
		outb(ctlr->card.io+i, ctlr->ea[i]);

	/*
	 * Finished with window 2.
	 * Set window 1 for normal operation.
	 */
	COMMAND(board, SelectWindow, 1);
	COMMAND(ctlr, SelectWindow, 1);

	/*
	 * If we have a 10BASE2 transceiver, start the DC-DC
	 * converter. Wait > 800 microseconds.
	 */
	if(((acr>>14) & 0x03) == 0x03){
		COMMAND(board, StartCoax, 0);
		COMMAND(ctlr, StartCoax, 0);
		delay(1);
	}

	print("3C509 I/O addr %lux irq %d:", board->io, board->irq);
	for(i = 0; i < sizeof(ctlr->ea); i++)
		print(" %2.2ux", ctlr->ea[i]);
	print("\n");

	return 0;
}

static void
attach(Ctlr *ctlr)
{
	Board *board = ctlr->board;

	/*
	 * Set the receiver packet filter for our own and
	 * and broadcast addresses, set the interrupt masks


@@ 239,44 231,30 @@ attach(Ctlr *ctlr)
	 * is the receiver interrupt. If the transmit FIFO fills up,
	 * we will also see TxAvailable interrupts.
	 */
	COMMAND(board, SetRxFilter, Broadcast|MyEtherAddr);
	COMMAND(board, SetReadZeroMask, AllIntr|Latch);
	COMMAND(board, SetIntrMask, AllIntr|Latch);
	COMMAND(board, RxEnable, 0);
	COMMAND(board, TxEnable, 0);
	COMMAND(ctlr, SetRxFilter, Broadcast|MyEtherAddr);
	COMMAND(ctlr, SetReadZeroMask, AllIntr|Latch);
	COMMAND(ctlr, SetIntrMask, AllIntr|Latch);
	COMMAND(ctlr, RxEnable, 0);
	COMMAND(ctlr, TxEnable, 0);
}

static void
mode(Ctlr *ctlr, int on)
{
	Board *board = ctlr->board;

	if(on)
		COMMAND(board, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr);
		COMMAND(ctlr, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr);
	else
		COMMAND(board, SetRxFilter, Broadcast|MyEtherAddr);
}

static int
getdiag(Board *board)
{
	int bytes;

	COMMAND(board, SelectWindow, 4);
	bytes = ins(board->io+FIFOdiag);
	COMMAND(board, SelectWindow, 1);
	return bytes & 0xFFFF;
		COMMAND(ctlr, SetRxFilter, Broadcast|MyEtherAddr);
}

static void
receive(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	ushort status;
	RingBuf *rb;
	int len;

	while(((status = ins(board->io+RxStatus)) & RxEmpty) == 0){
	while(((status = ins(ctlr->card.io+RxStatus)) & RxEmpty) == 0){
		/*
		 * If we had an error, log it and continue
		 * without updating the ring.


@@ 317,9 295,9 @@ receive(Ctlr *ctlr)
				 * doubleword. We can pick them out 16-bits
				 * at a time (can try 32-bits at a time
				 * later).
				insl(board->io+Fifo, rb->pkt, HOWMANY(len, 4));
				insl(ctlr->card.io+Fifo, rb->pkt, HOWMANY(len, 4));
				 */
				inss(board->io+Fifo, rb->pkt, HOWMANY(len, 2));
				inss(ctlr->card.io+Fifo, rb->pkt, HOWMANY(len, 2));

				/*
				 * Update the ring.


@@ 333,8 311,8 @@ receive(Ctlr *ctlr)
		 * Discard the packet as we're done with it.
		 * Wait for discard to complete.
		 */
		COMMAND(board, RxDiscard, 0);
		while(ins(board->io+Status) & CmdInProgress)
		COMMAND(ctlr, RxDiscard, 0);
		while(ins(ctlr->card.io+Status) & CmdInProgress)
			;
	}
}


@@ 342,12 320,9 @@ receive(Ctlr *ctlr)
static void
transmit(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	RingBuf *tb;
	int s;
	ushort len;

	s = splhi();
	for(tb = &ctlr->tb[ctlr->ti]; tb->owner == Interface; tb = &ctlr->tb[ctlr->ti]){
		/*
		 * If there's no room in the FIFO for this packet,


@@ 356,8 331,8 @@ transmit(Ctlr *ctlr)
		 * we need 4 bytes for the preamble.
		 */
		len = ROUNDUP(tb->len, 4);
		if(len > ins(board->io+TxFreeBytes)+4){
			COMMAND(board, SetTxAvailable, len);
		if(len > ins(ctlr->card.io+TxFreeBytes)+4){
			COMMAND(ctlr, SetTxAvailable, len);
			break;
		}



@@ 365,23 340,32 @@ transmit(Ctlr *ctlr)
		 * There's room, copy the packet to the FIFO and free
		 * the buffer back to the host.
		 */
		outs(board->io+Fifo, tb->len);
		outs(board->io+Fifo, 0);
		outss(board->io+Fifo, tb->pkt, len/2);
		outs(ctlr->card.io+Fifo, tb->len);
		outs(ctlr->card.io+Fifo, 0);
		outss(ctlr->card.io+Fifo, tb->pkt, len/2);
		tb->owner = Host;
		ctlr->ti = NEXT(ctlr->ti, ctlr->ntb);
	}
	splx(s);
}

static ushort
getdiag(Ctlr *ctlr)
{
	ushort bytes;

	COMMAND(ctlr, SelectWindow, 4);
	bytes = ins(ctlr->card.io+FIFOdiag);
	COMMAND(ctlr, SelectWindow, 1);
	return bytes & 0xFFFF;
}

static void
interrupt(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	ushort status, diag;
	uchar txstatus, x;

	status = ins(board->io+Status);
	status = ins(ctlr->card.io+Status);

	if(status & Failure){
		/*


@@ 391,16 375,16 @@ interrupt(Ctlr *ctlr)
		 * need to retransmit?
		 * This probably isn't right.
		 */
		diag = getdiag(board);
		diag = getdiag(ctlr);
		print("ether509: status #%ux, diag #%ux\n", status, diag);

		if(diag & TxOverrun){
			COMMAND(board, TxReset, 0);
			COMMAND(board, TxEnable, 0);
			COMMAND(ctlr, TxReset, 0);
			COMMAND(ctlr, TxEnable, 0);
		}

		if(diag & RxUnderrun){
			COMMAND(board, RxReset, 0);
			COMMAND(ctlr, RxReset, 0);
			attach(ctlr);
		}



@@ 425,14 409,14 @@ interrupt(Ctlr *ctlr)
		 */
		txstatus = 0;
		do{
			if(x = inb(board->io+TxStatus))
				outb(board->io+TxStatus, 0);
			if(x = inb(ctlr->card.io+TxStatus))
				outb(ctlr->card.io+TxStatus, 0);
			txstatus |= x;
		}while(ins(board->io+Status) & TxComplete);
		}while(ins(ctlr->card.io+Status) & TxComplete);

		if(txstatus & (TxJabber|TxUnderrun))
			COMMAND(board, TxReset, 0);
		COMMAND(board, TxEnable, 0);
			COMMAND(ctlr, TxReset, 0);
		COMMAND(ctlr, TxEnable, 0);
		ctlr->oerrs++;
	}



@@ 441,7 425,7 @@ interrupt(Ctlr *ctlr)
		 * Reset the Tx FIFO threshold.
		 */
		if(status & TxAvailable)
			COMMAND(board, AckIntr, TxAvailable);
			COMMAND(ctlr, AckIntr, TxAvailable);
		transmit(ctlr);
		wakeup(&ctlr->tr);
		status &= ~(TxAvailable|TxComplete);


@@ 453,18 437,25 @@ interrupt(Ctlr *ctlr)
	 * Otherwise, acknowledge the interrupt.
	 */
	if(status & AllIntr)
		panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(board));
		panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(ctlr));

	COMMAND(board, AckIntr, Latch);
	COMMAND(ctlr, AckIntr, Latch);
}

Board ether509 = {
	reset,
	0,			/* init */
	attach,
	mode,
	0,			/* receive */
	transmit,
	interrupt,
	0,			/* watch */
Card ether509 = {
	"3Com509",			/* ident */

	reset,				/* reset */
	0,				/* init */
	attach,				/* attach */
	mode,				/* mode */

	0,				/* read */
	0,				/* write */

	0,				/* receive */
	transmit,			/* transmit */
	interrupt,			/* interrupt */
	0,				/* watch */
	0,				/* overflow */
};

M pc/ether8003.c => pc/ether8003.c +80 -62
@@ 19,7 19,7 @@ enum {					/* 83C584 Bus Interface Controller */
	Ijr		= 0x06,		/* Initialisation Jumpers */
	Gp2		= 0x07,		/* General Purpose Data Register */
	Lar		= 0x08,		/* LAN Address Registers */
	Id		= 0x0E,		/* Board ID byte */
	Id		= 0x0E,		/* Card ID byte */
	Cksum		= 0x0F,		/* Checksum */
};



@@ 47,7 47,6 @@ static int intrmap[] = {
static int
reset(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	int i;
	uchar msr, icr, laar, irr, sum;



@@ 56,18 55,18 @@ reset(Ctlr *ctlr)
	 * and validate the checksum - the sum of all 8 bytes
	 * should be 0xFF.
	 */
	for(board->io = 0x200; board->io < 0x400; board->io += 0x20){
	for(ctlr->card.io = 0x200; ctlr->card.io < 0x400; ctlr->card.io += 0x20){
		sum = 0;
		for(i = 0; i < sizeof(ctlr->ea); i++){
			ctlr->ea[i] = inb(board->io+Lar+i);
			ctlr->ea[i] = inb(ctlr->card.io+Lar+i);
			sum += ctlr->ea[i];
		}
		sum += inb(board->io+Id);
		sum += inb(board->io+Cksum);
		sum += inb(ctlr->card.io+Id);
		sum += inb(ctlr->card.io+Cksum);
		if(sum == 0xFF)
			break;
	}
	if(board->io >= 0x400)
	if(ctlr->card.io >= 0x400)
		return -1;

	/*


@@ 75,10 74,10 @@ reset(Ctlr *ctlr)
	 * Be careful to preserve the Msr address bits,
	 * they don't get reloaded from the EEPROM on reset.
	 */
	msr = inb(board->io+Msr);
	outb(board->io+Msr, Rst|msr);
	msr = inb(ctlr->card.io+Msr);
	outb(ctlr->card.io+Msr, Rst|msr);
	delay(1);
	outb(board->io+Msr, msr);
	outb(ctlr->card.io+Msr, msr);
	delay(2);

	/*


@@ 90,60 89,53 @@ reset(Ctlr *ctlr)
	 * 8003EBT, 8003S, 8003SH or 8003WT, we don't care), in which
	 * case the default irq gets used.
	 */
	msr = inb(board->io+Msr);
	icr = inb(board->io+Icr);
	laar = inb(board->io+Laar);
	irr = inb(board->io+Irr);
	msr = inb(ctlr->card.io+Msr);
	icr = inb(ctlr->card.io+Icr);
	laar = inb(ctlr->card.io+Laar);
	irr = inb(ctlr->card.io+Irr);
	if(icr != ctlr->ea[1] || irr != ctlr->ea[4] || laar != ctlr->ea[5])
		board->irq = intrmap[((irr>>5) & 0x3)|(icr & 0x4)];
		ctlr->card.irq = intrmap[((irr>>5) & 0x3)|(icr & 0x4)];
	else {
		msr = (((ulong)board->ramstart)>>13) & 0x3F;
		msr = (((ulong)ctlr->card.ramstart)>>13) & 0x3F;
		icr = laar = 0;
		board->watch = 0;
		ctlr->card.watch = 0;
	}

	/*
	 * Set up the bus-size, RAM address and RAM size
	 * from the info in the configuration registers.
	 */
	board->bit16 = icr & 0x1;
	ctlr->card.bit16 = icr & 0x1;

	board->ram = 1;
	board->ramstart = KZERO|((msr & 0x3F)<<13);
	if(board->bit16)
		board->ramstart |= (laar & 0x1F)<<19;
	ctlr->card.ram = 1;
	ctlr->card.ramstart = KZERO|((msr & 0x3F)<<13);
	if(ctlr->card.bit16)
		ctlr->card.ramstart |= (laar & 0x1F)<<19;
	else
		board->ramstart |= 0x80000;
		ctlr->card.ramstart |= 0x80000;

	if(icr & (1<<3))
		board->ramstop = 32*1024;
		ctlr->card.ramstop = 32*1024;
	else
		board->ramstop = 8*1024;
	if(board->bit16)
		board->ramstop <<= 1;
	board->ramstop += board->ramstart;
		ctlr->card.ramstop = 8*1024;
	if(ctlr->card.bit16)
		ctlr->card.ramstop <<= 1;
	ctlr->card.ramstop += ctlr->card.ramstart;

	/*
	 * Set the DP8390 ring addresses.
	 */
	board->dp8390 = board->io+0x10;
	board->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
	board->pstop = HOWMANY(board->ramstop-board->ramstart, Dp8390BufSz);
	board->tstart = 0;

	print("WD8003 I/O addr %lux width %d addr %lux size %d irq %d:",
		board->io, board->bit16 ? 16: 8, board->ramstart,
		board->ramstop-board->ramstart, board->irq);
	for(i = 0; i < sizeof(ctlr->ea); i++)
		print(" %2.2ux", ctlr->ea[i]);
	print("\n");
	ctlr->card.dp8390 = ctlr->card.io+0x10;
	ctlr->card.pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
	ctlr->card.pstop = HOWMANY(ctlr->card.ramstop-ctlr->card.ramstart, Dp8390BufSz);
	ctlr->card.tstart = 0;

	/*
	 * Enable interface RAM, set interface width.
	 */
	outb(board->io+Msr, Menb|msr);
	if(board->bit16)
		outb(board->io+Laar, laar|L16en|M16en|ZeroWS16);
	outb(ctlr->card.io+Msr, Menb|msr);
	if(ctlr->card.bit16)
		outb(ctlr->card.io+Laar, laar|L16en|M16en|ZeroWS16);

	/*
	 * Finally, init the 8390 and set the


@@ 155,17 147,36 @@ reset(Ctlr *ctlr)
	return 0;
}

static void*
read(Ctlr *ctlr, void *to, ulong from, ulong len)
{
	/*
	 * In this case, 'from' is an index into the shared memory.
	 */
	memmove(to, (void*)(ctlr->card.ramstart+from), len);
	return to;
}

static void*
write(Ctlr *ctlr, ulong to, void *from, ulong len)
{
	/*
	 * In this case, 'to' is an index into the shared memory.
	 */
	memmove((void*)(ctlr->card.ramstart+to), from, len);
	return (void*)to;
}

static void
watch(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	uchar msr;
	int s;

	s = splhi();
	msr = inb(board->io+Msr);
	msr = inb(ctlr->card.io+Msr);
	/*
	 * If the board has reset itself,
	 * If the card has reset itself,
	 * start again.
	 */
	if((msr & Menb) == 0){


@@ 184,21 195,28 @@ watch(Ctlr *ctlr)
 * Defaults are set for the dumb 8003E
 * which can't be autoconfigured.
 */
Board ether8003 = {
	reset,
	0,			/* init */
	dp8390attach,
	dp8390mode,
	dp8390receive,
	dp8390transmit,
	dp8390intr,
	watch,

	0x280,			/* io */
	3,			/* irq */
	0,			/* bit16 */

	1,			/* ram */
	0xD0000,		/* ramstart */
	0xD0000+8*1024,		/* ramstop */
Card ether8003 = {
	"WD8003",			/* ident */

	reset,				/* reset */
	0,				/* init */
	dp8390attach,			/* attach */
	dp8390mode,			/* mode */

	read,				/* read */
	write,				/* write */

	dp8390receive,			/* receive */
	dp8390transmit,			/* transmit */
	dp8390intr,			/* interrupt */
	watch,				/* watch */
	0,				/* overflow */

	0x280,				/* io */
	3,				/* irq */
	0,				/* bit16 */

	1,				/* ram */
	0xD0000,			/* ramstart */
	0xD0000+8*1024,			/* ramstop */
};

M pc/ether8390.c => pc/ether8390.c +269 -199
@@ 24,9 24,11 @@ enum {
	RDMAwrite	= (2<<3),	/* remote DMA write */
	RDMAsend	= (3<<3),	/* remote DMA send packet */
	RDMAabort	= (4<<3),	/* abort/complete remote DMA */
	Page0		= (0x00<<6),	/* page select */
	Page1		= (0x01<<6),
	Page2		= (0x02<<6),
	Ps0		= 0x40,		/* page select */
	Ps1		= 0x80,		/* page select */
	Page0		= 0x00,
	Page1		= Ps0,
	Page2		= Ps1,
};

enum {					/* Page 0, read */


@@ 38,7 40,7 @@ enum {					/* Page 0, read */
	Fifo		= 0x06,		/* FIFO */
	Isr		= 0x07,		/* interrupt status register (R/W) */
	Crda0		= 0x08,		/* current remote DMA address 0 */
	Crda1		= 0x08,		/* current remote DMA address 1 */
	Crda1		= 0x09,		/* current remote DMA address 1 */
	Rsr		= 0x0C,		/* receive status register */
	Cntr0		= 0x0D,		/* frame alignment errors */
	Cntr1		= 0x0E,		/* CRC errors */


@@ 144,15 146,9 @@ typedef struct {
	uchar	len1;
} Hdr;

typedef struct {
	Hdr;
	uchar	data[Dp8390BufSz-sizeof(Hdr)];
} Buf;

static void
dp8390disable(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	int timo;

	/*


@@ 163,54 159,63 @@ dp8390disable(Ctlr *ctlr)
	 * chip there if this is called when probing for a device
	 * at boot.
	 */
	outb(board->dp8390+Cr, Page0|RDMAabort|Stp);
	for(timo = 10000; (inb(board->dp8390+Isr) & Rst) == 0 && timo; timo--)
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Stp);
	dp8390outb(ctlr->card.dp8390+Rbcr0, 0);
	dp8390outb(ctlr->card.dp8390+Rbcr1, 0);
	for(timo = 10000; (dp8390inb(ctlr->card.dp8390+Isr) & Rst) == 0 && timo; timo--)
			;
}

static void
dp8390ring(Ctlr *ctlr)
{
	dp8390outb(ctlr->card.dp8390+Pstart, ctlr->card.pstart);
	dp8390outb(ctlr->card.dp8390+Pstop, ctlr->card.pstop);
	dp8390outb(ctlr->card.dp8390+Bnry, ctlr->card.pstop-1);

	dp8390outb(ctlr->card.dp8390+Cr, Page1|RDMAabort|Stp);
	dp8390outb(ctlr->card.dp8390+Curr, ctlr->card.pstart);
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Stp);

	ctlr->card.nxtpkt = ctlr->card.pstart;
}

void
dp8390reset(Ctlr *ctlr)
{
	Board *board = ctlr->board;

	/*
	 * This is the initialisation procedure described
	 * as 'mandatory' in the datasheet.
	 * as 'mandatory' in the datasheet, with references
	 * to the 3Com503 technical reference manual.
	 */ 
	dp8390disable(ctlr);
	if(board->bit16)
		outb(board->dp8390+Dcr, Ft4|Ls|Wts);
	if(ctlr->card.bit16)
		dp8390outb(ctlr->card.dp8390+Dcr, Ft4|Ls|Wts);
	else
		outb(board->dp8390+Dcr, Ft4|Ls);

	outb(board->dp8390+Rbcr0, 0);
	outb(board->dp8390+Rbcr1, 0);

	outb(board->dp8390+Rcr, Ab);
	outb(board->dp8390+Tcr, Lb);
		dp8390outb(ctlr->card.dp8390+Dcr, Ft4|Ls);

	outb(board->dp8390+Bnry, board->pstart);
	outb(board->dp8390+Pstart, board->pstart);
	outb(board->dp8390+Pstop, board->pstop);
	dp8390outb(ctlr->card.dp8390+Rbcr0, 0);
	dp8390outb(ctlr->card.dp8390+Rbcr1, 0);

	outb(board->dp8390+Isr, 0xFF);
	outb(board->dp8390+Imr, Ovwe|Txee|Rxee|Ptxe|Prxe);

	outb(board->dp8390+Cr, Page1|RDMAabort|Stp);
	dp8390outb(ctlr->card.dp8390+Tcr, 0);
	dp8390outb(ctlr->card.dp8390+Rcr, Mon);

	/*
	 * Can't initialise ethernet address as we may
	 * not know it yet.
	 * Init the ring hardware and software ring pointers.
	 * Can't initialise ethernet address as we may not know
	 * it yet.
	 */
	outb(board->dp8390+Curr, board->pstart+1);
	dp8390ring(ctlr);
	dp8390outb(ctlr->card.dp8390+Tpsr, ctlr->card.tstart);

	dp8390outb(ctlr->card.dp8390+Isr, 0xFF);
	dp8390outb(ctlr->card.dp8390+Imr, Cnte|Ovwe|Txee|Rxee|Ptxe|Prxe);

	/*
	 * Leave the chip initialised,
	 * but in internal loopback mode.
	 */
	outb(board->dp8390+Cr, Page0|RDMAabort|Sta);

	outb(board->dp8390+Tpsr, board->tstart);
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Sta);
}

void


@@ 218,25 223,27 @@ dp8390attach(Ctlr *ctlr)
{
	/*
	 * Enable the chip for transmit/receive.
	 * The init routine leaves the chip in internal
	 * loopback.
	 * The init routine leaves the chip in monitor
	 * mode.
	 */
	outb(ctlr->board->dp8390+Tcr, 0);
	dp8390outb(ctlr->card.dp8390+Rcr, Ab);
}

void
dp8390mode(Ctlr *ctlr, int on)
{
	/*
	 * Set/reset promiscuous mode.
	 */
	if(on)
		outb(ctlr->board->dp8390+Rcr, Pro|Ab);
		dp8390outb(ctlr->card.dp8390+Rcr, Pro|Ab);
	else
		outb(ctlr->board->dp8390+Rcr, Ab);
		dp8390outb(ctlr->card.dp8390+Rcr, Ab);
}

void
dp8390setea(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	uchar cr;
	int i;



@@ 247,58 254,47 @@ dp8390setea(Ctlr *ctlr)
	 * addresses as we never set the multicast
	 * enable.
	 */
	cr = inb(board->dp8390+Cr);
	outb(board->dp8390+Cr, Page1|(~Page0 & cr));
	cr = dp8390inb(ctlr->card.dp8390+Cr) & ~Txp;
	dp8390outb(ctlr->card.dp8390+Cr, Page1|(~(Ps1|Ps0) & cr));
	for(i = 0; i < sizeof(ctlr->ea); i++)
		outb(board->dp8390+Par0+i, ctlr->ea[i]);
	outb(board->dp8390+Cr, cr);
		dp8390outb(ctlr->card.dp8390+Par0+i, ctlr->ea[i]);
	dp8390outb(ctlr->card.dp8390+Cr, cr);
}

void*
dp8390read(Ctlr *ctlr, void *to, ulong from, ulong len)
{
	Board *board = ctlr->board;
	uchar cr;
	int timo;

	/*
	 * If the interface has shared memory, just
	 * do a memmove.
	 * In this case, 'from' is an index into the shared memory.
	 */
	if(board->ram){
		memmove(to, (void*)(board->ramstart+from), len);
		return to;
	}

	/*
	 * Read some data at offset 'from' in the board's memory
	 * Read some data at offset 'from' in the card's memory
	 * using the DP8390 remote DMA facility, and place it at
	 * 'to' in main memory, via the I/O data port.
	 */
	cr = inb(board->dp8390+Cr);
	outb(board->dp8390+Cr, Page0|RDMAabort|Sta);
	outb(board->dp8390+Isr, Rdc);
	cr = dp8390inb(ctlr->card.dp8390+Cr) & ~Txp;
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Sta);
	dp8390outb(ctlr->card.dp8390+Isr, Rdc);

	/*
	 * Set up the remote DMA address and count.
	 */
	if(board->bit16)
	if(ctlr->card.bit16)
		len = ROUNDUP(len, 2);
	outb(board->dp8390+Rbcr0, len & 0xFF);
	outb(board->dp8390+Rbcr1, (len>>8) & 0xFF);
	outb(board->dp8390+Rsar0, from & 0xFF);
	outb(board->dp8390+Rsar1, (from>>8) & 0xFF);
	dp8390outb(ctlr->card.dp8390+Rbcr0, len & 0xFF);
	dp8390outb(ctlr->card.dp8390+Rbcr1, (len>>8) & 0xFF);
	dp8390outb(ctlr->card.dp8390+Rsar0, from & 0xFF);
	dp8390outb(ctlr->card.dp8390+Rsar1, (from>>8) & 0xFF);

	/*
	 * Start the remote DMA read and suck the data
	 * out of the I/O port.
	 */
	outb(board->dp8390+Cr, Page0|RDMAread|Sta);
	if(board->bit16)
		inss(board->data, to, len/2);
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAread|Sta);
	if(ctlr->card.bit16)
		inss(ctlr->card.data, to, len/2);
	else
		insb(board->data, to, len);
		insb(ctlr->card.data, to, len);

	/*
	 * Wait for the remote DMA to complete. The timeout


@@ 307,99 303,105 @@ dp8390read(Ctlr *ctlr, void *to, ulong from, ulong len)
	 * to the miracles of the bus, we could get this far
	 * and still be talking to a slot full of nothing.
	 */
	for(timo = 10000; (inb(board->dp8390+Isr) & Rdc) == 0 && timo; timo--)
	for(timo = 10000; (dp8390inb(ctlr->card.dp8390+Isr) & Rdc) == 0 && timo; timo--)
			;

	outb(board->dp8390+Isr, Rdc);
	outb(board->dp8390+Cr, cr);
	dp8390outb(ctlr->card.dp8390+Isr, Rdc);
	dp8390outb(ctlr->card.dp8390+Cr, cr);
	return to;
}

void*
dp8390write(Ctlr *ctlr, ulong to, void *from, ulong len)
{
	Board *board = ctlr->board;
	uchar cr;
	ulong crda;

	/*
	 * If the interface has shared memory, just
	 * do a memmove.
	 * In this case, 'to' is an index into the shared memory.
	 */
	if(board->ram){
		memmove((void*)(board->ramstart+to), from, len);
		return (void*)to;
	}

	/*
	 * Write some data to offset 'to' in the board's memory
	 * Write some data to offset 'to' in the card's memory
	 * using the DP8390 remote DMA facility, reading it at
	 * 'from' in main memory, via the I/O data port.
	 */
	cr = inb(board->dp8390+Cr);
	outb(board->dp8390+Cr, Page0|RDMAabort|Sta);
	outb(board->dp8390+Isr, Rdc);
	cr = dp8390inb(ctlr->card.dp8390+Cr) & ~Txp;
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Sta);
	dp8390outb(ctlr->card.dp8390+Isr, Rdc);

	if(ctlr->card.bit16)
		len = ROUNDUP(len, 2);

	/*
	 * Set up the remote DMA address and count.
	 * This is straight from the datasheet, hence
	 * the initial write to Rbcr0 and Cr.
	 * This is straight from the DP8390[12D] datasheet, hence
	 * the initial set up for read.
	 */
	outb(board->dp8390+Rbcr0, 0xFF);
	outb(board->dp8390+Cr, Page0|RDMAread|Sta);
	crda = to-1-ctlr->card.bit16;
	dp8390outb(ctlr->card.dp8390+Rbcr0, (len+1+ctlr->card.bit16) & 0xFF);
	dp8390outb(ctlr->card.dp8390+Rbcr1, ((len+1+ctlr->card.bit16)>>8) & 0xFF);
	dp8390outb(ctlr->card.dp8390+Rsar0, crda & 0xFF);
	dp8390outb(ctlr->card.dp8390+Rsar1, (crda>>8) & 0xFF);
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAread|Sta);

	for(;;){
		crda = dp8390inb(ctlr->card.dp8390+Crda0);
		crda |= dp8390inb(ctlr->card.dp8390+Crda1)<<8;
		if(crda == to){
			/*
			 * Start the remote DMA write and make sure
			 * the registers are correct.
			 */
			dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAwrite|Sta);

	if(board->bit16)
		len = ROUNDUP(len, 2);
	outb(board->dp8390+Rbcr0, len & 0xFF);
	outb(board->dp8390+Rbcr1, (len>>8) & 0xFF);
	outb(board->dp8390+Rsar0, to & 0xFF);
	outb(board->dp8390+Rsar1, (to>>8) & 0xFF);
			crda = dp8390inb(ctlr->card.dp8390+Crda0);
			crda |= dp8390inb(ctlr->card.dp8390+Crda1)<<8;
			if(crda != to)
				panic("crda write %d to %d\n", crda, to);

			break;
		}
	}

	/*
	 * Start the remote DMA write and pump the data
	 * into the I/O port.
	 * Pump the data into the I/O port.
	 */
	outb(board->dp8390+Cr, Page0|RDMAwrite|Sta);
	if(board->bit16)
		outss(board->data, from, len/2);
	if(ctlr->card.bit16)
		outss(ctlr->card.data, from, len/2);
	else
		outsb(board->data, from, len);
		outsb(ctlr->card.data, from, len);

	/*
	 * Wait for the remote DMA to finish. We'll need
	 * a timeout here if this ever gets called before
	 * we know there really is a chip there.
	 */
	while((inb(board->dp8390+Isr) & Rdc) == 0)
	while((dp8390inb(ctlr->card.dp8390+Isr) & Rdc) == 0)
			;

	outb(board->dp8390+Isr, Rdc);
	outb(board->dp8390+Cr, cr);
	dp8390outb(ctlr->card.dp8390+Isr, Rdc);
	dp8390outb(ctlr->card.dp8390+Cr, cr);
	return (void*)to;
}

static uchar
getcurr(Ctlr *ctlr)
{
	uchar cr, curr;

	cr = dp8390inb(ctlr->card.dp8390+Cr) & ~Txp;
	dp8390outb(ctlr->card.dp8390+Cr, Page1|(~(Ps1|Ps0) & cr));
	curr = dp8390inb(ctlr->card.dp8390+Curr);
	dp8390outb(ctlr->card.dp8390+Cr, cr);
	return curr;
}

void
dp8390receive(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	RingBuf *ring;
	uchar bnry, curr, next;
	uchar curr, len1, *pkt;
	Hdr hdr;
	ulong data;
	int i, len;

	bnry = inb(board->dp8390+Bnry);
	next = bnry+1;
	if(next >= board->pstop)
		next = board->pstart;

	for(i = 0; ; i++){
		outb(board->dp8390+Cr, Page1|RDMAabort|Sta);
		curr = inb(board->dp8390+Curr);
		outb(board->dp8390+Cr, Page0|RDMAabort|Sta);
		if(next == curr)
			break;
	ulong data, len;

	for(curr = getcurr(ctlr); ctlr->card.nxtpkt != curr; curr = getcurr(ctlr)){
		/*
		 * Hack to keep away from the card's memory while it is receiving
		 * a packet. This is only a problem on the NCR 3170 Safari.


@@ 411,11 413,11 @@ dp8390receive(Ctlr *ctlr)
			uchar a, b, c;
		
			for(;;delay(10)){
				a = inb(board->dp8390+Clda0);
				b = inb(board->dp8390+Clda0);
				a = dp8390inb(ctlr->card.dp8390+Clda0);
				b = dp8390inb(ctlr->card.dp8390+Clda0);
				if(a != b)
					continue;
				c = inb(board->dp8390+Clda0);
				c = dp8390inb(ctlr->card.dp8390+Clda0);
				if(c != b)
					continue;
				break;


@@ 424,63 426,139 @@ dp8390receive(Ctlr *ctlr)

		ctlr->inpackets++;

		data = next*Dp8390BufSz;
		dp8390read(ctlr, &hdr, data, sizeof(Hdr));
		len = ((hdr.len1<<8)|hdr.len0)-4;
		data = ctlr->card.nxtpkt*Dp8390BufSz;
		(*ctlr->card.read)(ctlr, &hdr, data, sizeof(Hdr));

		/*
		 * Chip is badly scrogged, reinitialise it.
		 * dp8390reset() calls the disable function.
		 * There's no need to reload the ethernet
		 * address.
		 *
		 * Untested.
		 * Don't believe the upper byte count, work it
		 * out from the software next-page pointer and
		 * the current next-page pointer.
		 */
		if(hdr.next > ctlr->card.nxtpkt)
			len1 = hdr.next - ctlr->card.nxtpkt - 1;
		else
			len1 = (ctlr->card.pstop-ctlr->card.nxtpkt) + (hdr.next-ctlr->card.pstart) - 1;
		if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))
			len1--;

		len = ((len1<<8)|hdr.len0)-4;

		/*
		 * Chip is badly scrogged, reinitialise the ring.
		 */
		if(hdr.next < board->pstart || hdr.next >= board->pstop || len < 60){
print("scrogged\n");
			dp8390reset(ctlr);
			dp8390attach(ctlr);
		if(hdr.next < ctlr->card.pstart || hdr.next >= ctlr->card.pstop
		  || len < 60 || len > sizeof(Etherpkt)){
			print("H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%d|",
				hdr.status, hdr.next, hdr.len0, hdr.len1, len);
			dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Stp);
			dp8390ring(ctlr);
			dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Sta);
			return;
		}

		/*
		 * If it's a good packet and we have a place to put it,
		 * read it in to the software ring.
		 * If the packet wraps round the hardware ring, read it
		 * in two pieces.
		 */
		ring = &ctlr->rb[ctlr->ri];
		if(ring->owner == Interface){
			ring->len = len;
		if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && ring->owner == Interface){
			pkt = ring->pkt;
			data += sizeof(Hdr);
			if((data+len) >= board->pstop*Dp8390BufSz){
				len = board->pstop*Dp8390BufSz - data;
				dp8390read(ctlr, ring->pkt+len, board->pstart*Dp8390BufSz,
					(data+ring->len) - board->pstop*Dp8390BufSz);
			ring->len = len;

			if((data+len) >= ctlr->card.pstop*Dp8390BufSz){
				ulong count = ctlr->card.pstop*Dp8390BufSz - data;

				(*ctlr->card.read)(ctlr, pkt, data, count);
				pkt += count;
				data = ctlr->card.pstart*Dp8390BufSz;
				len -= count;
			}
			dp8390read(ctlr, ring->pkt, data, len);
			if(len)
				(*ctlr->card.read)(ctlr, pkt, data, len);

			ring->owner = Host;
			ctlr->ri = NEXT(ctlr->ri, ctlr->nrb);
		}

		next = hdr.next;
		bnry = next-1;
		if(bnry < board->pstart)
			bnry = board->pstop-1;
		outb(board->dp8390+Bnry, bnry);
		/*
		 * Finished woth this packet, update the
		 * hardware and software ring pointers.
		 */
		ctlr->card.nxtpkt = hdr.next;

		hdr.next--;
		if(hdr.next < ctlr->card.pstart)
			hdr.next = ctlr->card.pstop-1;
		dp8390outb(ctlr->card.dp8390+Bnry, hdr.next);
	}
}

/*
 * Initiate a transmission. Must be called splhi().
 */
void
dp8390transmit(Ctlr *ctlr)
{
	Board *board;
	RingBuf *ring;

	ring = &ctlr->tb[ctlr->ti];
	if(ctlr->tbusy == 0 && ring->owner == Interface){
		(*ctlr->card.write)(ctlr, ctlr->card.tstart*Dp8390BufSz, ring->pkt, ring->len);
		dp8390outb(ctlr->card.dp8390+Tbcr0, ring->len & 0xFF);
		dp8390outb(ctlr->card.dp8390+Tbcr1, (ring->len>>8) & 0xFF);
		dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Txp|Sta);
		ctlr->tbusy = 1;
	}
}

void
dp8390overflow(Ctlr *ctlr)
{
	uchar txp;
	int resend;

	/*
	 * The following procedure is taken from the DP8390[12D] datasheet,
	 * it seems pretty adamant that this is what has to be done.
	 */
	txp = dp8390inb(ctlr->card.dp8390+Cr) & Txp;
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Stp);
	delay(2);
	dp8390outb(ctlr->card.dp8390+Rbcr0, 0);
	dp8390outb(ctlr->card.dp8390+Rbcr1, 0);

	resend = 0;
	if(txp && (dp8390inb(ctlr->card.dp8390+Isr) & (Txe|Ptx)) == 0)
		resend = 1;

	dp8390outb(ctlr->card.dp8390+Tcr, Lb);
	dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Sta);
	(*ctlr->card.receive)(ctlr);
	dp8390outb(ctlr->card.dp8390+Isr, Ovw);
	wakeup(&ctlr->rr);
	dp8390outb(ctlr->card.dp8390+Tcr, 0);

	if(resend)
		dp8390outb(ctlr->card.dp8390+Cr, Page0|RDMAabort|Txp|Sta);
}

void
dp8390watch(Ctlr *ctlr)
{
	int s;

	/*
	 * Stub watchdog routine.
	 * Whine if a transmit takes too long.
	 */
	s = splhi();
	board = ctlr->board;
	ring = &ctlr->tb[ctlr->ti];
	if(ring->busy == 0 && ring->owner == Interface){
		dp8390write(ctlr, board->tstart*Dp8390BufSz, ring->pkt, ring->len);
		outb(board->dp8390+Tbcr0, ring->len & 0xFF);
		outb(board->dp8390+Tbcr1, (ring->len>>8) & 0xFF);
		outb(board->dp8390+Cr, Page0|RDMAabort|Txp|Sta);
		ring->busy = 1;
	if(ctlr->tbusy){
		ctlr->tbusy++;
		if(ctlr->tbusy > 1 && ctlr->debug)
			print("TB%d|", ctlr->tbusy);
	}
	splx(s);
}


@@ 488,37 566,21 @@ dp8390transmit(Ctlr *ctlr)
void
dp8390intr(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	RingBuf *ring;
	uchar isr;
	uchar isr, r;

	/*
	 * While there is something of interest,
	 * clear all the interrupts and process.
	 */
	while(isr = inb(board->dp8390+Isr)){
		outb(board->dp8390+Isr, isr);
	dp8390outb(ctlr->card.dp8390+Imr, 0x00);
	while(isr = dp8390inb(ctlr->card.dp8390+Isr)){

		if(isr & Ovw){
			/*
			 * Need to do some work here:
			 *   stop the chip;
			 *   clear out the ring;
			 *   re-enable the chip.
			 * The procedure to put the chip back on
			 * an active network is to first put it
			 * into internal loopback mode, enable it,
			 * then take it out of loopback.
			 *
			 * Untested.
			 */
			if(ctlr->card.overflow)
				(*ctlr->card.overflow)(ctlr);
			dp8390outb(ctlr->card.dp8390+Isr, Ovw);
			ctlr->overflows++;

			dp8390disable(ctlr);
			(*ctlr->board->receive)(ctlr);
			outb(board->dp8390+Tcr, Lb);
			outb(board->dp8390+Cr, Page0|RDMAabort|Sta);
			dp8390attach(ctlr);
		}

		/*


@@ 527,35 589,43 @@ dp8390intr(Ctlr *ctlr)
		 * wakeup the kproc.
		 */
		if(isr & (Rxe|Prx)){
			(*ctlr->board->receive)(ctlr);
			(*ctlr->card.receive)(ctlr);
			dp8390outb(ctlr->card.dp8390+Isr, Rxe|Prx);
			wakeup(&ctlr->rr);
		}

		/*
		 * Log errors and successful transmissions.
		 */
		if(isr & Txe)
			ctlr->oerrs++;
		if(isr & Rxe){
			ctlr->frames += inb(board->dp8390+Cntr0);
			ctlr->crcs += inb(board->dp8390+Cntr1);
			ctlr->buffs += inb(board->dp8390+Cntr2);
		}
		if(isr & Ptx)
			ctlr->outpackets++;

		/*
		 * A packet completed transmission, successfully or
		 * not. Start transmission on the next buffered packet,
		 * and wake the output routine.
		 */
		if(isr & (Txe|Ptx)){
			r = dp8390inb(ctlr->card.dp8390+Tsr);
			if(isr & Txe){
				if((r & (Cdh|Fu|Crs|Abt)) && ctlr->debug)
					print("Tsr#%2.2ux|", r);
				ctlr->oerrs++;
			}

			dp8390outb(ctlr->card.dp8390+Isr, Txe|Ptx);

			if(isr & Ptx)
				ctlr->outpackets++;

			ring = &ctlr->tb[ctlr->ti];
			ring->owner = Host;
			ring->busy = 0;
			ctlr->tbusy = 0;
			ctlr->ti = NEXT(ctlr->ti, ctlr->ntb);
			(*ctlr->board->transmit)(ctlr);
			(*ctlr->card.transmit)(ctlr);
			wakeup(&ctlr->tr);
		}

		if(isr & Cnt){
			ctlr->frames += dp8390inb(ctlr->card.dp8390+Cntr0);
			ctlr->crcs += dp8390inb(ctlr->card.dp8390+Cntr1);
			ctlr->buffs += dp8390inb(ctlr->card.dp8390+Cntr2);
			dp8390outb(ctlr->card.dp8390+Isr, Cnt);
		}
	}
	dp8390outb(ctlr->card.dp8390+Imr, Cnte|Ovwe|Txee|Rxee|Ptxe|Prxe);
}

M pc/l.s => pc/l.s +28 -0
@@ 766,3 766,31 @@ l20:
	MOVB	BX,-1(DI)(CX*1)
	LOOP	l20
	RET

/*
 * The DP8390 ethernet chip needs some time between
 * successive chip selects, so we force a jump into
 * the instruction stream to break the pipeline.
 */
TEXT dp8390inb(SB), $0
	MOVL	p+0(FP),DX
	XORL	AX,AX				/* CF = 0 */
	INB

	JCC	_dp8390inb0			/* always true */
	MOVL	AX,AX

_dp8390inb0:
	RET

TEXT dp8390outb(SB), $0
	MOVL	p+0(FP),DX
	MOVL	b+4(FP),AX
	OUTB

	CLC					/* CF = 0 */
	JCC	_dp8390outb0			/* always true */
	MOVL	AX,AX

_dp8390outb0:
	RET

M power/trap.c => power/trap.c +2 -0
@@ 286,8 286,10 @@ intr(Ureg *ur)
			cause &= ~INTR5;
	}
	if(limit == 0) {
		LEDON(LED5);
		print("intr: unable to identify and clear level5\n");
		cause &= ~INTR5;
		LEDOFF(LED5);
	}

	if(cause & (INTR2|INTR4)) {