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