#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" #include "ether.h" /* * Half-arsed attempt at a general top-level * ethernet driver. Needs work: * handle multiple controllers * much tidying * set ethernet address * need a ctl file passed down to card drivers * so we can set options. */ struct Ctlr *softctlr; Chan* etherclone(Chan *c, Chan *nc) { return devclone(c, nc); } int etherwalk(Chan *c, char *name) { return netwalk(c, name, &softctlr->net); } void etherstat(Chan *c, char *dp) { netstat(c, dp, &softctlr->net); } Chan* etheropen(Chan *c, int omode) { return netopen(c, omode, &softctlr->net); } void ethercreate(Chan *c, char *name, int omode, ulong perm) { USED(c, name, omode, perm); 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, &softctlr->net); } long etherwrite(Chan *c, char *a, long n, ulong offset) { USED(offset); return streamwrite(c, a, n, 0); } void etherremove(Chan *c) { USED(c); error(Eperm); } void etherwstat(Chan *c, char *dp) { netwstat(c, dp, &softctlr->net); } static int isobuf(void *arg) { Ctlr *ctlr = arg; return ctlr->tb[ctlr->th].owner == Host; } static void etheroput(Queue *q, Block *bp) { Ctlr *ctlr; Type *type; Etherpkt *pkt; RingBuf *ring; int len, n, s; Block *nbp; uchar ea[6]; char *err; type = q->ptr; ctlr = type->ctlr; if(bp->type == M_CTL){ err = 0; qlock(ctlr); if(streamparse("connect", bp)){ if(type->type == -1) ctlr->all--; type->type = strtol((char*)bp->rptr, 0, 0); if(type->type == -1) ctlr->all++; } else if(streamparse("promiscuous", bp)) { if(type->prom) goto ctlout; if(type->filter){ err = "address already set"; goto ctlout; } type->prom = 1; ctlr->prom++; if(ctlr->prom+ctlr->filter == 1) (*ctlr->card.mode)(ctlr, 1); } else if(streamparse("address", bp)) { if(type->prom){ err = "already promiscuous"; goto ctlout; } if(parseether(ea, bp->rptr) < 0){ err = "bad ether address"; goto ctlout; } memmove(type->ea, ea, sizeof(ea)); if(!type->filter){ type->filter = 1; ctlr->filter++; if(ctlr->filter == 1) (*ctlr->card.mode)(ctlr, 3); } } ctlout: qunlock(ctlr); freeb(bp); if(err) error(err); return; } /* * Give packet a local address, return upstream if destined for * this machine. */ if(BLEN(bp) < ETHERHDRSIZE && (bp = pullup(bp, ETHERHDRSIZE)) == 0) return; pkt = (Etherpkt*)bp->rptr; memmove(pkt->s, type->ea, sizeof(type->ea)); if(memcmp(ctlr->ea, pkt->d, sizeof(ctlr->ea)) == 0){ len = blen(bp); if(bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){ putq(&ctlr->lbq, bp); wakeup(&ctlr->rr); } return; } if(memcmp(ctlr->ba, pkt->d, sizeof(ctlr->ba)) == 0 || ctlr->prom || ctlr->all){ len = blen(bp); nbp = copyb(bp, len); if(nbp = expandb(nbp, len >= ETHERMINTU ? len: ETHERMINTU)){ nbp->wptr = nbp->rptr+len; putq(&ctlr->lbq, nbp); wakeup(&ctlr->rr); } } /* * Only one transmitter at a time. */ qlock(&ctlr->tlock); if(waserror()){ qunlock(&ctlr->tlock); freeb(bp); nexterror(); } /* * Wait till we get an output buffer. * should try to restart. */ if(isobuf(ctlr) == 0){ tsleep(&ctlr->tr, isobuf, ctlr, 3*1000); if(isobuf(ctlr) == 0){ qunlock(&ctlr->tlock); freeb(bp); poperror(); return; } } ring = &ctlr->tb[ctlr->th]; /* * Copy message into buffer. */ len = 0; for(nbp = bp; nbp; nbp = nbp->next){ if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){ memmove(ring->pkt+len, nbp->rptr, n); len += n; } if(bp->flags & S_DELIM) break; } /* * Pad the packet (zero the pad). */ if(len < ETHERMINTU){ memset(ring->pkt+len, 0, ETHERMINTU-len); len = ETHERMINTU; } /* * Set up the transmit buffer and * start the transmission. */ s = splhi(); ring->len = len; ring->owner = Interface; ctlr->th = NEXT(ctlr->th, ctlr->ntb); (*ctlr->card.transmit)(ctlr); ctlr->outpackets++; splx(s); qunlock(&ctlr->tlock); freeb(bp); poperror(); } /* * Open an ether line discipline. */ static void etherstopen(Queue *q, Stream *s) { Ctlr *ctlr = softctlr; Type *type; type = &ctlr->type[s->id]; RD(q)->ptr = WR(q)->ptr = type; type->type = 0; type->q = RD(q); type->inuse = 1; memmove(type->ea, ctlr->ea, sizeof(type->ea)); type->ctlr = ctlr; } /* * Close ether line discipline. * * The locking is to synchronize changing the ethertype with * sending packets up the stream on interrupts. */ static int isclosed(void *arg) { return ((Type*)arg)->q == 0; } static void etherstclose(Queue *q) { Type *type = (Type*)(q->ptr); Ctlr *ctlr = type->ctlr; if(type->prom){ qlock(ctlr); ctlr->prom--; if(ctlr->prom+ctlr->filter == 0) (*ctlr->card.mode)(ctlr, 0); qunlock(ctlr); } if(type->filter){ qlock(ctlr); ctlr->filter--; if(ctlr->filter == 0) (*ctlr->card.mode)(ctlr, ctlr->prom ? 1 : 0); qunlock(ctlr); } if(type->type == -1){ qlock(ctlr); ctlr->all--; qunlock(ctlr); } /* * Mark as closing and wait for kproc * to close us. */ lock(&ctlr->clock); type->clist = ctlr->clist; ctlr->clist = type; unlock(&ctlr->clock); wakeup(&ctlr->rr); sleep(&type->cr, isclosed, type); type->type = 0; type->prom = 0; type->filter = 0; type->inuse = 0; netdisown(type); type->ctlr = 0; } static Qinfo info = { nullput, etheroput, etherstopen, etherstclose, "ether" }; static int clonecon(Chan *c) { Ctlr *ctlr = softctlr; Type *type; USED(c); for(type = ctlr->type; type < &ctlr->type[NType]; type++){ qlock(type); if(type->inuse || type->q){ qunlock(type); continue; } type->inuse = 1; memmove(type->ea, ctlr->ea, sizeof(type->ea)); netown(type, u->p->user, 0); qunlock(type); return type - ctlr->type; } exhausted("ether channels"); return 0; } static void statsfill(Chan *c, char *p, int n) { Ctlr *ctlr = softctlr; char buf[256]; USED(c); 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", ctlr->inpackets, ctlr->outpackets, ctlr->crcs, ctlr->overflows, ctlr->frames, ctlr->buffs, ctlr->oerrs, ctlr->ea[0], ctlr->ea[1], ctlr->ea[2], ctlr->ea[3], ctlr->ea[4], ctlr->ea[5]); strncpy(p, buf, n); } static void typefill(Chan *c, char *p, int n) { char buf[16]; Type *type; type = &softctlr->type[STREAMID(c->qid.path)]; sprint(buf, "%d", type->type); strncpy(p, buf, n); } int eaddrmatch(Ctlr *ctlr, uchar *ea) { Type *type; for(type = &ctlr->type[0]; type < &ctlr->type[NType]; type++){ if(type->q == 0 || ea[0] != type->ea[0]) continue; if(memcmp(ea, type->ea, sizeof(type->ea)) == 0) return 1; } return 0; } static void etherup(Ctlr *ctlr, Etherpkt *pkt, int len) { int t; Type *type; Block *bp; t = (pkt->type[0]<<8)|pkt->type[1]; for(type = &ctlr->type[0]; type < &ctlr->type[NType]; type++){ /* * Check for open, the right type, and flow control. */ if(type->q == 0) continue; if(t != type->type && type->type >= 0) continue; if(type->q->next->len > Streamhi) continue; /* * Only a trace channel gets packets destined for other machines. */ if(type->type != -1 && pkt->d[0] != 0xFF && (*pkt->d != *type->ea || memcmp(pkt->d, type->ea, sizeof(pkt->d)))) continue; if(waserror() == 0){ bp = allocb(len); memmove(bp->rptr, pkt, len); bp->wptr += len; bp->flags |= S_DELIM; PUTNEXT(type->q, bp); poperror(); } } } static int isinput(void *arg) { Ctlr *ctlr = arg; return ctlr->lbq.first || ctlr->rb[ctlr->rh].owner == Host || ctlr->clist; } static void etherkproc(void *arg) { Ctlr *ctlr = arg; RingBuf *ring; Block *bp; Type *type; if(waserror()){ print("%s noted\n", ctlr->name); /* fix if(ctlr->card.reset) (*ctlr->card.reset)(ctlr); */ ctlr->kproc = 0; nexterror(); } for(;;){ tsleep(&ctlr->rr, isinput, ctlr, 500); if(ctlr->card.watch) (*ctlr->card.watch)(ctlr); /* * Process any internal loopback packets. */ while(bp = getq(&ctlr->lbq)){ ctlr->inpackets++; etherup(ctlr, (Etherpkt*)bp->rptr, BLEN(bp)); freeb(bp); } /* * Process any received packets. */ while(ctlr->rb[ctlr->rh].owner == Host){ ctlr->inpackets++; ring = &ctlr->rb[ctlr->rh]; etherup(ctlr, (Etherpkt*)ring->pkt, ring->len); ring->owner = Interface; ctlr->rh = NEXT(ctlr->rh, ctlr->nrb); } /* * Close Types requesting it. */ if(ctlr->clist){ lock(&ctlr->clock); for(type = ctlr->clist; type; type = type->clist){ type->q = 0; wakeup(&type->cr); } ctlr->clist = 0; unlock(&ctlr->clock); } } } static void etherintr(Ureg *ur, void *a) { Ctlr *ctlr = softctlr; USED(ur, a); (*ctlr->card.intr)(ctlr); } static void reset(Ctlr *ctlr) { int i; if(ctlr->nrb == 0) ctlr->nrb = Nrb; ctlr->rb = xalloc(sizeof(RingBuf)*ctlr->nrb); if(ctlr->ntb == 0) ctlr->ntb = Ntb; ctlr->tb = xalloc(sizeof(RingBuf)*ctlr->ntb); memset(ctlr->ba, 0xFF, sizeof(ctlr->ba)); ctlr->net.name = "ether"; ctlr->net.nconv = NType; ctlr->net.devp = &info; ctlr->net.protop = 0; ctlr->net.listen = 0; ctlr->net.clone = clonecon; ctlr->net.ninfo = 2; ctlr->net.info[0].name = "stats"; ctlr->net.info[0].fill = statsfill; ctlr->net.info[1].name = "type"; ctlr->net.info[1].fill = typefill; for(i = 0; i < NType; i++) netadd(&ctlr->net, &ctlr->type[i], i); } extern int wd8003reset(Ctlr*); extern int ne2000reset(Ctlr*); extern int ccc509reset(Ctlr*); extern int nsciareset(Ctlr*); extern int ne2000PCMreset(Ctlr*); #define NCARD 32 struct { char *type; int (*reset)(Ctlr*); } cards[NCARD+1]; void addethercard(char *t, int (*r)(Ctlr*)) { static int ncard; if(ncard == NCARD) panic("too many ether cards"); cards[ncard].type = t; cards[ncard].reset = r; ncard++; } void etherreset(void) { Ctlr *ctlr; int i, n, ctlrno; if(softctlr == 0) softctlr = xalloc(sizeof(Ctlr)); else memset(softctlr, 0, sizeof(Ctlr)); ctlr = softctlr; for(ctlrno = i = 0; isaconfig("ether", i, &ctlr->card); i++){ for(n = 0; cards[n].type; n++){ if(strcmp(cards[n].type, ctlr->card.type)) continue; ctlr->ctlrno = ctlrno; memmove(ctlr->ea, ctlr->card.ea, sizeof(ctlr->ea)); if((*cards[n].reset)(ctlr)) break; /*ctlrno++;*/ ctlr->present = 1; /* * IRQ2 doesn't really exist, it's used to gang the interrupt * controllers together. A device set to IRQ2 will appear on * the second interrupt controller as IRQ9. */ if(ctlr->card.irq == 2) ctlr->card.irq = 9; setvec(Int0vec + ctlr->card.irq, etherintr, 0); print("ether%d:%s: port %lux irq %d addr %lux size %d width %d:", ctlr->ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq, ctlr->card.mem, ctlr->card.size, ctlr->card.bit16 ? 16: 8); for(i = 0; i < sizeof(ctlr->ea); i++) print("%2.2ux", ctlr->ea[i]); print("\n"); reset(ctlr); return; } memset(softctlr, 0, sizeof(Ctlr)); } } void etherinit(void) { Ctlr *ctlr = softctlr; int i; if(ctlr->present == 0) return; ctlr->rh = 0; ctlr->ri = 0; for(i = 0; i < ctlr->nrb; i++) ctlr->rb[i].owner = Interface; ctlr->th = 0; ctlr->ti = 0; for(i = 0; i < ctlr->ntb; i++) ctlr->tb[i].owner = Host; } Chan* etherattach(char *spec) { Ctlr *ctlr = softctlr; if(ctlr->present == 0) error(Enodev); /* * Enable the interface * and start the kproc. */ (*ctlr->card.attach)(ctlr); if(ctlr->kproc == 0){ sprint(ctlr->name, "ether%dkproc", 0); ctlr->kproc = 1; kproc(ctlr->name, etherkproc, ctlr); } return devattach('l', spec); }