| plan 9 kernel history: overview | file list | diff list |
1992/0409/pc/devether.c (diff list | history)
| pc/devether.c on 1992/0403 | ||
| 1992/0403 | /* * Western Digital ethernet adapter * BUGS: | |
| 1992/0404 | * no more than one controller | |
| 1992/0406 | * TODO: | |
| 1992/0403 | * fix for different controller types | |
| 1992/0406 | * output * deal with stalling and restarting output on input overflow * fix magic ring constants | |
| 1992/0407 | * rewrite per SMC doc | |
| 1992/0403 | */ #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; | |
| 1992/0404 | typedef struct Ring Ring; | |
| 1992/0403 | enum { IObase = 0x360, | |
| 1992/0404 | RAMbase = 0xC8000, RAMsize = 8*1024, BUFsize = 256, | |
| 1992/0403 | ||
| 1992/0404 | Nctlr = 1, NPktype = 9, /* types/interface */ }; | |
| 1992/0408 | #define NEXT(x, l) ((((x)+1)%(l)) == 0 ? 6: (((x)+1)%(l))) #define PREV(x, l) (((x)-1) == 5 ? (l-1): ((x)-1)) | |
| 1992/0404 | /* * register offsets from IObase */ enum { | |
| 1992/0403 | EA = 0x08, /* Ethernet Address in ROM */ ID = 0x0E, /* interface type */ NIC = 0x10, /* National DP8390 Chip */ Cr = NIC+0x00, /* Page [01] */ | |
| 1992/0404 | Pstart = NIC+0x01, /* write */ Pstop = NIC+0x02, /* write */ Bnry = NIC+0x03, | |
| 1992/0403 | Tsr = NIC+0x04, /* read */ Tpsr = Tsr, /* write */ | |
| 1992/0404 | Tbcr0 = NIC+0x05, /* write */ Tbcr1 = NIC+0x06, /* write */ | |
| 1992/0403 | 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, | |
| 1992/0404 | }; | |
| 1992/0403 | ||
| 1992/0404 | /* * some register bits */ enum { Prx = 0x01, /* Isr: packet received */ Ptx = 0x02, /* packet transmitted */ Rxe = 0x04, /* receive error */ Txe = 0x08, /* transmit error */ Ovw = 0x10, /* overwrite warning */ Rst = 0x80, /* reset status */ }; | |
| 1992/0403 | ||
| 1992/0404 | struct Ring { uchar status; uchar next; | |
| 1992/0407 | uchar len0; uchar len1; | |
| 1992/0404 | uchar data[BUFsize-4]; | |
| 1992/0403 | }; /* * 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; | |
| 1992/0407 | Ring *ring; | |
| 1992/0403 | Rendez rr; /* rendezvous for an input buffer */ | |
| 1992/0407 | Queue rq; | |
| 1992/0404 | uchar bnry; uchar curr; | |
| 1992/0403 | ||
| 1992/0407 | Etherpkt *xpkt; | |
| 1992/0404 | QLock xl; | |
| 1992/0403 | Rendez xr; | |
| 1992/0407 | uchar xbusy; | |
| 1992/0403 | 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]; | |
| 1992/0407 | static int isxfree(void *arg) { Ctlr *cp = arg; | |
| 1992/0409 | return cp->xbusy == 0; | |
| 1992/0407 | } | |
| 1992/0403 | static void etheroput(Queue *q, Block *bp) { | |
| 1992/0406 | Ctlr *cp; | |
| 1992/0403 | int len, n; | |
| 1992/0406 | Pktype *pp; | |
| 1992/0403 | Etherpkt *p; Block *nbp; | |
| 1992/0406 | cp = ((Pktype *)q->ptr)->ctlr; | |
| 1992/0403 | if(bp->type == M_CTL){ | |
| 1992/0406 | pp = q->ptr; | |
| 1992/0403 | if(streamparse("connect", bp)) | |
| 1992/0406 | pp->type = strtol((char *)bp->rptr, 0, 0); | |
| 1992/0403 | else if(streamparse("promiscuous", bp)) { | |
| 1992/0406 | pp->prom = 1; qlock(cp); cp->prom++; if(cp->prom == 1) outb(cp->iobase+Rcr, 0x14); /* PRO|AB */ qunlock(cp); | |
| 1992/0403 | } freeb(bp); return; } /* * give packet a local address, return upstream if destined for * this machine. */ | |
| 1992/0406 | if(BLEN(bp) < ETHERHDRSIZE && (bp = pullup(bp, ETHERHDRSIZE)) == 0) return; | |
| 1992/0403 | p = (Etherpkt *)bp->rptr; | |
| 1992/0406 | memmove(p->s, cp->ea, sizeof(cp->ea)); if(memcmp(cp->ea, p->d, sizeof(cp->ea)) == 0){ | |
| 1992/0403 | len = blen(bp); | |
| 1992/0406 | if (bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){ putq(&cp->rq, bp); wakeup(&cp->rr); | |
| 1992/0403 | } return; } | |
| 1992/0406 | if(memcmp(cp->ba, p->d, sizeof(cp->ba)) == 0){ | |
| 1992/0403 | len = blen(bp); nbp = copyb(bp, len); | |
| 1992/0406 | if(nbp = expandb(nbp, len >= ETHERMINTU ? len: ETHERMINTU)){ | |
| 1992/0403 | nbp->wptr = nbp->rptr+len; | |
| 1992/0406 | putq(&cp->rq, nbp); wakeup(&cp->rr); | |
| 1992/0403 | } } /* * only one transmitter at a time */ | |
| 1992/0407 | qlock(&cp->xl); | |
| 1992/0403 | if(waserror()){ | |
| 1992/0407 | qunlock(&cp->xl); | |
| 1992/0403 | nexterror(); } /* | |
| 1992/0407 | * Wait till we get an output buffer | |
| 1992/0403 | */ | |
| 1992/0407 | sleep(&cp->xr, isxfree, cp); p = cp->xpkt; | |
| 1992/0403 | /* * 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; } /* | |
| 1992/0407 | * pad the packet (zero the pad) | |
| 1992/0403 | */ if(len < ETHERMINTU){ memset(((char*)p)+len, 0, ETHERMINTU-len); len = ETHERMINTU; } /* | |
| 1992/0407 | * give packet a local address | |
| 1992/0403 | */ | |
| 1992/0407 | memmove(p->s, cp->ea, sizeof(cp->ea)); | |
| 1992/0403 | /* | |
| 1992/0407 | * start the transmission | |
| 1992/0403 | */ | |
| 1992/0407 | outb(cp->iobase+Tbcr0, len & 0xFF); outb(cp->iobase+Tbcr1, (len>>8) & 0xFF); outb(cp->iobase+Cr, 0x26); /* Page0|TXP|STA */ cp->xbusy = 1; | |
| 1992/0403 | freeb(bp); | |
| 1992/0407 | qunlock(&cp->xl); | |
| 1992/0403 | poperror(); } /* * 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) { | |
| 1992/0404 | Ctlr *cp = &ctlr[0]; Pktype *pp; | |
| 1992/0403 | ||
| 1992/0404 | pp = &cp->pktype[s->id]; qlock(pp); RD(q)->ptr = WR(q)->ptr = pp; pp->type = 0; pp->q = RD(q); pp->inuse = 1; pp->ctlr = cp; qunlock(pp); | |
| 1992/0403 | } /* * 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) { | |
| 1992/0404 | Pktype *pp; | |
| 1992/0403 | ||
| 1992/0404 | pp = (Pktype *)(q->ptr); if(pp->prom){ qlock(pp->ctlr); pp->ctlr->prom--; | |
| 1992/0405 | if(pp->ctlr->prom == 0) | |
| 1992/0404 | outb(pp->ctlr->iobase+Rcr, 0x04);/* AB */ qunlock(pp->ctlr); | |
| 1992/0403 | } | |
| 1992/0404 | qlock(pp); pp->type = 0; pp->q = 0; pp->prom = 0; pp->inuse = 0; netdisown(&pp->ctlr->net, pp - pp->ctlr->pktype); qunlock(pp); | |
| 1992/0403 | } static Qinfo info = { nullput, etheroput, etherstopen, etherstclose, "ether" }; static int | |
| 1992/0404 | clonecon(Chan *c) | |
| 1992/0403 | { | |
| 1992/0404 | Ctlr *cp = &ctlr[0]; Pktype *pp; | |
| 1992/0403 | ||
| 1992/0404 | for(pp = cp->pktype; pp < &cp->pktype[NPktype]; pp++){ qlock(pp); if(pp->inuse || pp->q){ qunlock(pp); | |
| 1992/0403 | continue; } | |
| 1992/0404 | pp->inuse = 1; netown(&cp->net, pp - cp->pktype, u->p->user, 0); qunlock(pp); return pp - cp->pktype; | |
| 1992/0403 | } exhausted("ether channels"); } static void | |
| 1992/0404 | statsfill(Chan *c, char *p, int n) | |
| 1992/0403 | { | |
| 1992/0404 | Ctlr *cp = &ctlr[0]; | |
| 1992/0403 | 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", | |
| 1992/0404 | cp->inpackets, cp->outpackets, cp->crcs, cp->overflows, cp->frames, cp->buffs, cp->oerrs, cp->ea[0], cp->ea[1], cp->ea[2], cp->ea[3], cp->ea[4], cp->ea[5]); | |
| 1992/0403 | strncpy(p, buf, n); } static void typefill(Chan *c, char *p, int n) { char buf[16]; | |
| 1992/0404 | Pktype *pp; | |
| 1992/0403 | ||
| 1992/0404 | pp = &ctlr[0].pktype[STREAMID(c->qid.path)]; sprint(buf, "%d", pp->type); | |
| 1992/0403 | strncpy(p, buf, n); } static void intr(Ureg *ur) { | |
| 1992/0404 | Ctlr *cp = &ctlr[0]; uchar isr, curr; while(isr = inb(cp->iobase+Isr)){ outb(cp->iobase+Isr, isr); if(isr & Txe) cp->oerrs++; if(isr & Rxe){ cp->frames += inb(cp->iobase+Cntr0); cp->crcs += inb(cp->iobase+Cntr1); cp->buffs += inb(cp->iobase+Cntr2); } if(isr & Ptx) cp->outpackets++; if(isr & (Txe|Ptx)){ | |
| 1992/0407 | cp->xbusy = 0; wakeup(&cp->xr); | |
| 1992/0404 | } | |
| 1992/0409 | if(isr & Ovw) | |
| 1992/0408 | cp->overflows++; | |
| 1992/0404 | /* * we have received packets. * this is the only place, other than the init code, * where we set the controller to Page1. | |
| 1992/0405 | * we must be sure to reset it back to Page0 in case | |
| 1992/0404 | * we interrupted some other part of this driver. */ if(isr & (Ovw|Prx)){ outb(cp->iobase+Cr, 0x62); /* Page1, RD2|STA */ cp->curr = inb(cp->iobase+Curr); | |
| 1992/0409 | if(cp->curr == 0) print("C0: b%d i%ux\n", cp->bnry, isr); | |
| 1992/0404 | outb(cp->iobase+Cr, 0x22); /* Page0, RD2|STA */ wakeup(&cp->rr); } } | |
| 1992/0403 | } /* * the following initialisation procedure | |
| 1992/0405 | * is mandatory. | |
| 1992/0404 | * we leave the chip idling on internal loopback | |
| 1992/0405 | * and pointing to Page0. | |
| 1992/0403 | */ static void | |
| 1992/0404 | init(Ctlr *cp) | |
| 1992/0403 | { int i; | |
| 1992/0404 | outb(cp->iobase+Cr, 0x21); /* Page0, RD2|STP */ outb(cp->iobase+Dcr, 0x48); /* FT1|LS */ outb(cp->iobase+Rbcr0, 0); outb(cp->iobase+Rbcr1, 0); outb(cp->iobase+Rcr, 0x04); /* AB */ outb(cp->iobase+Tcr, 0x20); /* LB0 */ cp->bnry = 6; | |
| 1992/0408 | outb(cp->iobase+Bnry, cp->bnry); | |
| 1992/0407 | cp->ring = (Ring*)(KZERO|RAMbase); | |
| 1992/0404 | outb(cp->iobase+Pstart, 6); /* 6*256 */ outb(cp->iobase+Pstop, 32); /* 8*1024/256 */ outb(cp->iobase+Isr, 0xFF); outb(cp->iobase+Imr, 0x1F); /* OVWE|TXEE|RXEE|PTXE|PRXE */ | |
| 1992/0403 | ||
| 1992/0404 | outb(cp->iobase+Cr, 0x61); /* Page1, RD2|STP */ for(i = 0; i < sizeof(cp->ea); i++) outb(cp->iobase+Par0+i, cp->ea[i]); | |
| 1992/0408 | cp->curr = cp->bnry+1; outb(cp->iobase+Curr, cp->curr); | |
| 1992/0404 | outb(cp->iobase+Cr, 0x22); /* Page0, RD2|STA */ outb(cp->iobase+Tpsr, 0); | |
| 1992/0407 | cp->xpkt = (Etherpkt*)(KZERO|RAMbase); | |
| 1992/0403 | } void etherreset(void) { | |
| 1992/0404 | Ctlr *cp = &ctlr[0]; | |
| 1992/0403 | int i; | |
| 1992/0409 | uchar msr; | |
| 1992/0403 | ||
| 1992/0404 | cp->iobase = IObase; | |
| 1992/0409 | msr = 0x40|inb(cp->iobase); outb(cp->iobase, msr); for(i = 0; i < 0x10; i++) print("#%2.2ux ", inb(cp->iobase+i)); print("\n"); | |
| 1992/0404 | for(i = 0; i < sizeof(cp->ea); i++) cp->ea[i] = inb(cp->iobase+EA+i); | |
| 1992/0408 | init(cp); setvec(Ethervec, intr); | |
| 1992/0404 | memset(cp->ba, 0xFF, sizeof(cp->ba)); | |
| 1992/0403 | ||
| 1992/0404 | cp->net.name = "ether"; cp->net.nconv = NPktype; cp->net.devp = &info; cp->net.protop = 0; cp->net.listen = 0; cp->net.clone = clonecon; cp->net.ninfo = 2; cp->net.prot = cp->prot; cp->net.info[0].name = "stats"; cp->net.info[0].fill = statsfill; cp->net.info[1].name = "type"; cp->net.info[1].fill = typefill; | |
| 1992/0403 | } | |
| 1992/0404 | static void etherup(Ctlr *cp, Etherpkt *p, int len) { Block *bp; Pktype *pp; int t; t = (p->type[0]<<8) | p->type[1]; for(pp = &cp->pktype[0]; pp < &cp->pktype[NPktype]; pp++){ /* * check before locking just to save a lock */ if(pp->q == 0 || (t != pp->type && pp->type != -1)) continue; /* * only a trace channel gets packets destined for other machines */ if(pp->type != -1 && p->d[0] != 0xFF && memcmp(p->d, cp->ea, sizeof(p->d))) continue; /* * check after locking to make sure things didn't * change under foot */ if(canqlock(pp) == 0) continue; if(pp->q == 0 || pp->q->next->len > Streamhi || (t != pp->type && pp->type != -1)){ qunlock(pp); continue; } if(waserror() == 0){ bp = allocb(len); memmove(bp->rptr, (uchar *)p, len); bp->wptr += len; bp->flags |= S_DELIM; PUTNEXT(pp->q, bp); } poperror(); qunlock(pp); } } | |
| 1992/0406 | static void printpkt(uchar bnry, ushort len, Etherpkt *p) { int i; print("%.2ux: %.4d d(%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux)s(%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux)t(%.2ux %.2ux)\n", bnry, len, p->d[0], p->d[1], p->d[2], p->d[3], p->d[4], p->d[5], p->s[0], p->s[1], p->s[2], p->s[3], p->s[4], p->s[5], p->type[0], p->type[1]); } | |
| 1992/0403 | static int isinput(void *arg) { | |
| 1992/0404 | Ctlr *cp = arg; | |
| 1992/0403 | ||
| 1992/0408 | return NEXT(cp->bnry, 32) != cp->curr; | |
| 1992/0403 | } static void etherkproc(void *arg) { | |
| 1992/0404 | Ctlr *cp = arg; Block *bp; uchar bnry, curr; Ring *rp; | |
| 1992/0403 | if(waserror()){ | |
| 1992/0404 | print("%s noted\n", cp->name); init(cp); cp->kproc = 0; | |
| 1992/0403 | nexterror(); } | |
| 1992/0404 | cp->kproc = 1; | |
| 1992/0403 | for(;;){ | |
| 1992/0404 | sleep(&cp->rr, isinput, cp); | |
| 1992/0406 | /* * process any internal loopback packets */ | |
| 1992/0404 | while(bp = getq(&cp->rq)){ cp->inpackets++; etherup(cp, (Etherpkt*)bp->rptr, BLEN(bp)); freeb(bp); } | |
| 1992/0408 | ||
| 1992/0406 | /* * process any received packets */ | |
| 1992/0408 | bnry = NEXT(cp->bnry, 32); while(bnry != cp->curr){ rp = &cp->ring[bnry]; | |
| 1992/0406 | cp->inpackets++; | |
| 1992/0407 | etherup(cp, (Etherpkt*)rp->data, ((rp->len1<<8)+rp->len0)-4); | |
| 1992/0408 | bnry = rp->next; cp->bnry = PREV(bnry, 32); | |
| 1992/0407 | outb(cp->iobase+Bnry, cp->bnry); | |
| 1992/0406 | } | |
| 1992/0403 | } } void etherinit(void) { int ctlrno = 0; /* * put the receiver online * and start the kproc */ | |
| 1992/0404 | outb(ctlr[ctlrno].iobase+Tcr, 0); | |
| 1992/0403 | 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); | |
| 1992/0409 | } void etherdump(void) { Ctlr *cp = &ctlr[0]; uchar bnry, curr, isr; int s; s = splhi(); bnry = inb(cp->iobase+Bnry); outb(cp->iobase+Cr, 0x62); /* Page1, RD2|STA */ curr = inb(cp->iobase+Curr); outb(cp->iobase+Cr, 0x22); /* Page0, RD2|STA */ isr = inb(cp->iobase+Isr); print("b%d c%d x%d B%d C%d I%ux", cp->bnry, cp->curr, cp->xbusy, bnry, curr, isr); print("\t%d %d %d %d %d %d %d\n", cp->inpackets, cp->opackets, cp->crcs, cp->oerrs, cp->frames, cp->overflows, cp->buffs); splx(s); | |
| 1992/0403 | } | |