~kris/9p

9hist

ef82c5e8c5a6df9f42b55bb16364f6868a6e467d — David du Colombier 34 years ago 01821c8
Plan 9 from Bell Labs 1992-04-03
2 files changed, 502 insertions(+), 0 deletions(-)

A pc/devether.c
M pc/io.h
A pc/devether.c => pc/devether.c +501 -0
@@ 0,0 1,501 @@
/*
 * 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);
}

M pc/io.h => pc/io.h +1 -0
@@ 14,6 14,7 @@ enum
	 Kbdvec=	Int0vec+1,	/*  keyboard interrupts */
	 Uart1vec=	Int0vec+3,	/*  modem line */
	 Uart0vec=	Int0vec+4,	/*  serial line */
	 Ethervec=	Int0vec+5,	/*  ethernet interrupt */
	 Floppyvec=	Int0vec+6,	/*  floppy interrupts */
	 Parallelvec=	Int0vec+7,	/*  parallel port interrupts */
	Int1vec=	Int0vec+8,	/* second 8259 */