~kris/9p

9hist

22ffe4fa3c224cccb0684eb316590444c7b04845 — David du Colombier 34 years ago 07f4721
Plan 9 from Bell Labs 1992-09-22
9 files changed, 608 insertions(+), 600 deletions(-)

M pc/clock.c
M pc/devether.c
A pc/ether509.c
M pc/fns.h
M pc/io.h
M pc/l.s
M port/devscc.c
M port/portfns.h
M ss/main.c
M pc/clock.c => pc/clock.c +12 -0
@@ 20,6 20,7 @@ enum
					 *  output a square wave whose
					 *  period is the counter period
					 */
	Latch0=		0x06,
	Freq=		1193182,	/* Real clock frequency */
};



@@ 40,6 41,8 @@ delay(int l)
void
clockinit(void)
{
	ulong dc;	/* change in counter */

	/*
	 *  set vector for clock interrupts
	 */


@@ 51,6 54,15 @@ clockinit(void)
	outb(Tmode, Load0square);
	outb(T0cntr, (Freq/HZ));	/* low byte */
	outb(T0cntr, (Freq/HZ)>>8);	/* high byte */

	/*
	 *  measure cpu speed to make delay() system
	 *  independent
	 */
	delay(100);
	outb(Tmode, Latch0);
	dc = inb(T0cntr);
	dc |= inb(T0cntr)<<8;
}

void

M pc/devether.c => pc/devether.c +63 -589
@@ 7,113 7,33 @@
#include "io.h"
#include "devtab.h"

typedef struct Hw Hw;
typedef struct Buffer Buffer;
typedef struct Type Type;
typedef struct Ctlr Ctlr;

struct Hw {
	int	(*reset)(Ctlr*);
	void	(*init)(Ctlr*);
	void	(*mode)(Ctlr*, int);
	void	(*online)(Ctlr*, int);
	void	(*receive)(Ctlr*);
	void	(*transmit)(Ctlr*);
	void	(*intr)(Ureg*);
	void	(*tweek)(Ctlr*);
	int	addr;			/* interface address */
	uchar	*ram;			/* interface shared memory address */
	int	bt16;			/* true if a 16 bit interface */
	int	lvl;			/* interrupt level */
	int	size;
	uchar	tstart;
	uchar	pstart;
	uchar	pstop;
/*
 * Half-arsed attempt at a general top-level
 * ethernet driver. Needs work:
 *	handle multiple controllers
 *	much tidying
 *	set ethernet address
 */
extern EtherHw ether509;
extern EtherHw ether80x3;

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

enum {
	Nctlr		= 1,		/* even one of these is too many */
	NType		= 9,		/* types/interface */

	Nrb		= 16,		/* software receive buffers */
	Ntb		= 4,		/* software transmit buffers */
	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))

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

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

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

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

	Hw	*hw;
	int	present;

	ushort	nrb;		/* number of software receive buffers */
	ushort	ntb;		/* number of software transmit buffers */
	Buffer	*rb;		/* software receive buffers */
	Buffer	*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];
	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 */
};
static Ctlr ctlr[Nctlr];

Chan*
etherattach(char *spec)
{


@@ 189,7 109,7 @@ etherwstat(Chan *c, char *dp)
static int
isobuf(void *arg)
{
	Ctlr *cp = arg;
	EtherCtlr *cp = arg;

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


@@ 197,17 117,17 @@ isobuf(void *arg)
static void
etheroput(Queue *q, Block *bp)
{
	Ctlr *cp;
	int len, n;
	Type *tp;
	EtherCtlr *cp;
	EtherType *tp;
	Etherpkt *p;
	Buffer *tb;
	EtherBuf *tb;
	int len, n;
	Block *nbp;

	if(bp->type == M_CTL){
		tp = q->ptr;
		if(streamparse("connect", bp))
			tp->type = strtol((char *)bp->rptr, 0, 0);
			tp->type = strtol((char*)bp->rptr, 0, 0);
		else if(streamparse("promiscuous", bp)) {
			tp->prom = 1;
			(*tp->ctlr->hw->mode)(tp->ctlr, 1);


@@ 216,7 136,7 @@ etheroput(Queue *q, Block *bp)
		return;
	}

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

	/*
	 * give packet a local address, return upstream if destined for


@@ 224,7 144,7 @@ etheroput(Queue *q, Block *bp)
	 */
	if(BLEN(bp) < ETHERHDRSIZE && (bp = pullup(bp, ETHERHDRSIZE)) == 0)
		return;
	p = (Etherpkt *)bp->rptr;
	p = (Etherpkt*)bp->rptr;
	memmove(p->s, cp->ea, sizeof(cp->ea));
	if(memcmp(cp->ea, p->d, sizeof(cp->ea)) == 0){
		len = blen(bp);


@@ 308,8 228,8 @@ etheroput(Queue *q, Block *bp)
static void
etherstopen(Queue *q, Stream *s)
{
	Ctlr *cp = &ctlr[0];
	Type *tp;
	EtherCtlr *cp = &ctlr[0];
	EtherType *tp;

	tp = &cp->type[s->id];
	qlock(tp);


@@ 330,9 250,9 @@ etherstopen(Queue *q, Stream *s)
static void
etherstclose(Queue *q)
{
	Type *tp;
	EtherType *tp;

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


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

	USED(c);
	for(tp = cp->type; tp < &cp->type[NType]; tp++){


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

	USED(c);


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

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


@@ 401,11 321,11 @@ typefill(Chan *c, char *p, int n)
}

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

	p = data;


@@ 448,7 368,7 @@ etherup(Ctlr *cp, void *data, int len)
static int
isinput(void *arg)
{
	Ctlr *cp = arg;
	EtherCtlr *cp = arg;

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


@@ 456,20 376,22 @@ isinput(void *arg)
static void
etherkproc(void *arg)
{
	Ctlr *cp = arg;
	Buffer *rb;
	EtherCtlr *cp = arg;
	EtherBuf *rb;
	Block *bp;

	if(waserror()){
		print("%s noted\n", cp->name);
		(*cp->hw->init)(cp);
		if(cp->hw->init)
			(*cp->hw->init)(cp);
		cp->kproc = 0;
		nexterror();
	}
	cp->kproc = 1;
	for(;;){
		tsleep(&cp->rr, isinput, cp, 500);
		(*cp->hw->tweek)(cp);
		if(cp->hw->tweak)
			(*cp->hw->tweak)(cp);

		/*
		 * process any internal loopback packets


@@ 493,19 415,33 @@ etherkproc(void *arg)
	}
}

static void
etherintr(Ureg *ur)
{
	EtherCtlr *cp = &ctlr[0];

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

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

	cp = &ctlr[0];
	cp->hw = &wd8013;
	if((*cp->hw->reset)(cp) < 0){
		cp->present = 0;
		return;
	for(hw = etherhw; *hw; hw++){
		cp->hw = *hw;
		if((*cp->hw->reset)(cp) == 0){
			cp->present = 1;
			setvec(Int0vec + cp->hw->irq, etherintr);
			break;
		}
	}
	cp->present = 1;
	if(cp->present == 0)
		return;

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



@@ 528,7 464,7 @@ void
etherinit(void)
{
	int ctlrno = 0;
	Ctlr *cp = &ctlr[ctlrno];
	EtherCtlr *cp = &ctlr[ctlrno];
	int i;

	if(cp->present == 0)


@@ 554,465 490,3 @@ etherinit(void)
		kproc(cp->name, etherkproc, cp);
	}
}

typedef struct {
	uchar	msr;			/* 83C584 bus interface */
	uchar	icr;
	uchar	iar;
	uchar	bio;
	uchar	irr;
	uchar	laar;
	uchar	ijr;
	uchar	gp2;
	uchar	lan[6];
	uchar	id;
	uchar	cksum;

	union {				/* DP8390/83C690 LAN controller */
		struct {			/* Page0, read */
			uchar	cr;
			uchar	clda0;
			uchar	clda1;
			uchar	bnry;
			uchar	tsr;
			uchar	ncr;
			uchar	fifo;
			uchar	isr;
			uchar	crda0;
			uchar	crda1;
			uchar	pad0x0A;
			uchar	pad0x0B;
			uchar	rsr;
			uchar	cntr0;
			uchar	cntr1;
			uchar	cntr2;
		} r;
		struct {			/* Page0, write */
			uchar	cr;
			uchar	pstart;
			uchar	pstop;
			uchar	bnry;
			uchar	tpsr;
			uchar	tbcr0;
			uchar	tbcr1;
			uchar	isr;
			uchar	rsar0;
			uchar	rsar1;
			uchar	rbcr0;
			uchar	rbcr1;
			uchar	rcr;
			uchar	tcr;
			uchar	dcr;
			uchar	imr;
		} w;
		struct {			/* Page1, read/write */
			uchar	cr;
			uchar	par[6];
			uchar	curr;
			uchar	mar[8];
		};
		struct {			/* Page2, read */
			uchar	cr;
			uchar	pstart;
			uchar	pstop;
			uchar	dummy1[1];
			uchar	tstart;
			uchar	next;
			uchar	block;
			uchar	enh;
			uchar	dummy2[4];
			uchar	rcon;
			uchar	tcon;
			uchar	dcon;
			uchar	intmask;
		} r2;
		struct {			/* Page2, write */
			uchar	cr;
			uchar	trincrl;
			uchar	trincrh;
			uchar	dummy1[2];
			uchar	next;
			uchar	block;
			uchar	enh;
		} w2;
	};
} Wd8013;

enum {
	MENB		= 0x40,			/* memory enable */

	/* bit definitions for 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 */

	/* bit defintitions for DP8390/83C690 cr */
	Page0		= (0<<6),
	Page1		= (1<<6),
	Page2		= (2<<6),
	RD2		= (4<<3),
	TXP		= (1<<2),
	STA		= (1<<1),
	STP		= (1<<0),
};

#define IN(hw, m)	inb((hw)->addr+OFFSETOF(Wd8013, m))
#define OUT(hw, m, x)	outb((hw)->addr+OFFSETOF(Wd8013, m), (x))

typedef struct {
	uchar	status;
	uchar	next;
	uchar	len0;
	uchar	len1;
	uchar	data[256-4];
} Ring;

static void
wd8013dumpregs(Hw *hw)
{
	print("msr=#%2.2ux\n", IN(hw, msr));
	print("icr=#%2.2ux\n", IN(hw, icr));
	print("iar=#%2.2ux\n", IN(hw, iar));
	print("bio=#%2.2ux\n", IN(hw, bio));
	print("irr=#%2.2ux\n", IN(hw, irr));
	print("laar=#%2.2ux\n", IN(hw, laar));
	print("ijr=#%2.2ux\n", IN(hw, ijr));
	print("gp2=#%2.2ux\n", IN(hw, gp2));
	print("lan0=#%2.2ux\n", IN(hw, lan[0]));
	print("lan1=#%2.2ux\n", IN(hw, lan[1]));
	print("lan2=#%2.2ux\n", IN(hw, lan[2]));
	print("lan3=#%2.2ux\n", IN(hw, lan[3]));
	print("lan4=#%2.2ux\n", IN(hw, lan[4]));
	print("lan5=#%2.2ux\n", IN(hw, lan[5]));
	print("id=#%2.2ux\n", IN(hw, id));
}

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

/*
 *  get configuration parameters, enable memory
 */
static int
wd8013reset(Ctlr *cp)
{
	Hw *hw = cp->hw;
	int i;
	uchar msr, icr, laar, irr;
	ulong ram;

	/* find the ineterface */
	SET(msr, icr, irr, laar);
	for(hw->addr = 0x200; hw->addr < 0x400; hw->addr += 0x20){
		msr = IN(hw, msr);
		icr = IN(hw, icr);
		irr = IN(hw, irr);
		laar = IN(hw, laar);
		if((msr&0x80) || (icr&0xf0) || irr == 0xff || laar == 0xff)
			continue;	/* nothing there */

		/* ethernet address */
		for(i = 0; i < sizeof(cp->ea); i++)
			cp->ea[i] = IN(hw, lan[i]);

		/* look for an elite ether address */
		if(cp->ea[0] == 0x00 && cp->ea[1] == 0x00 && cp->ea[2] == 0xC0)
			break;
	}
	if(hw->addr >= 0x400)
		return -1;

	if(cp->rb == 0){
		cp->rb = xspanalloc(sizeof(Buffer)*Nrb, BY2PG, 0);
		cp->nrb = Nrb;
		cp->tb = xspanalloc(sizeof(Buffer)*Ntb, BY2PG, 0);
		cp->ntb = Ntb;
	}

	/* 16 bit operation? */
	hw->bt16 = icr & 0x1;

	/* address of interface RAM */
	ram = KZERO | ((msr & 0x3f) << 13);
	if(hw->bt16)
		ram |= (laar & 0x3f)<<19;
	else
		ram |= 0x80000;
	hw->ram = (uchar*)ram;

	/* interrupt level */
	hw->lvl = intrmap[((irr>>5) & 0x3) | (icr & 0x4)];

	/* ram size */
	if(icr&(1<<3))
		hw->size = 32*1024;
	else
		hw->size = 8*1024;
	if(hw->bt16)
		hw->size <<= 1;
	hw->pstart = HOWMANY(sizeof(Etherpkt), 256);
	hw->pstop = HOWMANY(hw->size, 256);

print("elite at %lux width %d addr %lux size %d lvl %d\n", hw->addr, hw->bt16?16:8,
	hw->ram, hw->size, hw->lvl);/**/

	/* enable interface RAM, set interface width */
	OUT(hw, msr, MENB|msr);
	if(hw->bt16)
		OUT(hw, laar, laar|L16EN|M16EN|ZeroWS16);

	(*hw->init)(cp);
	setvec(Int0vec + hw->lvl, hw->intr);
	return 0;
}

static void
dp8390rinit(Ctlr *cp)
{
	Hw *hw = cp->hw;

	OUT(hw, w.cr, Page0|RD2|STP);
	OUT(hw, w.bnry, hw->pstart);
	OUT(hw, w.cr, Page1|RD2|STP);
	OUT(hw, curr, hw->pstart+1);
	OUT(hw, w.cr, Page0|RD2|STA);
}

/*
 * we leave the chip idling on internal loopback
 * and pointing to Page0.
 */
static void
dp8390init(Ctlr *cp)
{
	Hw *hw = cp->hw;
	int i;

	OUT(hw, w.cr, Page0|RD2|STP);
	if(hw->bt16)
		OUT(hw, w.dcr, (1<<0)|(3<<5));	/* 16 bit interface, 12 byte DMA burst */
	else
		OUT(hw, w.dcr, 0x48);		/* FT1|LS */
	OUT(hw, w.rbcr0, 0);
	OUT(hw, w.rbcr1, 0);
	if(cp->prom)
		OUT(hw, w.rcr, 0x14);		/* PRO|AB */
	else
		OUT(hw, w.rcr, 0x04);		/* AB */
	OUT(hw, w.tcr, 0x20);			/* LB0 */

	OUT(hw, w.bnry, hw->pstart);
	OUT(hw, w.pstart, hw->pstart);
	OUT(hw, w.pstop, hw->pstop);
	OUT(hw, w.isr, 0xFF);
	OUT(hw, w.imr, 0x1F);			/* OVWE|TXEE|RXEE|PTXE|PRXE */

	OUT(hw, w.cr, Page1|RD2|STP);
	for(i = 0; i < sizeof(cp->ea); i++)
		OUT(hw, par[i], cp->ea[i]);
	OUT(hw, curr, hw->pstart+1);

	OUT(hw, w.cr, Page0|RD2|STA);
	OUT(hw, w.tpsr, 0);
}

void
wd8013mode(Ctlr *cp, int on)
{
	qlock(cp);
	if(on){
		cp->prom++;
		if(cp->prom == 1)
			OUT(cp->hw, w.rcr, 0x14);/* PRO|AB */
	}
	else {
		cp->prom--;
		if(cp->prom == 0)
			OUT(cp->hw, w.rcr, 0x04);/* AB */
	}
	qunlock(cp);
}

static void
wd8013online(Ctlr *cp, int on)
{
	USED(on);
	OUT(cp->hw, w.tcr, 0);
}

static void
wd8013tweek(Ctlr *cp)
{
	uchar msr;
	Hw *hw = cp->hw;
	int s;

	s = splhi();
	msr = IN(hw, msr);
	if((msr & MENB) == 0){
		/* board has reset itself, start again */
		delay(100);

		(*hw->reset)(cp);
		etherinit();

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

/*
 *  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 DMA registers and, if they are changing, wait.
 */
void
waitfordma(Hw *hw)
{
	uchar a,b,c;

	for(;;delay(10)){
		a = IN(hw, r.clda0);
		b = IN(hw, r.clda0);
		if(a != b)
			continue;
		c = IN(hw, r.clda0);
		if(c != b)
			continue;
		break;
	}
}

static void
wd8013receive(Ctlr *cp)
{
	Hw *hw = cp->hw;
	Buffer *rb;
	uchar bnry, curr, next;
	Ring *p;
	int i, len;

	bnry = IN(hw, r.bnry);
	next = bnry+1;
	if(next >= hw->pstop)
		next = hw->pstart;
	for(i = 0; ; i++){
		OUT(hw, w.cr, Page1|RD2|STA);
		curr = IN(hw, curr);
		OUT(hw, w.cr, Page0|RD2|STA);
		if(next == curr)
			break;
		if(strcmp(machtype, "NCRD.0") == 0)
			waitfordma(hw);
		cp->inpackets++;
		p = &((Ring*)hw->ram)[next];
		len = ((p->len1<<8)|p->len0)-4;
		if(p->next < hw->pstart || p->next >= hw->pstop || len < 60){
			/*print("%d/%d : #%2.2ux #%2.2ux  #%2.2ux #%2.2ux\n", next, len,
				p->status, p->next, p->len0, p->len1);/**/
			dp8390rinit(cp);
			break;
		}
		rb = &cp->rb[cp->ri];
		if(rb->owner == Interface){
			rb->len = len;
			if((p->data+len) >= (hw->ram+hw->size)){
				len = (hw->ram+hw->size) - p->data;
				memmove(rb->pkt+len,
					&((Ring*)hw->ram)[hw->pstart],
					(p->data+rb->len) - (hw->ram+hw->size));
			}
			memmove(rb->pkt, p->data, len);
			rb->owner = Host;
			cp->ri = NEXT(cp->ri, cp->nrb);
		}
		p->status = 0;
		next = p->next;
		bnry = next-1;
		if(bnry < hw->pstart)
			bnry = hw->pstop-1;
		OUT(hw, w.bnry, bnry);
	}
}

static void
wd8013transmit(Ctlr *cp)
{
	Hw *hw;
	Buffer *tb;
	int s;

	s = splhi();
	hw = cp->hw;
	tb = &cp->tb[cp->ti];
	if(tb->busy == 0 && tb->owner == Interface){
		memmove(hw->ram, tb->pkt, tb->len);
		OUT(hw, w.tbcr0, tb->len & 0xFF);
		OUT(hw, w.tbcr1, (tb->len>>8) & 0xFF);
		OUT(hw, w.cr, Page0|RD2|TXP|STA);
		tb->busy = 1;
	}
	splx(s);
}

static void
wd8013intr(Ureg *ur)
{
	Ctlr *cp = &ctlr[0];
	Hw *hw = cp->hw;
	Buffer *tb;
	uchar isr;

	USED(ur);
	while(isr = IN(hw, r.isr)){
		OUT(hw, w.isr, isr);
		/*
		 * we have received packets.
		 */
		if(isr & (0x04|0x01)){		/* Rxe|Prx - packet received */
			(*cp->hw->receive)(cp);
			wakeup(&cp->rr);
		}
		if(isr & 0x10)			/* Ovw - overwrite warning */
			cp->overflows++;
		if(isr & 0x08)			/* Txe - transmit error */
			cp->oerrs++;
		if(isr & 0x04){			/* Rxe - receive error */
			cp->frames += IN(hw, r.cntr0);
			cp->crcs += IN(hw, r.cntr1);
			cp->buffs += IN(hw, r.cntr2);
		}
		if(isr & 0x02)			/* Ptx - packet transmitted */
			cp->outpackets++;
		/*
		 * a packet completed transmission, successfully or
		 * not. start transmission on the next buffered packet,
		 * and wake the output routine.
		 */
		if(isr & (0x08|0x02)){
			tb = &cp->tb[cp->ti];
			tb->owner = Host;
			tb->busy = 0;
			cp->ti = NEXT(cp->ti, cp->ntb);
			(*cp->hw->transmit)(cp);
			wakeup(&cp->tr);
		}
	}
}

static Hw wd8013 =
{
	wd8013reset,
	dp8390init,
	wd8013mode,
	wd8013online,
	wd8013receive,
	wd8013transmit,
	wd8013intr,
	wd8013tweek,
};

A pc/ether509.c => pc/ether509.c +422 -0
@@ 0,0 1,422 @@
#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"

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

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

					/* Commands */
	SelectWindow	= 0x01,		/* SelectWindow command */
	StartCoax	= 0x02,		/* Start Coaxial Transceiver */
	RxDisable	= 0x03,		/* RX Disable */
	RxEnable	= 0x04,		/* RX Enable */
	RxDiscard	= 0x08,		/* RX Discard Top Packet */
	TxEnable	= 0x09,		/* TX Enable */
	TxDisable	= 0x0A,		/* TX Disable */
	AckIntr		= 0x0D,		/* Acknowledge Interrupt */
	SetIntrMask	= 0x0E,		/* Set Interrupt Mask */
	SetReadZeroMask	= 0x0F,		/* Set Read Zero Mask */
	SetRxFilter	= 0x10,		/* Set RX Filter */
	SetTxAvailable	= 0x12,		/* Set TX Available Threshold */

					/* RX Filter Command Bits */
	MyEtherAddr	= 0x01,		/* Individual address */
	Multicast	= 0x02,		/* Group (multicast) addresses */
	Broadcast	= 0x04,		/* Broadcast address */
	Promiscuous	= 0x08,		/* All addresses (promiscuous mode */

					/* Window Register Offsets */
	Command		= 0x0E,		/* all windows */
	Status		= 0x0E,

	EEPROMdata	= 0x0C,		/* window 0 */
	EEPROMcmd	= 0x0A,
	ResourceConfig	= 0x08,
	ConfigControl	= 0x04,

	TxFreeBytes	= 0x0C,		/* window 1 */
	TxStatus	= 0x0B,
	RxStatus	= 0x08,
	Fifo		= 0x00,

					/* Status/Interrupt Bits */
	Latch		= 0x0001,	/* Interrupt Latch */
	TxComplete	= 0x0004,	/* TX Complete */
	TxAvailable	= 0x0008,	/* TX Available */
	RxComplete	= 0x0010,	/* RX Complete */
	AllIntr		= 0x00FE,	/* All Interrupt Bits */
	CmdInProgress	= 0x1000,	/* Command In Progress */

					/* RxStatus Bits */
	RxByteMask	= 0x07FF,	/* RX Bytes (0-1514) */
	RxErrMask	= 0x3800,	/* Type of Error: */
	RxErrOverrun	= 0x0000,	/*   Overrrun */
	RxErrOversize	= 0x0800,	/*   Oversize Packet (>1514) */
	RxErrDribble	= 0x1000,	/*   Dribble Bit(s) */
	RxErrRunt	= 0x1800,	/*   Runt Packet */
	RxErrFraming	= 0x2000,	/*   Alignment (Framing) */
	RxErrCRC	= 0x2800,	/*   CRC */
	RxError		= 0x4000,	/* Error */
	RxEmpty		= 0x8000,	/* Incomplete or FIFO empty */
};

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

/*
 * get configuration parameters
 */
static int
reset(EtherCtlr *cp)
{
	EtherHw *hw = cp->hw;
	int i, ea;
	ushort acr;
	uchar al;

	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. Write two 0 bytes then the ID sequence to the IDport.
	 */
	outb(IDport, 0);
	outb(IDport, 0);
	outb(IDport, 0xc0);
	delay(100)
	outb(IDport, 0);
	outb(IDport, 0);
	for(al = 0xFF, i = 0; i < 255; i++){
		outb(IDport, al);
		if(al & 0x80){
			al <<= 1;
			al ^= 0xCF;
		}
		else
			al <<= 1;
	}

	/*
	 * 3. Read the Product ID from the EEPROM.
	 *    This is done by writing the IDPort with 0x83 (0x80
	 *    is the 'read EEPROM command, 0x03 is the offset of
	 *    the Product ID field in the EEPROM).
	 *    The data comes back 1 bit at a time.
	 *    We seem to need a delay here between reading the bits.
	 *
	 * If the ID doesn't match the 3C509 ID code, the adapter
	 * probably isn't there, so barf.
	 */
	outb(IDport, 0x83);
	for(acr = 0, i = 0; i < 16; i++){
		delay(5);
		acr <<= 1;
		acr |= inb(IDport) & 0x01;
	}
	if((acr & 0xF0FF) != 0x9050)
		return -1;

	/*
	 * 3. Read the Address Configuration from the EEPROM.
	 *    The Address Configuration field is at offset 0x08 in the EEPROM).
	 * 6. Activate the adapter by writing the Activate command
	 *    (0xFF).
	 */
	outb(IDport, 0x88);
	for(acr = 0, i = 0; i < 16; i++){
		delay(20);
		acr <<= 1;
		acr |= inb(IDport) & 0x01;
	}
	outb(IDport, 0xFF);

	/*
	 * 8. Now we can talk to the adapter's I/O base addresses.
	 *    We get the I/O base address from the acr just read.
	 *
	 *    Enable the adapter. 
	 */
	hw->addr = (acr & 0x1F)*0x10 + 0x200;
	outb(hw->addr+ConfigControl, 0x01);

	/*
	 * Read the IRQ from the Resource Configuration Register
	 * and the ethernet address from the EEPROM.
	 * The EEPROM command is 8bits, the lower 6 bits being
	 * the address offset.
	 */
	hw->irq = (ins(hw->addr+ResourceConfig)>>12) & 0x0F;
	for(ea = 0, i = 0; i < 3; i++, ea += 2){
		while(ins(hw->addr+EEPROMcmd) & 0x8000)
			;
		outs(hw->addr+EEPROMcmd, (2<<6)|i);
		while(ins(hw->addr+EEPROMcmd) & 0x8000)
			;
		acr = ins(hw->addr+EEPROMdata);
		cp->ea[ea] = (acr>>8) & 0xFF;
		cp->ea[ea+1] = acr & 0xFF;
	}

	/*
	 * Finished with window 0. Now set the ethernet address
	 * in window 2.
	 * 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);
	for(i = 0; i < 6; i++)
		outb(hw->addr+i, cp->ea[i]);

	/*
	 * Finished with window 2.
	 * Set window 1 for normal operation.
	 */
	COMMAND(hw, 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);
		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("\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)
{
	EtherHw *hw = cp->hw;

	USED(on);					/* BUG */
	/*
	 * Set the receiver packet filter for our own and
	 * and broadcast addresses, set the interrupt masks
	 * for all interrupts, and enable the receiver and transmitter.
	 * The only interrupt we should see under normal conditions
	 * 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);
}


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

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

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

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

		/*
		 * If we had an error, log it and continue
		 * without updating the ring.
		 */
		if(status & RxError){
			print("error #%ux, #%ux\n", status, getdiag(hw));
			switch(status & RxErrMask){

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

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

			case RxErrCRC:		/* CRC */
				cp->crcs++;
				break;
			}
		}
		else {
			/*
			 * We have a packet. Read it into the next
			 * free ring buffer, if any.
			 * The CRC is already stripped off.
			 */
			rb = &cp->rb[cp->ri];
			if(rb->owner == Interface){
				len = (status & RxByteMask);
				rb->len = len;
	
				/*
				 * Must read len bytes padded to a
				 * 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));
				 */
				inss(hw->addr+Fifo, rb->pkt, HOWMANY(len, 2));

				/*
				 * Update the ring.
				 */
				rb->owner = Host;
				cp->ri = NEXT(cp->ri, cp->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)
			;
	}
}

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

	s = splhi();
	for(tb = &cp->tb[cp->ti]; tb->owner == Interface; tb = &cp->tb[cp->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);
			break;
		}

		/*
		 * There's room, copy the packet to the FIFO and free
		 * the buffer back to the host.
		 * Output packet muat 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);
		tb->owner = Host;
		cp->ti = NEXT(cp->ti, cp->ntb);
	}
	splx(s);
}

static void
interrupt(EtherCtlr *cp)
{
	EtherHw *hw = cp->hw;
	ushort status;

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

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

	if(status & TxComplete){
		/*
		 * Needs work here.
		 */
		print("txstat %ux\n", inb(hw->addr+TxStatus));
	}

	if(status & TxAvailable){
		(*cp->hw->transmit)(cp);
		wakeup(&cp->tr);
		status &= ~TxAvailable;
	}

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

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

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

M pc/fns.h => pc/fns.h +4 -0
@@ 31,7 31,9 @@ void	i8042reset(void);
void	ident(void);
void	idle(void);
int	inb(int);
ushort	ins(int);
void	inss(int, void*, int);
void	insl(int, void*, int);
void	kbdinit(void);
void	mathinit(void);
void	mmuinit(void);


@@ 42,7 44,9 @@ void	mouseaccelerate(int);
void	mouseres(int);
void	mousespeed(int);
void	outb(int, int);
void	outs(int, ushort);
void	outss(int, void*, int);
void	outsl(int, void*, int);
void	pmubuzz(int, int);
int	pmucpuspeed(int);
void	pmulights(int);

M pc/io.h => pc/io.h +94 -0
@@ 24,3 24,97 @@ 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 */
};

M pc/l.s => pc/l.s +2 -2
@@ 226,7 226,7 @@ TEXT	ins(SB), $0

	MOVL	p+0(FP), DX
	XORL	AX, AX
	INL
	OP16; INL
	RET




@@ 258,7 258,7 @@ TEXT	insl(SB),$0
TEXT	outs(SB), $0
	MOVL	p+0(FP), DX
	MOVL	s+4(FP), AX
	OUTL
	OP16; OUTL
	RET

/*

M port/devscc.c => port/devscc.c +8 -6
@@ 271,7 271,7 @@ sccbreak(SCC *sp, int ms)
 *  transmit and receive enabled, interrupts disabled.
 */
static void
sccsetup0(SCC *sp)
sccsetup0(SCC *sp, int brsource)
{
	memset(sp->sticky, 0, sizeof(sp->sticky));



@@ 284,9 284,11 @@ sccsetup0(SCC *sp)
	sccsetbaud(sp, 9600);
	sp->sticky[4] = Rx1stop | X16;
	sccwrreg(sp, 4, 0);
	sp->sticky[11] = TxClockBR | RxClockBR | TRxCOutBR | TRxCOI;
	sp->sticky[11] = TxClockBR | RxClockBR | TRxCOutBR /*| TRxCOI*/;
	sccwrreg(sp, 11, 0);
	sp->sticky[14] = BREna | BRSource;
	sp->sticky[14] = BREna;
	if(brsource)
		sp->sticky[14] |= BRSource;
	sccwrreg(sp, 14, 0);

	/*


@@ 299,7 301,7 @@ sccsetup0(SCC *sp)
}

void
sccsetup(void *addr, ulong freq)
sccsetup(void *addr, ulong freq, int brsource)
{
	SCCdev *dev;
	SCC *sp;


@@ 314,13 316,13 @@ sccsetup(void *addr, ulong freq)
	sp->ptr = &dev->ptra;
	sp->data = &dev->dataa;
	sp->freq = freq;
	sccsetup0(sp);
	sccsetup0(sp, brsource);
	sp = xalloc(sizeof(SCC));
	scc[nscc+1] = sp;
	sp->ptr = &dev->ptrb;
	sp->data = &dev->datab;
	sp->freq = freq;
	sccsetup0(sp);
	sccsetup0(sp, brsource);
	nscc += 2;
}


M port/portfns.h => port/portfns.h +1 -1
@@ 192,7 192,7 @@ Proc*		runproc(void);
void		savefpregs(FPsave*);
void		sccclock(void);
void		sccintr(void);
void		sccsetup(void*, ulong);
void		sccsetup(void*, ulong, int);
void		sccspecial(int, IOQ*, IOQ*, int);
void		sched(void);
void		schedinit(void);

M ss/main.c => ss/main.c +2 -2
@@ 115,9 115,9 @@ ioinit(void)

	/* tell scc driver its addresses */
	k = kmappa(KMDUART, PTEIO|PTENOCACHE);
	sccsetup((void*)(k->va), KMFREQ);
	sccsetup((void*)(k->va), KMFREQ, 1);
	k = kmappa(EIADUART, PTEIO|PTENOCACHE);
	sccsetup((void*)(k->va), EIAFREQ);
	sccsetup((void*)(k->va), EIAFREQ, 1);

	/* scc port 0 is the keyboard */
	sccspecial(0, 0, &kbdq, 2400);