plan 9 kernel history: overview | file list | diff list

1992/0403/pc/devether.c (diff list | history)

pc/devether.c on 1992/0403
1992/0403    
/* 
 * Western Digital ethernet adapter 
 * BUGS: 
 *	more than one controller 
 *	fix for different controller types 
 */ 
#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" 
 
typedef struct Ctlr Ctlr; 
typedef struct Pktype Pktype; 
 
enum { 
	IObase		= 0x360, 
 
	EA		= 0x08,		/* Ethernet Address in ROM */ 
	ID		= 0x0E,		/* interface type */ 
 
	NIC		= 0x10,		/* National DP8390 Chip */ 
	Cr		= NIC+0x00,	/* Page [01] */ 
 
	Clda0		= NIC+0x01,	/* read */ 
	Pstart		= Clda0,	/* write */ 
	Clda1		= NIC+0x02,	/* read */ 
	Pstop		= Clda1,	/* write */ 
	Bnry		= NIC+0x03,	/* Page 0 */ 
	Tsr		= NIC+0x04,	/* read */ 
	Tpsr		= Tsr,		/* write */ 
	Ncr		= NIC+0x05, 
	Isr		= NIC+0x07, 
	Rbcr0		= NIC+0x0A, 
	Rbcr1		= NIC+0x0B, 
	Rsr		= NIC+0x0C,	/* read */ 
	Rcr		= Rsr,		/* write */ 
	Cntr0		= NIC+0x0D,	/* read */ 
	Tcr		= Cntr0,	/* write */ 
	Cntr1		= NIC+0x0E,	/* read */ 
	Dcr		= Cntr1,	/* write */ 
	Cntr2		= NIC+0x0F,	/* read */ 
	Imr		= Cntr2,	/* write */ 
 
	Par0		= NIC+0x01,	/* Page 1 */ 
	Curr		= NIC+0x07, 
	Mar0		= NIC+0x08, 
	Mar1		= NIC+0x09, 
	Mar2		= NIC+0x0A, 
	Mar3		= NIC+0x0B, 
	Mar4		= NIC+0x0C, 
	Mar5		= NIC+0x0D, 
	Mar6		= NIC+0x0E, 
	Mar7		= NIC+0x0F, 
 
	RAMbase		= 0xC8000, 
 
	Nctlr		= 1, 
	NPktype		= 9,		/* types/interface */ 
}; 
 
/* 
 * one per ethernet packet type 
 */ 
struct Pktype { 
	QLock; 
	int	type;			/* ethernet type */ 
	int	prom;			/* promiscuous mode */ 
	Queue	*q; 
	int	inuse; 
	Ctlr	*ctlr; 
}; 
 
/* 
 *  per ethernet 
 */ 
struct Ctlr { 
	QLock; 
 
	Rendez	rr;			/* rendezvous for an input buffer */ 
	Queue	rq; 
 
	Qlock	xl; 
	Rendez	xr; 
 
	int	iobase;			/* I/O base address */ 
 
	Pktype	pktype[NPktype]; 
	uchar	ea[6]; 
	uchar	ba[6]; 
 
	uchar	prom;			/* true if promiscuous mode */ 
	uchar	kproc;			/* true if kproc started */ 
	char	name[NAMELEN];		/* name of kproc */ 
	Network	net; 
	Netprot	prot[NPktype]; 
 
	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]; 
 
static void 
etheroput(Queue *q, Block *bp) 
{ 
	Ctlr *c; 
	int len, n; 
	Pktype *t; 
	Etherpkt *p; 
	Block *nbp; 
 
	c = ((Pktype *)q->ptr)->ctlr; 
	if(bp->type == M_CTL){ 
		t = q->ptr; 
		if(streamparse("connect", bp)) 
			t->type = strtol((char *)bp->rptr, 0, 0); 
		else if(streamparse("promiscuous", bp)) { 
			t->prom = 1; 
			qlock(c); 
			c->prom++; 
			if(c->prom == 1) 
				outb(c->iobase+Rcr, 0x14);	/* PRO|AB */ 
			qunlock(c); 
		} 
		freeb(bp); 
		return; 
	} 
 
	/* 
	 * give packet a local address, return upstream if destined for 
	 * this machine. 
	 */ 
	if(BLEN(bp) < ETHERHDRSIZE){ 
		bp = pullup(bp, ETHERHDRSIZE); 
		if(bp == 0) 
			return; 
	} 
	p = (Etherpkt *)bp->rptr; 
	memmove(p->s, c->ea, sizeof(c->ea)); 
	if(memcmp(c->ea, p->d, sizeof(c->ea)) == 0){ 
		len = blen(bp); 
		bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU); 
		if(bp){ 
			putq(&c->rq, bp); 
			wakeup(&c->rr); 
		} 
		return; 
	} 
	if(memcmp(c->ba, p->d, sizeof(c->ba)) == 0){ 
		len = blen(bp); 
		nbp = copyb(bp, len); 
		nbp = expandb(nbp, len >= ETHERMINTU ? len: ETHERMINTU); 
		if(nbp){ 
			nbp->wptr = nbp->rptr+len; 
			putq(&c->rq, nbp); 
			wakeup(&c->rr); 
		} 
	} 
 
	/* 
	 * only one transmitter at a time 
	 */ 
	qlock(&c->xl); 
	if(waserror()){ 
		qunlock(&c->xl); 
		nexterror(); 
	} 
 
	/* 
	 *  Wait till we get an output buffer 
	 */ 
	sleep(&c->xr, isobuf, c); 
	p = enet.xn; 
 
	/* 
	 * copy message into buffer 
	 */ 
	len = 0; 
	for(nbp = bp; nbp; nbp = nbp->next){ 
		if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){ 
			memmove(((uchar *)p)+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) 
	 */ 
	if(len < ETHERMINTU){ 
		memset(((char*)p)+len, 0, ETHERMINTU-len); 
		len = ETHERMINTU; 
	} 
	enet.xn->len = len; 
 
	/* 
	 *  give packet a local address 
	 */ 
	memmove(p->s, enet.ea, sizeof(enet.ea)); 
 
	/* 
	 *  give to Chip 
	 */ 
	splhi();		/* sync with interrupt routine */ 
	enet.xn->owner = Chip; 
	if(enet.xmting == 0) 
		ethersend(enet.xn); 
	spllo(); 
 
	/* 
	 *  send 
	 */ 
	enet.xn = XSUCC(enet.xn); 
	freeb(bp); 
	qunlock(&enet.xl); 
	poperror(); 
 
	enet.outpackets++; 
} 
 
/* 
 * open an ether line discipline 
 * 
 * the lock is to synchronize changing the ethertype with 
 * sending packets up the stream on interrupts. 
 */ 
static void 
etherstopen(Queue *q, Stream *s) 
{ 
	Ctlr *c = &ctlr[0]; 
	Pktype *t; 
 
	t = &c->pktype[s->id]; 
	qlock(t); 
	RD(q)->ptr = WR(q)->ptr = t; 
	t->type = 0; 
	t->q = RD(q); 
	t->inuse = 1; 
	t->ctlr = c; 
	qunlock(t); 
} 
 
/* 
 *  close ether line discipline 
 * 
 *  the lock is to synchronize changing the ethertype with 
 *  sending packets up the stream on interrupts. 
 */ 
static void 
etherstclose(Queue *q) 
{ 
	Pktype *t; 
 
	t = (Pktype *)(q->ptr); 
	if(t->prom){ 
		qlock(t->ctlr); 
		t->ctlr->prom--; 
		if(t->ctlr->prom == 0) 
			/* turn off promiscuous mode here */; 
		qunlock(t->ctlr); 
	} 
	qlock(t); 
	t->type = 0; 
	t->q = 0; 
	t->prom = 0; 
	t->inuse = 0; 
	netdisown(&t->ctlr->net, t - t->ctlr->pktype); 
	qunlock(t); 
} 
 
static Qinfo info = { 
	nullput, 
	etheroput, 
	etherstopen, 
	etherstclose, 
	"ether" 
}; 
 
static int 
clonecon(Chan *chan) 
{ 
	Ctlr *c = &ctlr[0]; 
	Pktype *t; 
 
	for(t = c->pktype; t < &c->pktype[NPktype]; t++){ 
		qlock(t); 
		if(t->inuse || t->q){ 
			qunlock(t); 
			continue; 
		} 
		t->inuse = 1; 
		netown(&c->net, t - c->pktype, u->p->user, 0); 
		qunlock(t); 
		return t - c->pktype; 
	} 
	exhausted("ether channels"); 
} 
 
static void 
statsfill(Chan *chan, char *p, int n) 
{ 
	Ctlr *c = &ctlr[0]; 
	char buf[256]; 
 
	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", 
		c->inpackets, c->outpackets, c->crcs, 
		c->overflows, c->frames, c->buffs, c->oerrs, 
		c->ea[0], c->ea[1], c->ea[2], c->ea[3], c->ea[4], c->ea[5]); 
	strncpy(p, buf, n); 
} 
 
static void 
typefill(Chan *c, char *p, int n) 
{ 
	char buf[16]; 
	Pktype *t; 
 
	t = &ctlr[0].pktype[STREAMID(c->qid.path)]; 
	sprint(buf, "%d", t->type); 
	strncpy(p, buf, n); 
} 
 
static void 
intr(Ureg *ur) 
{ 
	panic("ether intr\n"); 
} 
 
/* 
 * the following initialisation procedure 
 * is mandatory 
 * after this, the chip is still offline 
 */ 
static void 
init(Ctlr *c) 
{ 
	int i; 
 
	outb(c->iobase+Cr, 0x21);	/* Page0, RD2|STP */ 
	outb(c->iobase+Dcr, 0x48);	/* FT1|LS */ 
	outb(c->iobase+Rbcr0, 0); 
	outb(c->iobase+Rbcr1, 0); 
	outb(c->iobase+Rcr, 0x04);	/* AB */ 
	outb(c->iobase+Tcr, 0); 
	outb(c->iobase+Bnry, 6); 
	outb(c->iobase+Pstart, 6);	/* 6*256 */ 
	outb(c->iobase+Pstop, 32);	/* 8*1024/256 */ 
	outb(c->iobase+Isr, 0xFF); 
	outb(c->iobase+Imr, 0x0F);	/* TXEE|RXEE|PTXE|PRXE */ 
 
	outb(c->iobase+Cr, 0x61);	/* Page1, RD2|STP */ 
	for(i = 0; i < sizeof(c->ea); i++) 
		outb(c->iobase+Par0+i, c->ea[i]); 
	outb(c->iobase+Curr, 6);	/* 6*256 */ 
} 
 
void 
etherreset(void) 
{ 
	Ctlr *c = &ctlr[0]; 
	int i; 
 
	c->iobase = IObase; 
	init(c); 
	setvec(Ethervec, intr); 
 
	for(i = 0; i < sizeof(c->ea); i++) 
		c->ea[i] = inb(c->iobase+EA+i); 
	memset(c->ba, 0xFF, sizeof(c->ba)); 
 
	c->net.name = "ether"; 
	c->net.nconv = NPktype; 
	c->net.devp = &info; 
	c->net.protop = 0; 
	c->net.listen = 0; 
	c->net.clone = clonecon; 
	c->net.ninfo = 2; 
	c->net.prot = c->prot; 
	c->net.info[0].name = "stats"; 
	c->net.info[0].fill = statsfill; 
	c->net.info[1].name = "type"; 
	c->net.info[1].fill = typefill; 
} 
 
static int 
isinput(void *arg) 
{ 
	Ctlr *c = arg; 
 
	return 0; 
} 
 
static void 
etherkproc(void *arg) 
{ 
	Ctlr *c = arg; 
 
	if(waserror()){ 
		print("someone noted %s\n", c->name); 
		c->kproc = 0; 
		nexterror(); 
	} 
	c->kproc = 1; 
	for(;;){ 
		sleep(&c->rr, isinput, c); 
	} 
} 
 
void 
etherinit(void) 
{ 
	int ctlrno = 0; 
 
	/* 
	 * put the receiver online 
	 * and start the kproc 
	 */ 
	outb(c->iobase+Cr, 0x22);	/* Page0, RD2|STA */ 
	outb(c->iobase+Tpsr, 0); 
	if(ctlr[ctlrno].kproc == 0){ 
		sprint(ctlr[ctlrno].name, "ether%dkproc", ctlrno); 
		kproc(ctlr[ctlrno].name, etherkproc, &ctlr[ctlrno]); 
	} 
} 
 
Chan * 
etherattach(char *spec) 
{ 
	return devattach('l', spec); 
} 
 
Chan * 
etherclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
 
int 
etherwalk(Chan *c, char *name) 
{ 
	return netwalk(c, name, &ctlr[0].net); 
} 
 
void 
etherstat(Chan *c, char *dp) 
{ 
	netstat(c, dp, &ctlr[0].net); 
} 
 
Chan * 
etheropen(Chan *c, int omode) 
{ 
	return netopen(c, omode, &ctlr[0].net); 
} 
 
void 
ethercreate(Chan *c, char *name, int omode, ulong perm) 
{ 
	error(Eperm); 
} 
 
void 
etherremove(Chan *c) 
{ 
	error(Eperm); 
} 
 
void 
etherwstat(Chan *c, char *dp) 
{ 
	netwstat(c, dp, &ctlr[0].net); 
} 
 
void 
etherclose(Chan *c) 
{ 
	if(c->stream) 
		streamclose(c); 
} 
 
long 
etherread(Chan *c, void *a, long n, ulong offset) 
{ 
	return netread(c, a, n, offset, &ctlr[0].net); 
} 
 
long 
etherwrite(Chan *c, char *a, long n, ulong offset) 
{ 
	return streamwrite(c, a, n, 0); 
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)