| plan 9 kernel history: overview | file list | diff list |
1992/1015/pc/devether.c (diff list | history)
| pc/devether.c on 1992/0403 | ||
| 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" | |
| 1992/0922 | /* * Half-arsed attempt at a general top-level * ethernet driver. Needs work: * handle multiple controllers * much tidying * set ethernet address */ extern EtherHw ether509; extern EtherHw ether80x3; | |
| 1992/0403 | ||
| 1992/0922 | static EtherHw *etherhw[] = { ðer509, ðer80x3, 0 | |
| 1992/0404 | }; enum { | |
| 1992/0922 | Nctlr = 1, | |
| 1992/0424 | }; | |
| 1992/0403 | ||
| 1992/0922 | static struct EtherCtlr ctlr[Nctlr]; | |
| 1992/0424 | #define NEXT(x, l) (((x)+1)%(l)) #define OFFSETOF(t, m) ((unsigned)&(((t*)0)->m)) #define HOWMANY(x, y) (((x)+((y)-1))/(y)) #define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) | |
| 1992/0403 | ||
| 1992/0425 | Chan* etherattach(char *spec) | |
| 1992/0424 | { | |
| 1992/0917 | if(ctlr[0].present == 0) error(Enodev); | |
| 1992/0425 | return devattach('l', spec); | |
| 1992/0424 | } | |
| 1992/0411 | ||
| 1992/0424 | Chan* etherclone(Chan *c, Chan *nc) | |
| 1992/0411 | { | |
| 1992/0424 | return devclone(c, nc); } | |
| 1992/0411 | ||
| 1992/0424 | 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) { | |
| 1992/0711 | USED(c, name, omode, perm); | |
| 1992/0424 | error(Eperm); } 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); | |
| 1992/0411 | } | |
| 1992/0424 | long etherwrite(Chan *c, char *a, long n, ulong offset) { | |
| 1992/0711 | USED(offset); | |
| 1992/0424 | return streamwrite(c, a, n, 0); } void etherremove(Chan *c) { | |
| 1992/0711 | USED(c); | |
| 1992/0424 | error(Eperm); } void etherwstat(Chan *c, char *dp) { netwstat(c, dp, &ctlr[0].net); } | |
| 1992/0407 | static int | |
| 1992/0424 | isobuf(void *arg) | |
| 1992/0407 | { | |
| 1992/0922 | EtherCtlr *cp = arg; | |
| 1992/0407 | ||
| 1992/0425 | return cp->tb[cp->th].owner == Host; | |
| 1992/0407 | } | |
| 1992/0403 | static void etheroput(Queue *q, Block *bp) { | |
| 1992/0922 | EtherCtlr *cp; EtherType *tp; | |
| 1992/0403 | Etherpkt *p; | |
| 1992/0922 | EtherBuf *tb; int len, n; | |
| 1992/0403 | Block *nbp; if(bp->type == M_CTL){ | |
| 1992/0410 | tp = q->ptr; | |
| 1992/0403 | if(streamparse("connect", bp)) | |
| 1992/0922 | tp->type = strtol((char*)bp->rptr, 0, 0); | |
| 1992/0403 | else if(streamparse("promiscuous", bp)) { | |
| 1992/0410 | tp->prom = 1; | |
| 1992/0424 | (*tp->ctlr->hw->mode)(tp->ctlr, 1); | |
| 1992/0403 | } freeb(bp); return; } | |
| 1992/0922 | cp = ((EtherType*)q->ptr)->ctlr; | |
| 1992/0424 | ||
| 1992/0403 | /* * 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/0922 | 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)){ | |
| 1992/0424 | putq(&cp->lbq, bp); | |
| 1992/0406 | 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/0424 | putq(&cp->lbq, nbp); | |
| 1992/0406 | wakeup(&cp->rr); | |
| 1992/0403 | } } /* * only one transmitter at a time */ | |
| 1992/0424 | qlock(&cp->tlock); | |
| 1992/0403 | if(waserror()){ | |
| 1992/0411 | freeb(bp); | |
| 1992/0424 | qunlock(&cp->tlock); | |
| 1992/0403 | nexterror(); } /* | |
| 1992/0424 | * wait till we get an output buffer. * should try to restart. | |
| 1992/0403 | */ | |
| 1992/0424 | sleep(&cp->tr, isobuf, cp); | |
| 1992/0403 | ||
| 1992/0424 | tb = &cp->tb[cp->th]; | |
| 1992/0403 | /* * copy message into buffer */ len = 0; for(nbp = bp; nbp; nbp = nbp->next){ if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){ | |
| 1992/0501 | memmove(tb->pkt+len, nbp->rptr, n); | |
| 1992/0403 | 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){ | |
| 1992/0501 | memset(tb->pkt+len, 0, ETHERMINTU-len); | |
| 1992/0403 | len = ETHERMINTU; } /* | |
| 1992/0424 | * set up the transmit buffer and | |
| 1992/0407 | * start the transmission | |
| 1992/0403 | */ | |
| 1992/0424 | cp->outpackets++; tb->len = len; tb->owner = Interface; cp->th = NEXT(cp->th, cp->ntb); (*cp->hw->transmit)(cp); | |
| 1992/0403 | freeb(bp); | |
| 1992/0424 | qunlock(&cp->tlock); | |
| 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/0922 | EtherCtlr *cp = &ctlr[0]; EtherType *tp; | |
| 1992/0403 | ||
| 1992/0410 | tp = &cp->type[s->id]; qlock(tp); RD(q)->ptr = WR(q)->ptr = tp; tp->type = 0; tp->q = RD(q); tp->inuse = 1; tp->ctlr = cp; qunlock(tp); | |
| 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/0922 | EtherType *tp; | |
| 1992/0403 | ||
| 1992/0922 | tp = (EtherType*)(q->ptr); | |
| 1992/0424 | if(tp->prom) (*tp->ctlr->hw->mode)(tp->ctlr, 0); | |
| 1992/0410 | qlock(tp); tp->type = 0; tp->q = 0; tp->prom = 0; tp->inuse = 0; | |
| 1992/0625 | netdisown(tp); | |
| 1992/0505 | tp->ctlr = 0; | |
| 1992/0410 | qunlock(tp); | |
| 1992/0403 | } static Qinfo info = { nullput, etheroput, etherstopen, etherstclose, "ether" }; static int | |
| 1992/0404 | clonecon(Chan *c) | |
| 1992/0403 | { | |
| 1992/0922 | EtherCtlr *cp = &ctlr[0]; EtherType *tp; | |
| 1992/0403 | ||
| 1992/0711 | USED(c); | |
| 1992/0410 | for(tp = cp->type; tp < &cp->type[NType]; tp++){ qlock(tp); if(tp->inuse || tp->q){ qunlock(tp); | |
| 1992/0403 | continue; } | |
| 1992/0410 | tp->inuse = 1; | |
| 1992/0625 | netown(tp, u->p->user, 0); | |
| 1992/0410 | qunlock(tp); return tp - cp->type; | |
| 1992/0403 | } exhausted("ether channels"); | |
| 1992/0711 | return 0; | |
| 1992/0403 | } static void | |
| 1992/0404 | statsfill(Chan *c, char *p, int n) | |
| 1992/0403 | { | |
| 1992/0922 | EtherCtlr *cp = &ctlr[0]; | |
| 1992/0403 | char buf[256]; | |
| 1992/0711 | USED(c); | |
| 1992/0403 | 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/0922 | EtherType *tp; | |
| 1992/0403 | ||
| 1992/0410 | tp = &ctlr[0].type[STREAMID(c->qid.path)]; sprint(buf, "%d", tp->type); | |
| 1992/0403 | strncpy(p, buf, n); } static void | |
| 1992/0922 | etherup(EtherCtlr *cp, void *data, int len) | |
| 1992/0403 | { | |
| 1992/0501 | Etherpkt *p; | |
| 1992/0404 | int t; | |
| 1992/0922 | EtherType *tp; | |
| 1992/0501 | Block *bp; | |
| 1992/0404 | ||
| 1992/0501 | p = data; | |
| 1992/0410 | t = (p->type[0]<<8)|p->type[1]; for(tp = &cp->type[0]; tp < &cp->type[NType]; tp++){ | |
| 1992/0404 | /* * check before locking just to save a lock */ | |
| 1992/0410 | if(tp->q == 0 || (t != tp->type && tp->type != -1)) | |
| 1992/0404 | continue; /* * only a trace channel gets packets destined for other machines */ | |
| 1992/0410 | if(tp->type != -1 && p->d[0] != 0xFF && memcmp(p->d, cp->ea, sizeof(p->d))) | |
| 1992/0404 | continue; /* * check after locking to make sure things didn't * change under foot */ | |
| 1992/0410 | if(canqlock(tp) == 0) | |
| 1992/0404 | continue; | |
| 1992/0410 | if(tp->q == 0 || tp->q->next->len > Streamhi || (t != tp->type && tp->type != -1)){ qunlock(tp); | |
| 1992/0404 | continue; } | |
| 1992/1015 | if(!waserror()){ | |
| 1992/0424 | bp = allocb(len); memmove(bp->rptr, p, len); bp->wptr += len; | |
| 1992/0404 | bp->flags |= S_DELIM; | |
| 1992/0410 | PUTNEXT(tp->q, bp); | |
| 1992/1015 | poperror(); | |
| 1992/0404 | } | |
| 1992/0410 | qunlock(tp); | |
| 1992/0404 | } } | |
| 1992/0403 | static int isinput(void *arg) { | |
| 1992/0922 | EtherCtlr *cp = arg; | |
| 1992/0403 | ||
| 1992/0925 | return cp->lbq.first || cp->rb[cp->rh].owner == Host; | |
| 1992/0403 | } static void etherkproc(void *arg) { | |
| 1992/0922 | EtherCtlr *cp = arg; EtherBuf *rb; | |
| 1992/0404 | Block *bp; | |
| 1992/0403 | if(waserror()){ | |
| 1992/0404 | print("%s noted\n", cp->name); | |
| 1992/0922 | if(cp->hw->init) (*cp->hw->init)(cp); | |
| 1992/0404 | cp->kproc = 0; | |
| 1992/0403 | nexterror(); } | |
| 1992/0404 | cp->kproc = 1; | |
| 1992/0403 | for(;;){ | |
| 1992/0915 | tsleep(&cp->rr, isinput, cp, 500); | |
| 1992/0922 | if(cp->hw->tweak) (*cp->hw->tweak)(cp); | |
| 1992/0424 | ||
| 1992/0406 | /* * process any internal loopback packets */ | |
| 1992/0424 | while(bp = getq(&cp->lbq)){ | |
| 1992/0404 | cp->inpackets++; | |
| 1992/0501 | etherup(cp, bp->rptr, BLEN(bp)); | |
| 1992/0404 | freeb(bp); } | |
| 1992/0408 | ||
| 1992/0406 | /* * process any received packets */ | |
| 1992/0424 | while(cp->rb[cp->rh].owner == Host){ | |
| 1992/0406 | cp->inpackets++; | |
| 1992/0424 | rb = &cp->rb[cp->rh]; | |
| 1992/0501 | etherup(cp, rb->pkt, rb->len); | |
| 1992/0424 | rb->owner = Interface; | |
| 1992/0502 | cp->rh = NEXT(cp->rh, cp->nrb); | |
| 1992/0424 | } } } | |
| 1992/0410 | ||
| 1992/0922 | static void etherintr(Ureg *ur) { EtherCtlr *cp = &ctlr[0]; USED(ur); (*cp->hw->intr)(cp); } | |
| 1992/0424 | void etherreset(void) { | |
| 1992/0922 | EtherCtlr *cp; EtherHw **hw; | |
| 1992/0625 | int i; | |
| 1992/0410 | ||
| 1992/0625 | cp = &ctlr[0]; | |
| 1992/0922 | for(hw = etherhw; *hw; hw++){ cp->hw = *hw; if((*cp->hw->reset)(cp) == 0){ cp->present = 1; setvec(Int0vec + cp->hw->irq, etherintr); break; } | |
| 1992/0917 | } | |
| 1992/0922 | if(cp->present == 0) return; | |
| 1992/0410 | ||
| 1992/0424 | memset(cp->ba, 0xFF, sizeof(cp->ba)); | |
| 1992/0410 | ||
| 1992/0424 | cp->net.name = "ether"; cp->net.nconv = NType; cp->net.devp = &info; cp->net.protop = 0; cp->net.listen = 0; cp->net.clone = clonecon; cp->net.ninfo = 2; 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/0625 | for(i = 0; i < NType; i++) netadd(&cp->net, &cp->type[i], i); | |
| 1992/0403 | } | |
| 1992/0425 | void etherinit(void) | |
| 1992/0403 | { int ctlrno = 0; | |
| 1992/0922 | EtherCtlr *cp = &ctlr[ctlrno]; | |
| 1992/0424 | int i; | |
| 1992/0403 | ||
| 1992/0917 | if(cp->present == 0) return; | |
| 1992/0424 | cp->rh = 0; cp->ri = 0; for(i = 0; i < cp->nrb; i++) cp->rb[i].owner = Interface; cp->th = 0; cp->ti = 0; for(i = 0; i < cp->ntb; i++) cp->tb[i].owner = Host; | |
| 1992/0403 | /* * put the receiver online * and start the kproc | |
| 1992/0501 | */ | |
| 1992/0424 | (*cp->hw->online)(cp, 1); if(cp->kproc == 0){ sprint(cp->name, "ether%dkproc", ctlrno); kproc(cp->name, etherkproc, cp); | |
| 1992/0403 | } } | |