@@ 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);
+}