~kris/9p

9hist

d6f9362718f54eb91441ed4fd62507e7f91b1428 — David du Colombier 33 years ago 97af757
Plan 9 from Bell Labs 1992-12-22
8 files changed, 1426 insertions(+), 419 deletions(-)

M pc/devether.c
A pc/ether.h
A pc/ether2000.c
A pc/ether503.c
M pc/ether509.c
A pc/ether8003.c
A pc/ether8390.c
M pc/io.h
M pc/devether.c => pc/devether.c +273 -204
@@ 7,6 7,8 @@
#include "io.h"
#include "devtab.h"

#include "ether.h"

/*
 * Half-arsed attempt at a general top-level
 * ethernet driver. Needs work:


@@ 14,33 16,33 @@
 *	much tidying
 *	set ethernet address
 */
extern EtherHw ether509;
extern EtherHw ether80x3;
extern Board ether8003;
extern Board ether503;
extern Board ether2000;
extern Board 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
 * cannot be done passively, so it must be last to
 * prevent scrogging one of the others.
 */
static Board *boards[] = {
	&ether8003,
	&ether503,
	&ether2000,

static EtherHw *etherhw[] = {
	&ether509,
	&ether80x3,
	0
};

enum {
	Nctlr		= 1,
	NCtlr		= 1,
};

static struct EtherCtlr ctlr[Nctlr];

#define NEXT(x, l)	(((x)+1)%(l))
#define OFFSETOF(t, m)	((unsigned)&(((t*)0)->m))
#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

Chan*
etherattach(char *spec)
{
	if(ctlr[0].present == 0)
		error(Enodev);
	return devattach('l', spec);
}
static struct Ctlr *softctlr[NCtlr];
static int nctlr;

Chan*
etherclone(Chan *c, Chan *nc)


@@ 51,19 53,19 @@ etherclone(Chan *c, Chan *nc)
int
etherwalk(Chan *c, char *name)
{
	return netwalk(c, name, &ctlr[0].net);
	return netwalk(c, name, &softctlr[0]->net);
}

void
etherstat(Chan *c, char *dp)
{
	netstat(c, dp, &ctlr[0].net);
	netstat(c, dp, &softctlr[0]->net);
}

Chan*
etheropen(Chan *c, int omode)
{
	return netopen(c, omode, &ctlr[0].net);
	return netopen(c, omode, &softctlr[0]->net);
}

void


@@ 83,7 85,7 @@ etherclose(Chan *c)
long
etherread(Chan *c, void *a, long n, ulong offset)
{
	return netread(c, a, n, offset, &ctlr[0].net);
	return netread(c, a, n, offset, &softctlr[0]->net);
}

long


@@ 103,166 105,193 @@ etherremove(Chan *c)
void
etherwstat(Chan *c, char *dp)
{
	netwstat(c, dp, &ctlr[0].net);
	netwstat(c, dp, &softctlr[0]->net);
}

static int
isobuf(void *arg)
{
	EtherCtlr *cp = arg;
	Ctlr *ctlr = arg;

	return cp->tb[cp->th].owner == Host;
	return ctlr->tb[ctlr->th].owner == Host;
}

static void
etheroput(Queue *q, Block *bp)
{
	EtherCtlr *cp;
	EtherType *tp;
	Etherpkt *p;
	EtherBuf *tb;
	Ctlr *ctlr;
	Type *type;
	Etherpkt *pkt;
	RingBuf *ring;
	int len, n;
	Block *nbp;

	type = q->ptr;
	ctlr = type->ctlr;
	if(bp->type == M_CTL){
		tp = q->ptr;
		if(streamparse("connect", bp))
			tp->type = strtol((char*)bp->rptr, 0, 0);
		qlock(ctlr);
		if(streamparse("connect", bp)){
			if(type->type == -1)
				ctlr->all--;
			type->type = strtol((char*)bp->rptr, 0, 0);
			if(type->type == -1)
				ctlr->all++;
		}
		else if(streamparse("promiscuous", bp)) {
			tp->prom = 1;
			(*tp->ctlr->hw->mode)(tp->ctlr, 1);
			type->prom = 1;
			ctlr->prom++;
			if(ctlr->prom == 1)
				(*ctlr->board->mode)(ctlr, 1);
		}
		qunlock(ctlr);
		freeb(bp);
		return;
	}

	cp = ((EtherType*)q->ptr)->ctlr;

	/*
	 * give packet a local address, return upstream if destined for
	 * Give packet a local address, return upstream if destined for
	 * this machine.
	 */
	if(BLEN(bp) < ETHERHDRSIZE && (bp = pullup(bp, ETHERHDRSIZE)) == 0)
		return;
	p = (Etherpkt*)bp->rptr;
	memmove(p->s, cp->ea, sizeof(cp->ea));
	if(memcmp(cp->ea, p->d, sizeof(cp->ea)) == 0){
	pkt = (Etherpkt*)bp->rptr;
	memmove(pkt->s, ctlr->ea, sizeof(ctlr->ea));
	if(memcmp(ctlr->ea, pkt->d, sizeof(ctlr->ea)) == 0){
		len = blen(bp);
		if (bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){
			putq(&cp->lbq, bp);
			wakeup(&cp->rr);
		if(bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){
			putq(&ctlr->lbq, bp);
			wakeup(&ctlr->rr);
		}
		return;
	}
	if(memcmp(cp->ba, p->d, sizeof(cp->ba)) == 0){
	if(memcmp(ctlr->ba, pkt->d, sizeof(ctlr->ba)) == 0 || ctlr->prom || ctlr->all){
		len = blen(bp);
		nbp = copyb(bp, len);
		if(nbp = expandb(nbp, len >= ETHERMINTU ? len: ETHERMINTU)){
			nbp->wptr = nbp->rptr+len;
			putq(&cp->lbq, nbp);
			wakeup(&cp->rr);
			putq(&ctlr->lbq, nbp);
			wakeup(&ctlr->rr);
		}
	}

	/*
	 * only one transmitter at a time
	 * Only one transmitter at a time.
	 */
	qlock(&cp->tlock);
	qlock(&ctlr->tlock);
	if(waserror()){
		freeb(bp);
		qunlock(&cp->tlock);
		qunlock(&ctlr->tlock);
		nexterror();
	}

	/*
	 * wait till we get an output buffer.
	 * Wait till we get an output buffer.
	 * should try to restart.
	 */
	sleep(&cp->tr, isobuf, cp);
	sleep(&ctlr->tr, isobuf, ctlr);

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

	/*
	 * copy message into buffer
	 * Copy message into buffer.
	 */
	len = 0;
	for(nbp = bp; nbp; nbp = nbp->next){
		if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){
			memmove(tb->pkt+len, nbp->rptr, n);
			memmove(ring->pkt+len, nbp->rptr, n);
			len += n;
		} else
			print("no room damn it\n");
		}
		if(bp->flags & S_DELIM)
			break;
	}

	/*
	 * pad the packet (zero the pad)
	 * Pad the packet (zero the pad).
	 */
	if(len < ETHERMINTU){
		memset(tb->pkt+len, 0, ETHERMINTU-len);
		memset(ring->pkt+len, 0, ETHERMINTU-len);
		len = ETHERMINTU;
	}

	/*
	 * set up the transmit buffer and 
	 * start the transmission
	 * Set up the transmit buffer and 
	 * start the transmission.
	 */
	cp->outpackets++;
	tb->len = len;
	tb->owner = Interface;
	cp->th = NEXT(cp->th, cp->ntb);
	(*cp->hw->transmit)(cp);
	ctlr->outpackets++;
	ring->len = len;
	ring->owner = Interface;
	ctlr->th = NEXT(ctlr->th, ctlr->ntb);
	(*ctlr->board->transmit)(ctlr);

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

/*
 * open an ether line discipline
 *
 * the lock is to synchronize changing the ethertype with
 * sending packets up the stream on interrupts.
 * Open an ether line discipline.
 */
static void
etherstopen(Queue *q, Stream *s)
{
	EtherCtlr *cp = &ctlr[0];
	EtherType *tp;

	tp = &cp->type[s->id];
	qlock(tp);
	RD(q)->ptr = WR(q)->ptr = tp;
	tp->type = 0;
	tp->q = RD(q);
	tp->inuse = 1;
	tp->ctlr = cp;
	qunlock(tp);
	Ctlr *ctlr = softctlr[0];
	Type *type;

	type = &ctlr->type[s->id];
	RD(q)->ptr = WR(q)->ptr = type;
	type->type = 0;
	type->q = RD(q);
	type->inuse = 1;
	type->ctlr = ctlr;
}

/*
 *  close ether line discipline
 * Close ether line discipline.
 *
 *  the lock is to synchronize changing the ethertype with
 *  sending packets up the stream on interrupts.
 * The locking is to synchronize changing the ethertype with
 * sending packets up the stream on interrupts.
 */
static int
isclosed(void *arg)
{
	return ((Type*)arg)->q == 0;
}

static void
etherstclose(Queue *q)
{
	EtherType *tp;

	tp = (EtherType*)(q->ptr);
	if(tp->prom)
		(*tp->ctlr->hw->mode)(tp->ctlr, 0);
	qlock(tp);
	tp->type = 0;
	tp->q = 0;
	tp->prom = 0;
	tp->inuse = 0;
	netdisown(tp);
	tp->ctlr = 0;
	qunlock(tp);
	Type *type = (Type*)(q->ptr);
	Ctlr *ctlr = type->ctlr;

	if(type->prom){
		qlock(ctlr);
		ctlr->prom--;
		if(ctlr->prom == 0)
			(*ctlr->board->mode)(ctlr, 0);
		qunlock(ctlr);
	}
	if(type->type == -1){
		qlock(ctlr);
		ctlr->all--;
		qunlock(ctlr);
	}

	/*
	 * Mark as closing and wait for kproc
	 * to close us.
	 */
	lock(&ctlr->clock);
	type->clist = ctlr->clist;
	ctlr->clist = type;
	unlock(&ctlr->clock);
	wakeup(&ctlr->rr);
	sleep(&type->cr, isclosed, type);

	type->type = 0;
	type->prom = 0;
	type->inuse = 0;
	netdisown(type);
	type->ctlr = 0;
}

static Qinfo info = {


@@ 276,20 305,20 @@ static Qinfo info = {
static int
clonecon(Chan *c)
{
	EtherCtlr *cp = &ctlr[0];
	EtherType *tp;
	Ctlr *ctlr = softctlr[0];
	Type *type;

	USED(c);
	for(tp = cp->type; tp < &cp->type[NType]; tp++){
		qlock(tp);
		if(tp->inuse || tp->q){
			qunlock(tp);
	for(type = ctlr->type; type < &ctlr->type[NType]; type++){
		qlock(type);
		if(type->inuse || type->q){
			qunlock(type);
			continue;
		}
		tp->inuse = 1;
		netown(tp, u->p->user, 0);
		qunlock(tp);
		return tp - cp->type;
		type->inuse = 1;
		netown(type, u->p->user, 0);
		qunlock(type);
		return type - ctlr->type;
	}
	exhausted("ether channels");
	return 0;


@@ 298,14 327,15 @@ clonecon(Chan *c)
static void
statsfill(Chan *c, char *p, int n)
{
	EtherCtlr *cp = &ctlr[0];
	Ctlr *ctlr = softctlr[0];
	char buf[256];

	USED(c);
	sprint(buf, "in: %d\nout: %d\ncrc errs %d\noverflows: %d\nframe errs %d\nbuff errs: %d\noerrs %d\naddr: %.02x:%.02x:%.02x:%.02x:%.02x:%.02x\n",
		cp->inpackets, cp->outpackets, cp->crcs,
		cp->overflows, cp->frames, cp->buffs, cp->oerrs,
		cp->ea[0], cp->ea[1], cp->ea[2], cp->ea[3], cp->ea[4], cp->ea[5]);
		ctlr->inpackets, ctlr->outpackets, ctlr->crcs,
		ctlr->overflows, ctlr->frames, ctlr->buffs, ctlr->oerrs,
		ctlr->ea[0], ctlr->ea[1], ctlr->ea[2],
		ctlr->ea[3], ctlr->ea[4], ctlr->ea[5]);
	strncpy(p, buf, n);
}



@@ 313,104 343,113 @@ static void
typefill(Chan *c, char *p, int n)
{
	char buf[16];
	EtherType *tp;
	Type *type;

	tp = &ctlr[0].type[STREAMID(c->qid.path)];
	sprint(buf, "%d", tp->type);
	type = &softctlr[0]->type[STREAMID(c->qid.path)];
	sprint(buf, "%d", type->type);
	strncpy(p, buf, n);
}

static void
etherup(EtherCtlr *cp, void *data, int len)
etherup(Ctlr *ctlr, Etherpkt *pkt, int len)
{
	Etherpkt *p;
	int t;
	EtherType *tp;
	Type *type;
	Block *bp;

	p = data;
	t = (p->type[0]<<8)|p->type[1];
	for(tp = &cp->type[0]; tp < &cp->type[NType]; tp++){
		/*
		 *  check before locking just to save a lock
		 */
		if(tp->q == 0 || (t != tp->type && tp->type != -1))
			continue;
	t = (pkt->type[0]<<8)|pkt->type[1];
	for(type = &ctlr->type[0]; type < &ctlr->type[NType]; type++){

		/*
		 *  only a trace channel gets packets destined for other machines
		 * Check for open, the right type, and flow control.
		 */
		if(tp->type != -1 && p->d[0] != 0xFF && memcmp(p->d, cp->ea, sizeof(p->d)))
		if(type->q == 0)
			continue;
		if(t != type->type && type->type != -1)
			continue;
		if(type->q->next->len > Streamhi)
			continue;

		/*
		 *  check after locking to make sure things didn't
		 *  change under foot
		 * Only a trace channel gets packets destined for other machines.
		 */
		if(canqlock(tp) == 0)
			continue;
		if(tp->q == 0 || tp->q->next->len > Streamhi || (t != tp->type && tp->type != -1)){
			qunlock(tp);
		if(type->type != -1 && pkt->d[0] != 0xFF
		  && (*pkt->d != *ctlr->ea || memcmp(pkt->d, ctlr->ea, sizeof(pkt->d))))
			continue;
		}
		if(!waserror()){

		if(waserror() == 0){
			bp = allocb(len);
			memmove(bp->rptr, p, len);
			memmove(bp->rptr, pkt, len);
			bp->wptr += len;
			bp->flags |= S_DELIM;
			PUTNEXT(tp->q, bp);
			PUTNEXT(type->q, bp);
			poperror();
		}
		qunlock(tp);
	}
}

static int
isinput(void *arg)
{
	EtherCtlr *cp = arg;
	Ctlr *ctlr = arg;

	return cp->lbq.first || cp->rb[cp->rh].owner == Host;
	return ctlr->lbq.first || ctlr->rb[ctlr->rh].owner == Host || ctlr->clist;
}

static void
etherkproc(void *arg)
{
	EtherCtlr *cp = arg;
	EtherBuf *rb;
	Ctlr *ctlr = arg;
	RingBuf *ring;
	Block *bp;
	Type *type;

	if(waserror()){
		print("%s noted\n", cp->name);
		if(cp->hw->init)
			(*cp->hw->init)(cp);
		cp->kproc = 0;
		print("%s noted\n", ctlr->name);
		/* fix
		if(ctlr->board->reset)
			(*ctlr->board->reset)(ctlr);
		 */
		ctlr->kproc = 0;
		nexterror();
	}
	cp->kproc = 1;

	for(;;){
		tsleep(&cp->rr, isinput, cp, 500);
		if(cp->hw->tweak)
			(*cp->hw->tweak)(cp);
		tsleep(&ctlr->rr, isinput, ctlr, 500);
		if(ctlr->board->watch)
			(*ctlr->board->watch)(ctlr);

		/*
		 * process any internal loopback packets
		 * Process any internal loopback packets.
		 */
		while(bp = getq(&cp->lbq)){
			cp->inpackets++;
			etherup(cp, bp->rptr, BLEN(bp));
		while(bp = getq(&ctlr->lbq)){
			ctlr->inpackets++;
			etherup(ctlr, (Etherpkt*)bp->rptr, BLEN(bp));
			freeb(bp);
		}

		/*
		 * process any received packets
		 * Process any received packets.
		 */
		while(cp->rb[cp->rh].owner == Host){
			cp->inpackets++;
			rb = &cp->rb[cp->rh];
			etherup(cp, rb->pkt, rb->len);
			rb->owner = Interface;
			cp->rh = NEXT(cp->rh, cp->nrb);
		while(ctlr->rb[ctlr->rh].owner == Host){
			ctlr->inpackets++;
			ring = &ctlr->rb[ctlr->rh];
			etherup(ctlr, (Etherpkt*)ring->pkt, ring->len);
			ring->owner = Interface;
			ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
		}

		/*
		 * Close Types requesting it.
		 */
		if(ctlr->clist){
			lock(&ctlr->clock);
			for(type = ctlr->clist; type; type = type->clist){
				type->q = 0;
				wakeup(&type->cr);
			}
			ctlr->clist = 0;
			unlock(&ctlr->clock);
		}
	}
}


@@ 418,75 457,105 @@ etherkproc(void *arg)
static void
etherintr(Ureg *ur)
{
	EtherCtlr *cp = &ctlr[0];
	Ctlr *ctlr = softctlr[0];

	USED(ur);
	(*cp->hw->intr)(cp);
	(*ctlr->board->intr)(ctlr);
}

void
etherreset(void)
{
	EtherCtlr *cp;
	EtherHw **hw;
	Ctlr *ctlr;
	Board **board;
	int i;

	cp = &ctlr[0];
	for(hw = etherhw; *hw; hw++){
		cp->hw = *hw;
		if((*cp->hw->reset)(cp) == 0){
			cp->present = 1;
			setvec(Int0vec + cp->hw->irq, etherintr);
	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){
			ctlr->present = 1;

			/*
			 * IRQ2 doesn't really exist, it's used to gang the interrupt
			 * 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);
			break;
		}
	}
	if(cp->present == 0)
	if(ctlr->present == 0)
		return;

	memset(cp->ba, 0xFF, sizeof(cp->ba));

	cp->net.name = "ether";
	cp->net.nconv = NType;
	cp->net.devp = &info;
	cp->net.protop = 0;
	cp->net.listen = 0;
	cp->net.clone = clonecon;
	cp->net.ninfo = 2;
	cp->net.info[0].name = "stats";
	cp->net.info[0].fill = statsfill;
	cp->net.info[1].name = "type";
	cp->net.info[1].fill = typefill;
	nctlr++;

	if(ctlr->nrb == 0)
		ctlr->nrb = Nrb;
	ctlr->rb = xalloc(sizeof(RingBuf)*ctlr->nrb);
	if(ctlr->ntb == 0)
		ctlr->ntb = Ntb;
	ctlr->tb = xalloc(sizeof(RingBuf)*ctlr->ntb);

	memset(ctlr->ba, 0xFF, sizeof(ctlr->ba));

	ctlr->net.name = "ether";
	ctlr->net.nconv = NType;
	ctlr->net.devp = &info;
	ctlr->net.protop = 0;
	ctlr->net.listen = 0;
	ctlr->net.clone = clonecon;
	ctlr->net.ninfo = 2;
	ctlr->net.info[0].name = "stats";
	ctlr->net.info[0].fill = statsfill;
	ctlr->net.info[1].name = "type";
	ctlr->net.info[1].fill = typefill;
	for(i = 0; i < NType; i++)
		netadd(&cp->net, &cp->type[i], i);
		netadd(&ctlr->net, &ctlr->type[i], i);
}

void
etherinit(void)
{
	int ctlrno = 0;
	EtherCtlr *cp = &ctlr[ctlrno];
	Ctlr *ctlr = softctlr[ctlrno];
	int i;

	if(cp->present == 0)
	if(ctlr->present == 0)
		return;

	cp->rh = 0;
	cp->ri = 0;
	for(i = 0; i < cp->nrb; i++)
		cp->rb[i].owner = Interface;
	ctlr->rh = 0;
	ctlr->ri = 0;
	for(i = 0; i < ctlr->nrb; i++)
		ctlr->rb[i].owner = Interface;

	ctlr->th = 0;
	ctlr->ti = 0;
	for(i = 0; i < ctlr->ntb; i++)
		ctlr->tb[i].owner = Host;
}

Chan*
etherattach(char *spec)
{
	int ctlrno = 0;
	Ctlr *ctlr = softctlr[ctlrno];

	cp->th = 0;
	cp->ti = 0;
	for(i = 0; i < cp->ntb; i++)
		cp->tb[i].owner = Host;
	if(ctlr->present == 0)
		error(Enodev);

	/*
	 * put the receiver online
	 * and start the kproc
	 * Enable the interface
	 * and start the kproc.
	 */	
	(*cp->hw->online)(cp, 1);
	if(cp->kproc == 0){
		sprint(cp->name, "ether%dkproc", ctlrno);
		kproc(cp->name, etherkproc, cp);
	(*ctlr->board->attach)(ctlr);
	if(ctlr->kproc == 0){
		sprint(ctlr->name, "ether%dkproc", ctlrno);
		ctlr->kproc = 1;
		kproc(ctlr->name, etherkproc, ctlr);
	}
	return devattach('l', spec);
}

A pc/ether.h => pc/ether.h +142 -0
@@ 0,0 1,142 @@
/*
 * All the goo for PC ethernet cards.
 */
typedef struct Board Board;
typedef struct RingBuf RingBuf;
typedef struct Type Type;
typedef struct Ctlr Ctlr;

/*
 * Hardware interface.
 */
struct Board {
	int	(*reset)(Ctlr*);
	void	(*init)(Ctlr*);
	void	(*attach)(Ctlr*);
	void	(*mode)(Ctlr*, int);

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

	ulong	io;			/* interface 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 */

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

/*
 * Software ring buffer.
 */
struct RingBuf {
	uchar	owner;
	uchar	busy;
	ushort	len;
	uchar	pkt[sizeof(Etherpkt)];
};

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

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

/*
 * One per ethernet packet type.
 */
struct Type {
	QLock;
	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 */
};

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

/*
 * Software controller.
 */
struct Ctlr {
	QLock;

	Board	*board;
	int	present;

	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 */

	Rendez	rr;		/* rendezvous for a receive buffer */
	ushort	rh;		/* first receive buffer belonging to host */
	ushort	ri;		/* first receive 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 interface */	

	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 */
	Network	net;

	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 */
};

#define NEXT(x, l)	(((x)+1)%(l))
#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

/*
 * The Western Digital 8003 and 8013 series, the NE2000
 * and the 3Com 503 all use the DP8390 Network Interface
 * Chip.
 */
extern void dp8390reset(Ctlr*);
extern void dp8390attach(Ctlr*);
extern void dp8390mode(Ctlr*, int);
extern void dp8390setea(Ctlr*);
extern void *dp8390read(Ctlr*, void*, ulong, ulong);
extern void dp8390receive(Ctlr*);
extern void dp8390transmit(Ctlr*);
extern void dp8390intr(Ctlr*);

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

A pc/ether2000.c => pc/ether2000.c +108 -0
@@ 0,0 1,108 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"

#include "ether.h"

/*
 * Driver written for the 'Notebook Computer Ethernet LAN Adapter',
 * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL
 * 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.
 *
 * The NE2000 is really just a DP8390 plus a data port
 * and a reset port.
 */
enum {
	Data		= 0x10,		/* offset from I/O base of data port */
	Reset		= 0x18,		/* offset from I/O base of reset port */
};

static int
reset(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	ushort buf[16];
	int i;

	/*
	 * We look for boards.
	 * We look for boards to make us barf.
	 */
	for(board->io = 0x300; board->io < 0x380; board->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));

		/*
		 * Init the (possible) chip, then use the (possible)
		 * chip to read the (possible) PROM for ethernet address
		 * and a marker byte.
		 * We could just look at the DP8390 command register after
		 * initialisation has been tried, but that wouldn't be
		 * enough, there are other ethernet boards which could
		 * match.
		 */
		board->dp8390 = board->io;
		board->data = board->io+Data;
		dp8390reset(ctlr);
		memset(buf, 0, sizeof(buf));
		dp8390read(ctlr, buf, 0, sizeof(buf));
		if(buf[0x0E] == 0x57 && buf[0x0F] == 0x57)
			break;
	}
	if(board->io >= 0x380)
		return -1;

	/*
	 * Stupid machine. We asked for shorts, we got shorts,
	 * although the PROM is a byte array.
	 * Now we can set the ethernet address.
	 */
	for(i = 0; i < sizeof(ctlr->ea); i++)
		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 */

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

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

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

A pc/ether503.c => pc/ether503.c +43 -0
@@ 0,0 1,43 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"

#include "ether.h"

static int
reset(Ctlr *ctlr)
{
	USED(ctlr);

	return -1;
}

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 */
};

M pc/ether509.c => pc/ether509.c +95 -121
@@ 7,15 7,7 @@
#include "io.h"
#include "devtab.h"

#define NEXT(x, l)	(((x)+1)%(l))
#define OFFSETOF(t, m)	((unsigned)&(((t*)0)->m))
#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

enum {
	Nrb		= 16,		/* software receive buffers */
	Ntb		= 4,		/* software transmit buffers */
};
#include "ether.h"

enum {
	IDport		= 0x0100,	/* anywhere between 0x0100 and 0x01F0 */


@@ 81,14 73,14 @@ enum {
	RxEmpty		= 0x8000,	/* Incomplete or FIFO empty */
};

#define COMMAND(hw, cmd, a)	outs(hw->addr+Command, ((cmd)<<11)|(a))
#define COMMAND(board, cmd, a)	outs(board->io+Command, ((cmd)<<11)|(a))

/*
 *  Write two 0 bytes to identify the IDport and them reset the
 *  ID sequence.  Then send the ID sequenced to the card to get
 *  the card it into command state.
 * Write two 0 bytes to identify the IDport and then reset the
 * ID sequence. Then send the ID sequence to the card to get
 * the card into command state.
 */
void
static void
idseq(void)
{
	int i;


@@ 108,26 100,21 @@ idseq(void)
}

/*
 * get configuration parameters
 * Get configuration parameters.
 */
static int
reset(EtherCtlr *cp)
reset(Ctlr *ctlr)
{
	EtherHw *hw = cp->hw;
	Board *board = ctlr->board;
	int i, ea;
	ushort x, acr;

	cp->rb = xspanalloc(sizeof(EtherBuf)*Nrb, BY2PG, 0);
	cp->nrb = Nrb;
	cp->tb = xspanalloc(sizeof(EtherBuf)*Ntb, BY2PG, 0);
	cp->ntb = Ntb;

	/*
	 * Do the little configuration dance. We only look
	 * at the first board that responds, if we ever have more
	 * than one we'll need to modify this sequence.
	 *
	 * 2. get to cammand state, reset, then return to command state
	 * 2. get to command state, reset, then return to command state
	 */
	idseq();
	outb(IDport, 0xC1);


@@ 174,8 161,8 @@ reset(EtherCtlr *cp)
	 *
	 *    Enable the adapter. 
	 */
	hw->addr = (acr & 0x1F)*0x10 + 0x200;
	outb(hw->addr+ConfigControl, 0x01);
	board->io = (acr & 0x1F)*0x10 + 0x200;
	outb(board->io+ConfigControl, 0x01);

	/*
	 * Read the IRQ from the Resource Configuration Register


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

	/*


@@ 201,58 188,38 @@ reset(EtherCtlr *cp)
	 * Commands have the format 'CCCCCAAAAAAAAAAA' where C
	 * is a bit in the command and A is a bit in the argument.
	 */
	COMMAND(hw, SelectWindow, 2);
	COMMAND(board, SelectWindow, 2);
	for(i = 0; i < 6; i++)
		outb(hw->addr+i, cp->ea[i]);
		outb(board->io+i, ctlr->ea[i]);

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

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

	print("3C509 I/O addr %lux irq %d:", hw->addr, hw->irq);
	for(i = 0; i < sizeof(cp->ea); i++)
		print(" %2.2ux", cp->ea[i]);
	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
mode(EtherCtlr *cp, int on)
{
	EtherHw *hw = cp->hw;

	qlock(cp);
	if(on){
		cp->prom++;
		if(cp->prom == 1)
			COMMAND(hw, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr);
	}
	else{
		cp->prom--;
		if(cp->prom == 0)
			COMMAND(hw, SetRxFilter, Broadcast|MyEtherAddr);
	}
	qunlock(cp);
}

static void
online(EtherCtlr *cp, int on)
attach(Ctlr *ctlr)
{
	EtherHw *hw = cp->hw;
	Board *board = ctlr->board;

	USED(on);					/* BUG */
	/*
	 * Set the receiver packet filter for our own and
	 * and broadcast addresses, set the interrupt masks


@@ 261,35 228,44 @@ online(EtherCtlr *cp, int on)
	 * is the receiver interrupt. If the transmit FIFO fills up,
	 * we will also see TxAvailable interrupts.
	 */
	COMMAND(hw, SetRxFilter, Broadcast|MyEtherAddr);
	COMMAND(hw, SetReadZeroMask, AllIntr|Latch);
	COMMAND(hw, SetIntrMask, AllIntr|Latch);
	COMMAND(hw, RxEnable, 0);
	COMMAND(hw, TxEnable, 0);
	COMMAND(board, SetRxFilter, Broadcast|MyEtherAddr);
	COMMAND(board, SetReadZeroMask, AllIntr|Latch);
	COMMAND(board, SetIntrMask, AllIntr|Latch);
	COMMAND(board, RxEnable, 0);
	COMMAND(board, TxEnable, 0);
}

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

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

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

	COMMAND(hw, SelectWindow, 4);
	bytes = ins(hw->addr+0x04);
	COMMAND(hw, SelectWindow, 1);
	COMMAND(board, SelectWindow, 4);
	bytes = ins(board->io+0x04);
	COMMAND(board, SelectWindow, 1);
	return bytes & 0xFFFF;
}

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

	while(((status = ins(hw->addr+RxStatus)) & RxEmpty) == 0){

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


@@ 298,19 274,19 @@ receive(EtherCtlr *cp)
			switch(status & RxErrMask){

			case RxErrOverrun:	/* Overrrun */
				cp->overflows++;
				ctlr->overflows++;
				break;

			case RxErrOversize:	/* Oversize Packet (>1514) */
			case RxErrRunt:		/* Runt Packet */
				cp->buffs++;
				ctlr->buffs++;
				break;
			case RxErrFraming:	/* Alignment (Framing) */
				cp->frames++;
				ctlr->frames++;
				break;

			case RxErrCRC:		/* CRC */
				cp->crcs++;
				ctlr->crcs++;
				break;
			}
		}


@@ 320,7 296,7 @@ receive(EtherCtlr *cp)
			 * free ring buffer, if any.
			 * The CRC is already stripped off.
			 */
			rb = &cp->rb[cp->ri];
			rb = &ctlr->rb[ctlr->ri];
			if(rb->owner == Interface){
				len = (status & RxByteMask);
				rb->len = len;


@@ 330,44 306,44 @@ receive(EtherCtlr *cp)
				 * doubleword. We can pick them out 16-bits
				 * at a time (can try 32-bits at a time
				 * later).
				insl(hw->addr+Fifo, rb->pkt, HOWMANY(len, 4));
				insl(board->io+Fifo, rb->pkt, HOWMANY(len, 4));
				 */
				inss(hw->addr+Fifo, rb->pkt, HOWMANY(len, 2));
				inss(board->io+Fifo, rb->pkt, HOWMANY(len, 2));

				/*
				 * Update the ring.
				 */
				rb->owner = Host;
				cp->ri = NEXT(cp->ri, cp->nrb);
				ctlr->ri = NEXT(ctlr->ri, ctlr->nrb);
			}
		}

		/*
		 * Discard the packet as we're done with it.
		 * Wait for discard to complete.
		 */
		COMMAND(hw, RxDiscard, 0);
		while(ins(hw->addr+Status) & CmdInProgress)
		COMMAND(board, RxDiscard, 0);
		while(ins(board->io+Status) & CmdInProgress)
			;
	}
}

static void
transmit(EtherCtlr *cp)
transmit(Ctlr *ctlr)
{
	EtherHw *hw = cp->hw;
	EtherBuf *tb;
	Board *board = ctlr->board;
	RingBuf *tb;
	int s;
	ushort len;

	s = splhi();
	for(tb = &cp->tb[cp->ti]; tb->owner == Interface; tb = &cp->tb[cp->ti]){

	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,
		 * set up an interrupt for when space becomes available.
		 */
		if(tb->len > ins(hw->addr+TxFreeBytes)){
			COMMAND(hw, SetTxAvailable, tb->len);
		if(tb->len > ins(board->io+TxFreeBytes)){
			COMMAND(board, SetTxAvailable, tb->len);
			break;
		}



@@ 377,27 353,27 @@ transmit(EtherCtlr *cp)
		 * Output packet must be a multiple of 4 in length.
		 */
		len = ROUNDUP(tb->len, 4)/2;
		outs(hw->addr+Fifo, tb->len);
		outs(hw->addr+Fifo, 0);
		outss(hw->addr+Fifo, tb->pkt, len);
		outs(board->io+Fifo, tb->len);
		outs(board->io+Fifo, 0);
		outss(board->io+Fifo, tb->pkt, len);
		tb->owner = Host;
		cp->ti = NEXT(cp->ti, cp->ntb);
		ctlr->ti = NEXT(ctlr->ti, ctlr->ntb);
	}
	splx(s);
}

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

	status = ins(hw->addr+Status);
	status = ins(board->io+Status);

	if(status & RxComplete){
		(*cp->hw->receive)(cp);
		wakeup(&cp->rr);
		receive(ctlr);
		wakeup(&ctlr->rr);
		status &= ~RxComplete;
	}



@@ 410,14 386,15 @@ interrupt(EtherCtlr *cp)
		 */
		txstatus = 0;
		do{
			if(x = inb(hw->addr+TxStatus))
				outb(hw->addr+TxStatus, 0);
			if(x = inb(board->io+TxStatus))
				outb(board->io+TxStatus, 0);
			txstatus |= x;
		}while(ins(hw->addr+Status) & TxComplete);
		}while(ins(board->io+Status) & TxComplete);

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

	if(status & (TxAvailable|TxComplete)){


@@ 425,33 402,30 @@ interrupt(EtherCtlr *cp)
		 * Reset the Tx FIFO threshold.
		 */
		if(status & TxAvailable)
			COMMAND(hw, AckIntr, TxAvailable);
		(*cp->hw->transmit)(cp);
		wakeup(&cp->tr);
			COMMAND(board, AckIntr, TxAvailable);
		transmit(ctlr);
		wakeup(&ctlr->tr);
		status &= ~(TxAvailable|TxComplete);
	}

	/*
	 * Panic if there are any interrupt bits on we haven't
	 * dealt with other than Latch.
	 * Otherwise, acknowledge the interrupt.
	 */
	if(status & AllIntr)
		panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(hw));
		panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(board));

	/*
	 * Acknowledge the interrupt.
	 */
	COMMAND(hw, AckIntr, Latch);
	COMMAND(board, AckIntr, Latch);
}

EtherHw ether509 = {
Board ether509 = {
	reset,
	0,					/* init */
	0,			/* init */
	attach,
	mode,
	online,
	receive,
	0,			/* receive */
	transmit,
	interrupt,
	0,					/* tweak */
	0,					/* I/O base address */
	0,			/* watch */
};

A pc/ether8003.c => pc/ether8003.c +204 -0
@@ 0,0 1,204 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"

#include "ether.h"

enum {					/* 83C584 Bus Interface Controller */
	Msr		= 0x00,		/* Memory Select Register */
	Icr		= 0x01,		/* Interface Configuration Register */
	Iar		= 0x02,		/* I/O Address Register */
	Bio		= 0x03,		/* BIOS ROM Address Register */
	Irr		= 0x04,		/* Interrupt Request Register */
	Laar		= 0x05,		/* LA Address Register */
	Ijr		= 0x06,		/* Initialisation Jumpers */
	Gp2		= 0x07,		/* General Purpose Data Register */
	Lar		= 0x08,		/* LAN Address Registers */
	Id		= 0x0E,		/* Board ID byte */
	Cksum		= 0x0F,		/* Checksum */
};

enum {					/* Msr */
	Rst		= 0x80,		/* software reset */
	Menb		= 0x40,		/* memory enable */
};

enum {					/* Laar */
	ZeroWS16	= (1<<5),	/* zero wait states for 16-bit ops */
	L16en		= (1<<6),	/* enable 16-bit LAN operation */
	M16en		= (1<<7),	/* enable 16-bit memory access */
};

/*
 * Mapping from configuration bits to interrupt level.
 */
static int intrmap[] = {
	9, 3, 5, 7, 10, 11, 15, 4,
};

/*
 * Get configuration parameters, enable memory.
 */
static int
reset(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	int i;
	uchar msr, icr, laar, irr, sum;

	/*
	 * Look for the interface. We read the LAN address ROM
	 * and validate the checksum - the sum of all 8 bytes
	 * should be 0xFF.
	 */
	for(board->io = 0x200; board->io < 0x400; board->io += 0x20){
		sum = 0;
		for(i = 0; i < sizeof(ctlr->ea); i++){
			ctlr->ea[i] = inb(board->io+Lar+i);
			sum += ctlr->ea[i];
		}
		sum += inb(board->io+Id);
		sum += inb(board->io+Cksum);
		if(sum == 0xFF)
			break;
	}
	if(board->io >= 0x400)
		return -1;

	/*
	 * Found it, reset it.
	 * 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);
	delay(1);
	outb(board->io+Msr, msr);
	delay(2);

	/*
	 * Check for old, dumb 8003E, which doesn't have an interface
	 * chip. Only the msr exists out of the 1st eight registers, reads
	 * of the others just alias the 2nd eight registers, the LAN
	 * address ROM. We can check icr, irr and laar against the ethernet
	 * address read above and if they match it's an 8003E (or an
	 * 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);
	if(icr != ctlr->ea[1] || irr != ctlr->ea[4] || laar != ctlr->ea[5])
		board->irq = intrmap[((irr>>5) & 0x3)|(icr & 0x4)];
	else {
		msr = (((ulong)board->ramstart)>>13) & 0x3F;
		icr = laar = 0;
		board->watch = 0;
	}

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

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

	if(icr & (1<<3))
		board->ramstop = 32*1024;
	else
		board->ramstop = 8*1024;
	if(board->bit16)
		board->ramstop <<= 1;
	board->ramstop += board->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("WD80x3 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");

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

	/*
	 * Finally, init the 8390 and set the
	 * ethernet address.
	 */
	dp8390reset(ctlr);
	dp8390setea(ctlr);

	return 0;
}

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

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

		dp8390reset(ctlr);
		etherinit();

		wakeup(&ctlr->tr);
		wakeup(&ctlr->rr);
	}
	splx(s);
}

/*
 * Defaults are set for the dumb 8003E
 * which can't be autoconfigured.
 */
Board ether8003 = {
	reset,
	0,			/* init */
	dp8390attach,
	dp8390mode,
	dp8390receive,
	dp8390transmit,
	dp8390intr,
	watch,

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

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

A pc/ether8390.c => pc/ether8390.c +561 -0
@@ 0,0 1,561 @@
/*
 * National Semiconductor DP8390
 * and SMC 83C90
 * Network Interface Controller.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"

#include "ether.h"

enum {
	Cr		= 0x00,		/* command register, all pages */

	Stp		= 0x01,		/* stop */
	Sta		= 0x02,		/* start */
	Txp		= 0x04,		/* transmit packet */
	RDMAread	= (1<<3),	/* remote DMA read */
	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),
};

enum {					/* Page 0, read */
	Clda0		= 0x01,		/* current local DMA address 0 */
	Clda1		= 0x02,		/* current local DMA address 1 */
	Bnry		= 0x03,		/* boundary pointer (R/W) */
	Tsr		= 0x04,		/* transmit status register */
	Ncr		= 0x05,		/* number of collisions register */
	Fifo		= 0x06,		/* FIFO */
	Isr		= 0x07,		/* interrupt status register (R/W) */
	Crda0		= 0x08,		/* current remote DMA address 0 */
	Crda1		= 0x08,		/* current remote DMA address 1 */
	Rsr		= 0x0C,		/* receive status register */
	Cntr0		= 0x0D,		/* frame alignment errors */
	Cntr1		= 0x0E,		/* CRC errors */
	Cntr2		= 0x0F,		/* missed packet errors */
};

enum {					/* Page 0, write */
	Pstart		= 0x01,		/* page start register */
	Pstop		= 0x02,		/* page stop register */
	Tpsr		= 0x04,		/* transmit page start address */
	Tbcr0		= 0x05,		/* transmit byte count register 0 */
	Tbcr1		= 0x06,		/* transmit byte count register 1 */
	Rsar0		= 0x08,		/* remote start address register 0 */
	Rsar1		= 0x09,		/* remote start address register 1 */
	Rbcr0		= 0x0A,		/* remote byte count register 0 */
	Rbcr1		= 0x0B,		/* remote byte count register 1 */
	Rcr		= 0x0C,		/* receive configuration register */
	Tcr		= 0x0D,		/* transmit configuration register */
	Dcr		= 0x0E,		/* data configuration register */
	Imr		= 0x0F,		/* interrupt mask */
};

enum {					/* Page 1, read/write */
	Par0		= 0x01,		/* physical address register 0 */
	Curr		= 0x07,		/* current page register */
	Mar0		= 0x08,		/* multicast address register 0 */
};

enum {					/* Interrupt Status Register */
	Prx		= 0x01,		/* packet received */
	Ptx		= 0x02,		/* packet transmitted */
	Rxe		= 0x04,		/* receive error */
	Txe		= 0x08,		/* transmit error */
	Ovw		= 0x10,		/* overwrite warning */
	Cnt		= 0x20,		/* counter overflow */
	Rdc		= 0x40,		/* remote DMA complete */
	Rst		= 0x80,		/* reset status */
};

enum {					/* Interrupt Mask Register */
	Prxe		= 0x01,		/* packet received interrupt enable */
	Ptxe		= 0x02,		/* packet transmitted interrupt enable */
	Rxee		= 0x04,		/* receive error interrupt enable */
	Txee		= 0x08,		/* transmit error interrupt enable */
	Ovwe		= 0x10,		/* overwrite warning interrupt enable */
	Cnte		= 0x20,		/* counter overflow interrupt enable */
	Rdce		= 0x40,		/* DMA complete interrupt enable */
};

enum {					/* Data Configuration register */
	Wts		= 0x01,		/* word transfer select */
	Bos		= 0x02,		/* byte order select */
	Las		= 0x04,		/* long address select */
	Ls		= 0x08,		/* loopback select */
	Arm		= 0x10,		/* auto-initialise remote */
	Ft1		= (0x00<<5),	/* FIFO threshhold select 1 byte/word */
	Ft2		= (0x01<<5),	/* FIFO threshhold select 2 bytes/words */
	Ft4		= (0x02<<5),	/* FIFO threshhold select 4 bytes/words */
	Ft6		= (0x03<<5),	/* FIFO threshhold select 6 bytes/words */
};

enum {					/* Transmit Configuration Register */
	Crc		= 0x01,		/* inhibit CRC */
	Lb		= 0x02,		/* internal loopback */
	Atd		= 0x08,		/* auto transmit disable */
	Ofst		= 0x10,		/* collision offset enable */
};

enum {					/* Transmit Status Register */
	Ptxok		= 0x01,		/* packet transmitted */
	Col		= 0x04,		/* transmit collided */
	Abt		= 0x08,		/* tranmit aborted */
	Crs		= 0x10,		/* carrier sense lost */
	Fu		= 0x20,		/* FIFO underrun */
	Cdh		= 0x40,		/* CD heartbeat */
	Owc		= 0x80,		/* out of window collision */
};

enum {					/* Receive Configuration Register */
	Sep		= 0x01,		/* save errored packets */
	Ar		= 0x02,		/* accept runt packets */
	Ab		= 0x04,		/* accept broadcast */
	Am		= 0x08,		/* accept multicast */
	Pro		= 0x10,		/* promiscuous physical */
	Mon		= 0x20,		/* monitor mode */
};

enum {					/* Receive Status Register */
	Prxok		= 0x01,		/* packet received intact */
	Crce		= 0x02,		/* CRC error */
	Fae		= 0x04,		/* frame alignment error */
	Fo		= 0x08,		/* FIFO overrun */
	Mpa		= 0x10,		/* missed packet */
	Phy		= 0x20,		/* physical/multicast address */
	Dis		= 0x40,		/* receiver disabled */
	Dfr		= 0x80,		/* deferring */
};

typedef struct {
	uchar	status;
	uchar	next;
	uchar	len0;
	uchar	len1;
} Hdr;

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

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

	/*
	 * Stop the chip. Set the Stp bit and wait for the chip
	 * to finish whatever was on its tiny mind before it sets
	 * the Rst bit.
	 * We need the timeout because there may not be a real
	 * 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--)
			;
}

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

	/*
	 * This is the initialisation procedure described
	 * as 'mandatory' in the datasheet.
	 */ 
	dp8390disable(ctlr);
	if(board->bit16)
		outb(board->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);

	outb(board->dp8390+Bnry, board->pstart);
	outb(board->dp8390+Pstart, board->pstart);
	outb(board->dp8390+Pstop, board->pstop);

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

	outb(board->dp8390+Cr, Page1|RDMAabort|Stp);

	/*
	 * Can't initialise ethernet address as we may
	 * not know it yet.
	 */
	outb(board->dp8390+Curr, board->pstart+1);

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

	outb(board->dp8390+Tpsr, board->tstart);
}

void
dp8390attach(Ctlr *ctlr)
{
	/*
	 * Enable the chip for transmit/receive.
	 * The init routine leaves the chip in internal
	 * loopback.
	 */
	outb(ctlr->board->dp8390+Tcr, 0);
}

void
dp8390mode(Ctlr *ctlr, int on)
{
	if(on)
		outb(ctlr->board->dp8390+Rcr, Pro|Ab);
	else
		outb(ctlr->board->dp8390+Rcr, Ab);
}

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

	/*
	 * Set the ethernet address into the chip.
	 * Take care to restore the command register
	 * afterwards. We don't care about multicast
	 * addresses as we never set the multicast
	 * enable.
	 */
	cr = inb(board->dp8390+Cr);
	outb(board->dp8390+Cr, Page1|(~Page0 & cr));
	for(i = 0; i < sizeof(ctlr->ea); i++)
		outb(board->dp8390+Par0+i, ctlr->ea[i]);
	outb(board->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
	 * 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);

	/*
	 * Set up the remote DMA address and count.
	 */
	if(board->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);

	/*
	 * 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);
	else
		insb(board->data, to, len);

	/*
	 * Wait for the remote DMA to complete. The timeout
	 * is necessary because we may call this routine on
	 * a non-existent chip during initialisation and, due
	 * 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--)
			;

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

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

	/*
	 * 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
	 * 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);

	/*
	 * Set up the remote DMA address and count.
	 * This is straight from the datasheet, hence
	 * the initial write to Rbcr0 and Cr.
	 */
	outb(board->dp8390+Rbcr0, 0xFF);
	outb(board->dp8390+Cr, Page0|RDMAread|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);

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

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

void
dp8390receive(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	RingBuf *ring;
	uchar bnry, curr, next;
	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;

		/*
		 * 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.
		 *
		 * We peek at the current local DMA registers and, if they are
		 * changing, wait.
		 */
		if(strcmp(arch->id, "NCRD.0") == 0){
			uchar a, b, c;
		
			for(;;delay(10)){
				a = inb(board->dp8390+Clda0);
				b = inb(board->dp8390+Clda0);
				if(a != b)
					continue;
				c = inb(board->dp8390+Clda0);
				if(c != b)
					continue;
				break;
			}
		}

		ctlr->inpackets++;

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

		/*
		 * Chip is badly scrogged, reinitialise it.
		 * dp8390reset() calls the disable function.
		 * There's no need to reload the ethernet
		 * address.
		 *
		 * Untested.
		 */
		if(hdr.next < board->pstart || hdr.next >= board->pstop || len < 60){
print("scrogged\n");
			dp8390reset(ctlr);
			dp8390attach(ctlr);
			return;
		}

		ring = &ctlr->rb[ctlr->ri];
		if(ring->owner == Interface){
			ring->len = len;
			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);
			}
			dp8390read(ctlr, ring->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);
	}
}

void
dp8390transmit(Ctlr *ctlr)
{
	Board *board;
	RingBuf *ring;
	int s;

	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;
	}
	splx(s);
}

void
dp8390intr(Ctlr *ctlr)
{
	Board *board = ctlr->board;
	RingBuf *ring;
	uchar isr;

	/*
	 * While there is something of interest,
	 * clear all the interrupts and process.
	 */
	while(isr = inb(board->dp8390+Isr)){
		outb(board->dp8390+Isr, 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.
			 */
			ctlr->overflows++;

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

		/*
		 * We have received packets.
		 * Take a spin round the ring and
		 * wakeup the kproc.
		 */
		if(isr & (Rxe|Prx)){
			(*ctlr->board->receive)(ctlr);
			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)){
			ring = &ctlr->tb[ctlr->ti];
			ring->owner = Host;
			ring->busy = 0;
			ctlr->ti = NEXT(ctlr->ti, ctlr->ntb);
			(*ctlr->board->transmit)(ctlr);
			wakeup(&ctlr->tr);
		}
	}
}

M pc/io.h => pc/io.h +0 -94
@@ 25,97 25,3 @@ enum

	Syscallvec=	64,
};

typedef struct EtherHw EtherHw;
typedef struct EtherBuf EtherBuf;
typedef struct EtherType EtherType;
typedef struct EtherCtlr EtherCtlr;

struct EtherHw {
	int	(*reset)(EtherCtlr*);
	void	(*init)(EtherCtlr*);
	void	(*mode)(EtherCtlr*, int);
	void	(*online)(EtherCtlr*, int);
	void	(*receive)(EtherCtlr*);
	void	(*transmit)(EtherCtlr*);
	void	(*intr)(EtherCtlr*);
	void	(*tweak)(EtherCtlr*);
	int	addr;			/* interface address */
	uchar	*ram;			/* interface shared memory address */
	int	bt16;			/* true if a 16 bit interface */
	int	irq;			/* interrupt level */
	int	size;
	uchar	tstart;
	uchar	pstart;
	uchar	pstop;
};

struct EtherBuf {
	uchar	owner;
	uchar	busy;
	ushort	len;
	uchar	pkt[sizeof(Etherpkt)];
};

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

	NType		= 9,		/* types/interface */
};

/*
 * one per ethernet packet type
 */
struct EtherType {
	QLock;
	Netprot;			/* stat info */
	int	type;			/* ethernet type */
	int	prom;			/* promiscuous mode */
	Queue	*q;
	int	inuse;
	EtherCtlr *ctlr;
};

/*
 * per ethernet
 */
struct EtherCtlr {
	QLock;

	EtherHw	*hw;
	int	present;

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

	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	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 */	

	EtherType type[NType];
	uchar	prom;		/* true if promiscuous mode */
	uchar	kproc;		/* true if kproc started */
	char	name[NAMELEN];	/* name of kproc */
	Network	net;

	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 */
};