From d6f9362718f54eb91441ed4fd62507e7f91b1428 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 22 Dec 1992 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1992-12-22 --- pc/devether.c | 477 +++++++++++++++++++++++------------------ pc/ether.h | 142 +++++++++++++ pc/ether2000.c | 108 ++++++++++ pc/ether503.c | 43 ++++ pc/ether509.c | 216 +++++++++---------- pc/ether8003.c | 204 ++++++++++++++++++ pc/ether8390.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++++ pc/io.h | 94 --------- 8 files changed, 1426 insertions(+), 419 deletions(-) create mode 100644 pc/ether.h create mode 100644 pc/ether2000.c create mode 100644 pc/ether503.c create mode 100644 pc/ether8003.c create mode 100644 pc/ether8390.c diff --git a/pc/devether.c b/pc/devether.c index f5416ba2d541d68e45a5144403ae20ab9b119421..c8b6ba2d85fcf11f9869ab3b5e358cb2a0262848 100644 --- a/pc/devether.c +++ b/pc/devether.c @@ -7,6 +7,8 @@ #include "io.h" #include "devtab.h" +#include "ether.h" + /* * Half-arsed attempt at a general top-level * ethernet driver. Needs work: @@ -14,33 +16,33 @@ * much tidying * set ethernet address */ -extern EtherHw ether509; -extern EtherHw ether80x3; +extern Board ether8003; +extern Board ether503; +extern Board ether2000; +extern Board ether509; + +/* + * The ordering here is important for those boards + * using the DP8390 (WD8003, 3COM503 and NE2000) as + * attempting to determine if a board is a NE2000 + * cannot be done passively, so it must be last to + * prevent scrogging one of the others. + */ +static Board *boards[] = { + ðer8003, + ðer503, + ðer2000, -static EtherHw *etherhw[] = { ðer509, - ðer80x3, 0 }; enum { - Nctlr = 1, + NCtlr = 1, }; -static struct EtherCtlr ctlr[Nctlr]; - -#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)) - -Chan* -etherattach(char *spec) -{ - if(ctlr[0].present == 0) - error(Enodev); - return devattach('l', spec); -} +static struct Ctlr *softctlr[NCtlr]; +static int nctlr; Chan* etherclone(Chan *c, Chan *nc) @@ -51,19 +53,19 @@ etherclone(Chan *c, Chan *nc) int etherwalk(Chan *c, char *name) { - return netwalk(c, name, &ctlr[0].net); + return netwalk(c, name, &softctlr[0]->net); } void etherstat(Chan *c, char *dp) { - netstat(c, dp, &ctlr[0].net); + netstat(c, dp, &softctlr[0]->net); } Chan* etheropen(Chan *c, int omode) { - return netopen(c, omode, &ctlr[0].net); + return netopen(c, omode, &softctlr[0]->net); } void @@ -83,7 +85,7 @@ etherclose(Chan *c) long etherread(Chan *c, void *a, long n, ulong offset) { - return netread(c, a, n, offset, &ctlr[0].net); + return netread(c, a, n, offset, &softctlr[0]->net); } long @@ -103,166 +105,193 @@ etherremove(Chan *c) void etherwstat(Chan *c, char *dp) { - netwstat(c, dp, &ctlr[0].net); + netwstat(c, dp, &softctlr[0]->net); } static int isobuf(void *arg) { - EtherCtlr *cp = arg; + Ctlr *ctlr = arg; - return cp->tb[cp->th].owner == Host; + return ctlr->tb[ctlr->th].owner == Host; } static void etheroput(Queue *q, Block *bp) { - EtherCtlr *cp; - EtherType *tp; - Etherpkt *p; - EtherBuf *tb; + Ctlr *ctlr; + Type *type; + Etherpkt *pkt; + RingBuf *ring; int len, n; Block *nbp; + type = q->ptr; + ctlr = type->ctlr; if(bp->type == M_CTL){ - tp = q->ptr; - if(streamparse("connect", bp)) - tp->type = strtol((char*)bp->rptr, 0, 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)) { - tp->prom = 1; - (*tp->ctlr->hw->mode)(tp->ctlr, 1); + type->prom = 1; + ctlr->prom++; + if(ctlr->prom == 1) + (*ctlr->board->mode)(ctlr, 1); } + qunlock(ctlr); freeb(bp); return; } - cp = ((EtherType*)q->ptr)->ctlr; - /* - * give packet a local address, return upstream if destined for + * Give packet a local address, return upstream if destined for * this machine. */ if(BLEN(bp) < ETHERHDRSIZE && (bp = pullup(bp, ETHERHDRSIZE)) == 0) return; - p = (Etherpkt*)bp->rptr; - memmove(p->s, cp->ea, sizeof(cp->ea)); - if(memcmp(cp->ea, p->d, sizeof(cp->ea)) == 0){ + pkt = (Etherpkt*)bp->rptr; + memmove(pkt->s, ctlr->ea, sizeof(ctlr->ea)); + if(memcmp(ctlr->ea, pkt->d, sizeof(ctlr->ea)) == 0){ len = blen(bp); - if (bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){ - putq(&cp->lbq, bp); - wakeup(&cp->rr); + if(bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){ + putq(&ctlr->lbq, bp); + wakeup(&ctlr->rr); } return; } - if(memcmp(cp->ba, p->d, sizeof(cp->ba)) == 0){ + 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(&cp->lbq, nbp); - wakeup(&cp->rr); + putq(&ctlr->lbq, nbp); + wakeup(&ctlr->rr); } } /* - * only one transmitter at a time + * Only one transmitter at a time. */ - qlock(&cp->tlock); + qlock(&ctlr->tlock); if(waserror()){ freeb(bp); - qunlock(&cp->tlock); + qunlock(&ctlr->tlock); nexterror(); } /* - * wait till we get an output buffer. + * Wait till we get an output buffer. * should try to restart. */ - sleep(&cp->tr, isobuf, cp); + sleep(&ctlr->tr, isobuf, ctlr); - tb = &cp->tb[cp->th]; + ring = &ctlr->tb[ctlr->th]; /* - * copy message into buffer + * Copy message into buffer. */ len = 0; for(nbp = bp; nbp; nbp = nbp->next){ if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){ - memmove(tb->pkt+len, nbp->rptr, n); + memmove(ring->pkt+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) + * Pad the packet (zero the pad). */ if(len < ETHERMINTU){ - memset(tb->pkt+len, 0, ETHERMINTU-len); + memset(ring->pkt+len, 0, ETHERMINTU-len); len = ETHERMINTU; } /* - * set up the transmit buffer and - * start the transmission + * Set up the transmit buffer and + * start the transmission. */ - cp->outpackets++; - tb->len = len; - tb->owner = Interface; - cp->th = NEXT(cp->th, cp->ntb); - (*cp->hw->transmit)(cp); + ctlr->outpackets++; + ring->len = len; + ring->owner = Interface; + ctlr->th = NEXT(ctlr->th, ctlr->ntb); + (*ctlr->board->transmit)(ctlr); freeb(bp); - qunlock(&cp->tlock); + qunlock(&ctlr->tlock); poperror(); } /* - * open an ether line discipline - * - * the lock is to synchronize changing the ethertype with - * sending packets up the stream on interrupts. + * Open an ether line discipline. */ static void etherstopen(Queue *q, Stream *s) { - EtherCtlr *cp = &ctlr[0]; - EtherType *tp; - - 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); + Ctlr *ctlr = softctlr[0]; + Type *type; + + type = &ctlr->type[s->id]; + RD(q)->ptr = WR(q)->ptr = type; + type->type = 0; + type->q = RD(q); + type->inuse = 1; + type->ctlr = ctlr; } /* - * close ether line discipline + * Close ether line discipline. * - * the lock is to synchronize changing the ethertype with - * sending packets up the stream on interrupts. + * 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) { - EtherType *tp; - - tp = (EtherType*)(q->ptr); - if(tp->prom) - (*tp->ctlr->hw->mode)(tp->ctlr, 0); - qlock(tp); - tp->type = 0; - tp->q = 0; - tp->prom = 0; - tp->inuse = 0; - netdisown(tp); - tp->ctlr = 0; - qunlock(tp); + Type *type = (Type*)(q->ptr); + Ctlr *ctlr = type->ctlr; + + if(type->prom){ + qlock(ctlr); + ctlr->prom--; + if(ctlr->prom == 0) + (*ctlr->board->mode)(ctlr, 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->inuse = 0; + netdisown(type); + type->ctlr = 0; } static Qinfo info = { @@ -276,20 +305,20 @@ static Qinfo info = { static int clonecon(Chan *c) { - EtherCtlr *cp = &ctlr[0]; - EtherType *tp; + Ctlr *ctlr = softctlr[0]; + Type *type; USED(c); - for(tp = cp->type; tp < &cp->type[NType]; tp++){ - qlock(tp); - if(tp->inuse || tp->q){ - qunlock(tp); + for(type = ctlr->type; type < &ctlr->type[NType]; type++){ + qlock(type); + if(type->inuse || type->q){ + qunlock(type); continue; } - tp->inuse = 1; - netown(tp, u->p->user, 0); - qunlock(tp); - return tp - cp->type; + type->inuse = 1; + netown(type, u->p->user, 0); + qunlock(type); + return type - ctlr->type; } exhausted("ether channels"); return 0; @@ -298,14 +327,15 @@ clonecon(Chan *c) static void statsfill(Chan *c, char *p, int n) { - EtherCtlr *cp = &ctlr[0]; + Ctlr *ctlr = softctlr[0]; 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", - 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]); + 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); } @@ -313,104 +343,113 @@ static void typefill(Chan *c, char *p, int n) { char buf[16]; - EtherType *tp; + Type *type; - tp = &ctlr[0].type[STREAMID(c->qid.path)]; - sprint(buf, "%d", tp->type); + type = &softctlr[0]->type[STREAMID(c->qid.path)]; + sprint(buf, "%d", type->type); strncpy(p, buf, n); } static void -etherup(EtherCtlr *cp, void *data, int len) +etherup(Ctlr *ctlr, Etherpkt *pkt, int len) { - Etherpkt *p; int t; - EtherType *tp; + Type *type; Block *bp; - p = data; - t = (p->type[0]<<8)|p->type[1]; - for(tp = &cp->type[0]; tp < &cp->type[NType]; tp++){ - /* - * check before locking just to save a lock - */ - if(tp->q == 0 || (t != tp->type && tp->type != -1)) - continue; + t = (pkt->type[0]<<8)|pkt->type[1]; + for(type = &ctlr->type[0]; type < &ctlr->type[NType]; type++){ /* - * only a trace channel gets packets destined for other machines + * Check for open, the right type, and flow control. */ - if(tp->type != -1 && p->d[0] != 0xFF && memcmp(p->d, cp->ea, sizeof(p->d))) + if(type->q == 0) + continue; + if(t != type->type && type->type != -1) + continue; + if(type->q->next->len > Streamhi) continue; /* - * check after locking to make sure things didn't - * change under foot + * Only a trace channel gets packets destined for other machines. */ - if(canqlock(tp) == 0) - continue; - if(tp->q == 0 || tp->q->next->len > Streamhi || (t != tp->type && tp->type != -1)){ - qunlock(tp); + if(type->type != -1 && pkt->d[0] != 0xFF + && (*pkt->d != *ctlr->ea || memcmp(pkt->d, ctlr->ea, sizeof(pkt->d)))) continue; - } - if(!waserror()){ + + if(waserror() == 0){ bp = allocb(len); - memmove(bp->rptr, p, len); + memmove(bp->rptr, pkt, len); bp->wptr += len; bp->flags |= S_DELIM; - PUTNEXT(tp->q, bp); + PUTNEXT(type->q, bp); poperror(); } - qunlock(tp); } } static int isinput(void *arg) { - EtherCtlr *cp = arg; + Ctlr *ctlr = arg; - return cp->lbq.first || cp->rb[cp->rh].owner == Host; + return ctlr->lbq.first || ctlr->rb[ctlr->rh].owner == Host || ctlr->clist; } static void etherkproc(void *arg) { - EtherCtlr *cp = arg; - EtherBuf *rb; + Ctlr *ctlr = arg; + RingBuf *ring; Block *bp; + Type *type; if(waserror()){ - print("%s noted\n", cp->name); - if(cp->hw->init) - (*cp->hw->init)(cp); - cp->kproc = 0; + print("%s noted\n", ctlr->name); + /* fix + if(ctlr->board->reset) + (*ctlr->board->reset)(ctlr); + */ + ctlr->kproc = 0; nexterror(); } - cp->kproc = 1; + for(;;){ - tsleep(&cp->rr, isinput, cp, 500); - if(cp->hw->tweak) - (*cp->hw->tweak)(cp); + tsleep(&ctlr->rr, isinput, ctlr, 500); + if(ctlr->board->watch) + (*ctlr->board->watch)(ctlr); /* - * process any internal loopback packets + * Process any internal loopback packets. */ - while(bp = getq(&cp->lbq)){ - cp->inpackets++; - etherup(cp, bp->rptr, BLEN(bp)); + while(bp = getq(&ctlr->lbq)){ + ctlr->inpackets++; + etherup(ctlr, (Etherpkt*)bp->rptr, BLEN(bp)); freeb(bp); } /* - * process any received packets + * Process any received packets. */ - while(cp->rb[cp->rh].owner == Host){ - cp->inpackets++; - rb = &cp->rb[cp->rh]; - etherup(cp, rb->pkt, rb->len); - rb->owner = Interface; - cp->rh = NEXT(cp->rh, cp->nrb); + 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); } } } @@ -418,75 +457,105 @@ etherkproc(void *arg) static void etherintr(Ureg *ur) { - EtherCtlr *cp = &ctlr[0]; + Ctlr *ctlr = softctlr[0]; USED(ur); - (*cp->hw->intr)(cp); + (*ctlr->board->intr)(ctlr); } void etherreset(void) { - EtherCtlr *cp; - EtherHw **hw; + Ctlr *ctlr; + Board **board; int i; - cp = &ctlr[0]; - for(hw = etherhw; *hw; hw++){ - cp->hw = *hw; - if((*cp->hw->reset)(cp) == 0){ - cp->present = 1; - setvec(Int0vec + cp->hw->irq, etherintr); + if(softctlr[nctlr] == 0) + softctlr[nctlr] = xalloc(sizeof(Ctlr)); + ctlr = softctlr[nctlr]; + for(board = boards; *board; board++){ + ctlr->board = *board; + if((*ctlr->board->reset)(ctlr) == 0){ + 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->board->irq == 2) + ctlr->board->irq = 9; + setvec(Int0vec + ctlr->board->irq, etherintr); break; } } - if(cp->present == 0) + if(ctlr->present == 0) return; - - memset(cp->ba, 0xFF, sizeof(cp->ba)); - - 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; + nctlr++; + + 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(&cp->net, &cp->type[i], i); + netadd(&ctlr->net, &ctlr->type[i], i); } void etherinit(void) { int ctlrno = 0; - EtherCtlr *cp = &ctlr[ctlrno]; + Ctlr *ctlr = softctlr[ctlrno]; int i; - if(cp->present == 0) + if(ctlr->present == 0) return; - cp->rh = 0; - cp->ri = 0; - for(i = 0; i < cp->nrb; i++) - cp->rb[i].owner = Interface; + 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) +{ + int ctlrno = 0; + Ctlr *ctlr = softctlr[ctlrno]; - cp->th = 0; - cp->ti = 0; - for(i = 0; i < cp->ntb; i++) - cp->tb[i].owner = Host; + if(ctlr->present == 0) + error(Enodev); /* - * put the receiver online - * and start the kproc + * Enable the interface + * and start the kproc. */ - (*cp->hw->online)(cp, 1); - if(cp->kproc == 0){ - sprint(cp->name, "ether%dkproc", ctlrno); - kproc(cp->name, etherkproc, cp); + (*ctlr->board->attach)(ctlr); + if(ctlr->kproc == 0){ + sprint(ctlr->name, "ether%dkproc", ctlrno); + ctlr->kproc = 1; + kproc(ctlr->name, etherkproc, ctlr); } + return devattach('l', spec); } diff --git a/pc/ether.h b/pc/ether.h new file mode 100644 index 0000000000000000000000000000000000000000..a98b61aa52d49cf262aea6d5f0f5701848bb5034 --- /dev/null +++ b/pc/ether.h @@ -0,0 +1,142 @@ +/* + * All the goo for PC ethernet cards. + */ +typedef struct Board Board; +typedef struct RingBuf RingBuf; +typedef struct Type Type; +typedef struct Ctlr Ctlr; + +/* + * Hardware interface. + */ +struct Board { + int (*reset)(Ctlr*); + void (*init)(Ctlr*); + void (*attach)(Ctlr*); + void (*mode)(Ctlr*, int); + + void (*receive)(Ctlr*); + void (*transmit)(Ctlr*); + void (*intr)(Ctlr*); + void (*watch)(Ctlr*); + + ulong io; /* interface I/O base address */ + uchar irq; /* interrupt level */ + uchar bit16; /* true if a 16 bit interface */ + + uchar ram; /* true if interface has shared memory */ + ulong ramstart; /* interface shared memory address start */ + ulong ramstop; /* interface shared memory address end */ + + ulong dp8390; /* I/O address of 8390 (if any) */ + ulong data; /* I/O data port if no shared memory */ + uchar tstart; /* 8390 ring addresses */ + uchar pstart; + uchar pstop; +}; + +/* + * Software ring buffer. + */ +struct RingBuf { + uchar owner; + uchar busy; + ushort len; + uchar pkt[sizeof(Etherpkt)]; +}; + +enum { + Host = 0, /* buffer owned by host */ + Interface = 1, /* buffer owned by interface */ + + Nrb = 16, /* default number of receive buffers */ + Ntb = 4, /* default number of transmit buffers */ +}; + +/* + * One per ethernet packet type. + */ +struct Type { + QLock; + Netprot; /* stat info */ + int type; /* ethernet type */ + int prom; /* promiscuous mode */ + Queue *q; + int inuse; + Ctlr *ctlr; + + Rendez cr; /* rendezvous for close */ + Type *clist; /* close list */ +}; + +enum { + NType = 9, /* types/interface */ +}; + +/* + * Software controller. + */ +struct Ctlr { + QLock; + + Board *board; + int present; + + ushort nrb; /* number of software receive buffers */ + ushort ntb; /* number of software transmit buffers */ + RingBuf *rb; /* software receive buffers */ + RingBuf *tb; /* software transmit buffers */ + + uchar ea[6]; /* ethernet address */ + uchar ba[6]; /* broadcast address */ + + Rendez rr; /* rendezvous for a receive buffer */ + ushort rh; /* first receive buffer belonging to host */ + ushort ri; /* first receive buffer belonging to interface */ + + Rendez tr; /* rendezvous for a transmit buffer */ + QLock tlock; /* semaphore on th */ + ushort th; /* first transmit buffer belonging to host */ + ushort ti; /* first transmit buffer belonging to interface */ + + Type type[NType]; + int all; /* number of channels listening to all packets */ + Type *clist; /* channels waiting to close */ + Lock clock; /* lock for clist */ + int prom; /* number of promiscuous channels */ + int kproc; /* true if kproc started */ + char name[NAMELEN]; /* name of kproc */ + Network net; + + Queue lbq; /* software loopback packet queue */ + + 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 */ +}; + +#define NEXT(x, l) (((x)+1)%(l)) +#define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) + +/* + * The Western Digital 8003 and 8013 series, the NE2000 + * and the 3Com 503 all use the DP8390 Network Interface + * Chip. + */ +extern void dp8390reset(Ctlr*); +extern void dp8390attach(Ctlr*); +extern void dp8390mode(Ctlr*, int); +extern void dp8390setea(Ctlr*); +extern void *dp8390read(Ctlr*, void*, ulong, ulong); +extern void dp8390receive(Ctlr*); +extern void dp8390transmit(Ctlr*); +extern void dp8390intr(Ctlr*); + +enum { + Dp8390BufSz = 256, /* hardware ring buffer size */ +}; diff --git a/pc/ether2000.c b/pc/ether2000.c new file mode 100644 index 0000000000000000000000000000000000000000..e357b3d3f2e4be7fb3ede3c00fe4fc919b789e8f --- /dev/null +++ b/pc/ether2000.c @@ -0,0 +1,108 @@ +#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" + +/* + * Driver written for the 'Notebook Computer Ethernet LAN Adapter', + * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL + * laptop. The manual says NE2000 compatible. + * The interface appears to be pretty well described in the National + * Semiconductor Local Area Network Databook (1992) as one of the + * AT evaluation boards. + * + * The NE2000 is really just a DP8390 plus a data port + * and a reset port. + */ +enum { + Data = 0x10, /* offset from I/O base of data port */ + Reset = 0x18, /* offset from I/O base of reset port */ +}; + +static int +reset(Ctlr *ctlr) +{ + Board *board = ctlr->board; + ushort buf[16]; + int i; + + /* + * We look for boards. + * We look for boards to make us barf. + */ + for(board->io = 0x300; board->io < 0x380; board->io += 0x20){ + /* + * Reset the board. This is done by doing a read + * followed by a write to the Reset address. + */ + outb(board->io+Reset, inb(board->io+Reset)); + + /* + * Init the (possible) chip, then use the (possible) + * chip to read the (possible) PROM for ethernet address + * and a marker byte. + * We could just look at the DP8390 command register after + * initialisation has been tried, but that wouldn't be + * enough, there are other ethernet boards which could + * match. + */ + board->dp8390 = board->io; + board->data = board->io+Data; + dp8390reset(ctlr); + memset(buf, 0, sizeof(buf)); + dp8390read(ctlr, buf, 0, sizeof(buf)); + if(buf[0x0E] == 0x57 && buf[0x0F] == 0x57) + break; + } + if(board->io >= 0x380) + return -1; + + /* + * Stupid machine. We asked for shorts, we got shorts, + * although the PROM is a byte array. + * Now we can set the ethernet address. + */ + for(i = 0; i < sizeof(ctlr->ea); i++) + ctlr->ea[i] = buf[i]; + dp8390setea(ctlr); + + print("NE2000 I/O addr %lux width %d addr %lux size %d irq %d:", + board->io, board->bit16 ? 16: 8, board->ramstart, + board->ramstop-board->ramstart, board->irq); + for(i = 0; i < sizeof(ctlr->ea); i++) + print(" %2.2ux", ctlr->ea[i]); + print("\n"); + + return 0; +} + +Board ether2000 = { + reset, + 0, /* init */ + dp8390attach, + dp8390mode, + dp8390receive, + dp8390transmit, + dp8390intr, + 0, /* watch */ + + 0x300, /* addr */ + 2, /* irq */ + 1, /* bit16 */ + + 0, /* ram */ + 0x4000, /* ramstart */ + 0x4000+16*1024, /* ramstop */ + + 0x300, /* dp8390 */ + 0x300+Data, /* data */ + 0x4000/Dp8390BufSz, /* tstart */ + 0x4600/Dp8390BufSz, /* pstart */ + 0x8000/Dp8390BufSz, /* pstop */ +}; diff --git a/pc/ether503.c b/pc/ether503.c new file mode 100644 index 0000000000000000000000000000000000000000..f518bfdee9bf366c421161ca46908cfeeb99279b --- /dev/null +++ b/pc/ether503.c @@ -0,0 +1,43 @@ +#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" + +static int +reset(Ctlr *ctlr) +{ + USED(ctlr); + + return -1; +} + +Board ether503 = { + reset, + 0, /* init */ + 0, /* attach */ + 0, /* mode */ + 0, /* receive */ + 0, /* transmit */ + 0, /* interrupt */ + 0, /* watch */ + + 0, /* addr */ + 0, /* irq */ + 0, /* bit16 */ + + 0, /* ram */ + 0, /* ramstart */ + 0, /* ramstop */ + + 0, /* dp8390 */ + 0, /* data */ + 0, /* tstart */ + 0, /* pstart */ + 0, /* pstop */ +}; diff --git a/pc/ether509.c b/pc/ether509.c index 1fe2cdfd2b964571ad31fd80bcb6e117871773b2..fd750d29e9958321b1fd80424e0fab8a654a2c33 100644 --- a/pc/ether509.c +++ b/pc/ether509.c @@ -7,15 +7,7 @@ #include "io.h" #include "devtab.h" -#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)) - -enum { - Nrb = 16, /* software receive buffers */ - Ntb = 4, /* software transmit buffers */ -}; +#include "ether.h" enum { IDport = 0x0100, /* anywhere between 0x0100 and 0x01F0 */ @@ -81,14 +73,14 @@ enum { RxEmpty = 0x8000, /* Incomplete or FIFO empty */ }; -#define COMMAND(hw, cmd, a) outs(hw->addr+Command, ((cmd)<<11)|(a)) +#define COMMAND(board, cmd, a) outs(board->io+Command, ((cmd)<<11)|(a)) /* - * Write two 0 bytes to identify the IDport and them reset the - * ID sequence. Then send the ID sequenced to the card to get - * the card it into command state. + * Write two 0 bytes to identify the IDport and then reset the + * ID sequence. Then send the ID sequence to the card to get + * the card into command state. */ -void +static void idseq(void) { int i; @@ -108,26 +100,21 @@ idseq(void) } /* - * get configuration parameters + * Get configuration parameters. */ static int -reset(EtherCtlr *cp) +reset(Ctlr *ctlr) { - EtherHw *hw = cp->hw; + Board *board = ctlr->board; int i, ea; ushort x, acr; - cp->rb = xspanalloc(sizeof(EtherBuf)*Nrb, BY2PG, 0); - cp->nrb = Nrb; - cp->tb = xspanalloc(sizeof(EtherBuf)*Ntb, BY2PG, 0); - cp->ntb = Ntb; - /* * Do the little configuration dance. We only look * at the first board that responds, if we ever have more * than one we'll need to modify this sequence. * - * 2. get to cammand state, reset, then return to command state + * 2. get to command state, reset, then return to command state */ idseq(); outb(IDport, 0xC1); @@ -174,8 +161,8 @@ reset(EtherCtlr *cp) * * Enable the adapter. */ - hw->addr = (acr & 0x1F)*0x10 + 0x200; - outb(hw->addr+ConfigControl, 0x01); + board->io = (acr & 0x1F)*0x10 + 0x200; + outb(board->io+ConfigControl, 0x01); /* * Read the IRQ from the Resource Configuration Register @@ -183,16 +170,16 @@ reset(EtherCtlr *cp) * The EEPROM command is 8bits, the lower 6 bits being * the address offset. */ - hw->irq = (ins(hw->addr+ResourceConfig)>>12) & 0x0F; + board->irq = (ins(board->io+ResourceConfig)>>12) & 0x0F; for(ea = 0, i = 0; i < 3; i++, ea += 2){ - while(ins(hw->addr+EEPROMcmd) & 0x8000) + while(ins(board->io+EEPROMcmd) & 0x8000) ; - outs(hw->addr+EEPROMcmd, (2<<6)|i); - while(ins(hw->addr+EEPROMcmd) & 0x8000) + outs(board->io+EEPROMcmd, (2<<6)|i); + while(ins(board->io+EEPROMcmd) & 0x8000) ; - x = ins(hw->addr+EEPROMdata); - cp->ea[ea] = (x>>8) & 0xFF; - cp->ea[ea+1] = x & 0xFF; + x = ins(board->io+EEPROMdata); + ctlr->ea[ea] = (x>>8) & 0xFF; + ctlr->ea[ea+1] = x & 0xFF; } /* @@ -201,58 +188,38 @@ reset(EtherCtlr *cp) * Commands have the format 'CCCCCAAAAAAAAAAA' where C * is a bit in the command and A is a bit in the argument. */ - COMMAND(hw, SelectWindow, 2); + COMMAND(board, SelectWindow, 2); for(i = 0; i < 6; i++) - outb(hw->addr+i, cp->ea[i]); + outb(board->io+i, ctlr->ea[i]); /* * Finished with window 2. * Set window 1 for normal operation. */ - COMMAND(hw, SelectWindow, 1); + COMMAND(board, SelectWindow, 1); /* * If we have a 10BASE2 transceiver, start the DC-DC * converter. Wait > 800 microseconds. */ if(((acr>>14) & 0x03) == 0x03){ - COMMAND(hw, StartCoax, 0); + COMMAND(board, StartCoax, 0); delay(1); } - print("3C509 I/O addr %lux irq %d:", hw->addr, hw->irq); - for(i = 0; i < sizeof(cp->ea); i++) - print(" %2.2ux", cp->ea[i]); + print("3C509 I/O addr %lux irq %d:", board->io, board->irq); + for(i = 0; i < sizeof(ctlr->ea); i++) + print(" %2.2ux", ctlr->ea[i]); print("\n"); return 0; } static void -mode(EtherCtlr *cp, int on) -{ - EtherHw *hw = cp->hw; - - qlock(cp); - if(on){ - cp->prom++; - if(cp->prom == 1) - COMMAND(hw, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr); - } - else{ - cp->prom--; - if(cp->prom == 0) - COMMAND(hw, SetRxFilter, Broadcast|MyEtherAddr); - } - qunlock(cp); -} - -static void -online(EtherCtlr *cp, int on) +attach(Ctlr *ctlr) { - EtherHw *hw = cp->hw; + Board *board = ctlr->board; - USED(on); /* BUG */ /* * Set the receiver packet filter for our own and * and broadcast addresses, set the interrupt masks @@ -261,35 +228,44 @@ online(EtherCtlr *cp, int on) * is the receiver interrupt. If the transmit FIFO fills up, * we will also see TxAvailable interrupts. */ - COMMAND(hw, SetRxFilter, Broadcast|MyEtherAddr); - COMMAND(hw, SetReadZeroMask, AllIntr|Latch); - COMMAND(hw, SetIntrMask, AllIntr|Latch); - COMMAND(hw, RxEnable, 0); - COMMAND(hw, TxEnable, 0); + COMMAND(board, SetRxFilter, Broadcast|MyEtherAddr); + COMMAND(board, SetReadZeroMask, AllIntr|Latch); + COMMAND(board, SetIntrMask, AllIntr|Latch); + COMMAND(board, RxEnable, 0); + COMMAND(board, TxEnable, 0); } +static void +mode(Ctlr *ctlr, int on) +{ + Board *board = ctlr->board; + + if(on) + COMMAND(board, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr); + else + COMMAND(board, SetRxFilter, Broadcast|MyEtherAddr); +} static int -getdiag(EtherHw *hw) +getdiag(Board *board) { int bytes; - COMMAND(hw, SelectWindow, 4); - bytes = ins(hw->addr+0x04); - COMMAND(hw, SelectWindow, 1); + COMMAND(board, SelectWindow, 4); + bytes = ins(board->io+0x04); + COMMAND(board, SelectWindow, 1); return bytes & 0xFFFF; } static void -receive(EtherCtlr *cp) +receive(Ctlr *ctlr) { - EtherHw *hw = cp->hw; + Board *board = ctlr->board; ushort status; - EtherBuf *rb; + RingBuf *rb; int len; - while(((status = ins(hw->addr+RxStatus)) & RxEmpty) == 0){ - + while(((status = ins(board->io+RxStatus)) & RxEmpty) == 0){ /* * If we had an error, log it and continue * without updating the ring. @@ -298,19 +274,19 @@ receive(EtherCtlr *cp) switch(status & RxErrMask){ case RxErrOverrun: /* Overrrun */ - cp->overflows++; + ctlr->overflows++; break; case RxErrOversize: /* Oversize Packet (>1514) */ case RxErrRunt: /* Runt Packet */ - cp->buffs++; + ctlr->buffs++; break; case RxErrFraming: /* Alignment (Framing) */ - cp->frames++; + ctlr->frames++; break; case RxErrCRC: /* CRC */ - cp->crcs++; + ctlr->crcs++; break; } } @@ -320,7 +296,7 @@ receive(EtherCtlr *cp) * free ring buffer, if any. * The CRC is already stripped off. */ - rb = &cp->rb[cp->ri]; + rb = &ctlr->rb[ctlr->ri]; if(rb->owner == Interface){ len = (status & RxByteMask); rb->len = len; @@ -330,44 +306,44 @@ receive(EtherCtlr *cp) * doubleword. We can pick them out 16-bits * at a time (can try 32-bits at a time * later). - insl(hw->addr+Fifo, rb->pkt, HOWMANY(len, 4)); + insl(board->io+Fifo, rb->pkt, HOWMANY(len, 4)); */ - inss(hw->addr+Fifo, rb->pkt, HOWMANY(len, 2)); + inss(board->io+Fifo, rb->pkt, HOWMANY(len, 2)); /* * Update the ring. */ rb->owner = Host; - cp->ri = NEXT(cp->ri, cp->nrb); + ctlr->ri = NEXT(ctlr->ri, ctlr->nrb); } } + /* * Discard the packet as we're done with it. * Wait for discard to complete. */ - COMMAND(hw, RxDiscard, 0); - while(ins(hw->addr+Status) & CmdInProgress) + COMMAND(board, RxDiscard, 0); + while(ins(board->io+Status) & CmdInProgress) ; } } static void -transmit(EtherCtlr *cp) +transmit(Ctlr *ctlr) { - EtherHw *hw = cp->hw; - EtherBuf *tb; + Board *board = ctlr->board; + RingBuf *tb; int s; ushort len; s = splhi(); - for(tb = &cp->tb[cp->ti]; tb->owner == Interface; tb = &cp->tb[cp->ti]){ - + for(tb = &ctlr->tb[ctlr->ti]; tb->owner == Interface; tb = &ctlr->tb[ctlr->ti]){ /* * If there's no room in the FIFO for this packet, * set up an interrupt for when space becomes available. */ - if(tb->len > ins(hw->addr+TxFreeBytes)){ - COMMAND(hw, SetTxAvailable, tb->len); + if(tb->len > ins(board->io+TxFreeBytes)){ + COMMAND(board, SetTxAvailable, tb->len); break; } @@ -377,27 +353,27 @@ transmit(EtherCtlr *cp) * Output packet must be a multiple of 4 in length. */ len = ROUNDUP(tb->len, 4)/2; - outs(hw->addr+Fifo, tb->len); - outs(hw->addr+Fifo, 0); - outss(hw->addr+Fifo, tb->pkt, len); + outs(board->io+Fifo, tb->len); + outs(board->io+Fifo, 0); + outss(board->io+Fifo, tb->pkt, len); tb->owner = Host; - cp->ti = NEXT(cp->ti, cp->ntb); + ctlr->ti = NEXT(ctlr->ti, ctlr->ntb); } splx(s); } static void -interrupt(EtherCtlr *cp) +interrupt(Ctlr *ctlr) { - EtherHw *hw = cp->hw; + Board *board = ctlr->board; ushort status; uchar txstatus, x; - status = ins(hw->addr+Status); + status = ins(board->io+Status); if(status & RxComplete){ - (*cp->hw->receive)(cp); - wakeup(&cp->rr); + receive(ctlr); + wakeup(&ctlr->rr); status &= ~RxComplete; } @@ -410,14 +386,15 @@ interrupt(EtherCtlr *cp) */ txstatus = 0; do{ - if(x = inb(hw->addr+TxStatus)) - outb(hw->addr+TxStatus, 0); + if(x = inb(board->io+TxStatus)) + outb(board->io+TxStatus, 0); txstatus |= x; - }while(ins(hw->addr+Status) & TxComplete); + }while(ins(board->io+Status) & TxComplete); + if(txstatus & (TxJabber|TxUnderrun)) - COMMAND(hw, TxReset, 0); - COMMAND(hw, TxEnable, 0); - cp->oerrs++; + COMMAND(board, TxReset, 0); + COMMAND(board, TxEnable, 0); + ctlr->oerrs++; } if(status & (TxAvailable|TxComplete)){ @@ -425,33 +402,30 @@ interrupt(EtherCtlr *cp) * Reset the Tx FIFO threshold. */ if(status & TxAvailable) - COMMAND(hw, AckIntr, TxAvailable); - (*cp->hw->transmit)(cp); - wakeup(&cp->tr); + COMMAND(board, AckIntr, TxAvailable); + transmit(ctlr); + wakeup(&ctlr->tr); status &= ~(TxAvailable|TxComplete); } /* * Panic if there are any interrupt bits on we haven't * dealt with other than Latch. + * Otherwise, acknowledge the interrupt. */ if(status & AllIntr) - panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(hw)); + panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(board)); - /* - * Acknowledge the interrupt. - */ - COMMAND(hw, AckIntr, Latch); + COMMAND(board, AckIntr, Latch); } -EtherHw ether509 = { +Board ether509 = { reset, - 0, /* init */ + 0, /* init */ + attach, mode, - online, - receive, + 0, /* receive */ transmit, interrupt, - 0, /* tweak */ - 0, /* I/O base address */ + 0, /* watch */ }; diff --git a/pc/ether8003.c b/pc/ether8003.c new file mode 100644 index 0000000000000000000000000000000000000000..65f2d722255d0b932c95167059a7347b4eb0eb04 --- /dev/null +++ b/pc/ether8003.c @@ -0,0 +1,204 @@ +#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" + +enum { /* 83C584 Bus Interface Controller */ + Msr = 0x00, /* Memory Select Register */ + Icr = 0x01, /* Interface Configuration Register */ + Iar = 0x02, /* I/O Address Register */ + Bio = 0x03, /* BIOS ROM Address Register */ + Irr = 0x04, /* Interrupt Request Register */ + Laar = 0x05, /* LA Address Register */ + Ijr = 0x06, /* Initialisation Jumpers */ + Gp2 = 0x07, /* General Purpose Data Register */ + Lar = 0x08, /* LAN Address Registers */ + Id = 0x0E, /* Board ID byte */ + Cksum = 0x0F, /* Checksum */ +}; + +enum { /* Msr */ + Rst = 0x80, /* software reset */ + Menb = 0x40, /* memory enable */ +}; + +enum { /* Laar */ + ZeroWS16 = (1<<5), /* zero wait states for 16-bit ops */ + L16en = (1<<6), /* enable 16-bit LAN operation */ + M16en = (1<<7), /* enable 16-bit memory access */ +}; + +/* + * Mapping from configuration bits to interrupt level. + */ +static int intrmap[] = { + 9, 3, 5, 7, 10, 11, 15, 4, +}; + +/* + * Get configuration parameters, enable memory. + */ +static int +reset(Ctlr *ctlr) +{ + Board *board = ctlr->board; + int i; + uchar msr, icr, laar, irr, sum; + + /* + * Look for the interface. We read the LAN address ROM + * and validate the checksum - the sum of all 8 bytes + * should be 0xFF. + */ + for(board->io = 0x200; board->io < 0x400; board->io += 0x20){ + sum = 0; + for(i = 0; i < sizeof(ctlr->ea); i++){ + ctlr->ea[i] = inb(board->io+Lar+i); + sum += ctlr->ea[i]; + } + sum += inb(board->io+Id); + sum += inb(board->io+Cksum); + if(sum == 0xFF) + break; + } + if(board->io >= 0x400) + return -1; + + /* + * Found it, reset it. + * Be careful to preserve the Msr address bits, + * they don't get reloaded from the EEPROM on reset. + */ + msr = inb(board->io+Msr); + outb(board->io+Msr, Rst|msr); + delay(1); + outb(board->io+Msr, msr); + delay(2); + + /* + * Check for old, dumb 8003E, which doesn't have an interface + * chip. Only the msr exists out of the 1st eight registers, reads + * of the others just alias the 2nd eight registers, the LAN + * address ROM. We can check icr, irr and laar against the ethernet + * address read above and if they match it's an 8003E (or an + * 8003EBT, 8003S, 8003SH or 8003WT, we don't care), in which + * case the default irq gets used. + */ + msr = inb(board->io+Msr); + icr = inb(board->io+Icr); + laar = inb(board->io+Laar); + irr = inb(board->io+Irr); + if(icr != ctlr->ea[1] || irr != ctlr->ea[4] || laar != ctlr->ea[5]) + board->irq = intrmap[((irr>>5) & 0x3)|(icr & 0x4)]; + else { + msr = (((ulong)board->ramstart)>>13) & 0x3F; + icr = laar = 0; + board->watch = 0; + } + + /* + * Set up the bus-size, RAM address and RAM size + * from the info in the configuration registers. + */ + board->bit16 = icr & 0x1; + + board->ram = 1; + board->ramstart = KZERO|((msr & 0x3F)<<13); + if(board->bit16) + board->ramstart |= (laar & 0x1F)<<19; + else + board->ramstart |= 0x80000; + + if(icr & (1<<3)) + board->ramstop = 32*1024; + else + board->ramstop = 8*1024; + if(board->bit16) + board->ramstop <<= 1; + board->ramstop += board->ramstart; + + /* + * Set the DP8390 ring addresses. + */ + board->dp8390 = board->io+0x10; + board->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz); + board->pstop = HOWMANY(board->ramstop-board->ramstart, Dp8390BufSz); + board->tstart = 0; + + print("WD80x3 I/O addr %lux width %d addr %lux size %d irq %d:", + board->io, board->bit16 ? 16: 8, board->ramstart, + board->ramstop-board->ramstart, board->irq); + for(i = 0; i < sizeof(ctlr->ea); i++) + print(" %2.2ux", ctlr->ea[i]); + print("\n"); + + /* + * Enable interface RAM, set interface width. + */ + outb(board->io+Msr, Menb|msr); + if(board->bit16) + outb(board->io+Laar, laar|L16en|M16en|ZeroWS16); + + /* + * Finally, init the 8390 and set the + * ethernet address. + */ + dp8390reset(ctlr); + dp8390setea(ctlr); + + return 0; +} + +static void +watch(Ctlr *ctlr) +{ + Board *board = ctlr->board; + uchar msr; + int s; + + s = splhi(); + msr = inb(board->io+Msr); + /* + * If the board has reset itself, + * start again. + */ + if((msr & Menb) == 0){ + delay(100); + + dp8390reset(ctlr); + etherinit(); + + wakeup(&ctlr->tr); + wakeup(&ctlr->rr); + } + splx(s); +} + +/* + * Defaults are set for the dumb 8003E + * which can't be autoconfigured. + */ +Board ether8003 = { + reset, + 0, /* init */ + dp8390attach, + dp8390mode, + dp8390receive, + dp8390transmit, + dp8390intr, + watch, + + 0x280, /* addr */ + 3, /* irq */ + 0, /* bit16 */ + + 1, /* ram */ + 0xD0000, /* ramstart */ + 0xD0000+8*1024, /* ramstop */ +}; diff --git a/pc/ether8390.c b/pc/ether8390.c new file mode 100644 index 0000000000000000000000000000000000000000..065edea0166be23e4c146e93b2d03118d05f0b64 --- /dev/null +++ b/pc/ether8390.c @@ -0,0 +1,561 @@ +/* + * National Semiconductor DP8390 + * and SMC 83C90 + * Network Interface Controller. + */ +#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" + +enum { + Cr = 0x00, /* command register, all pages */ + + Stp = 0x01, /* stop */ + Sta = 0x02, /* start */ + Txp = 0x04, /* transmit packet */ + RDMAread = (1<<3), /* remote DMA read */ + RDMAwrite = (2<<3), /* remote DMA write */ + RDMAsend = (3<<3), /* remote DMA send packet */ + RDMAabort = (4<<3), /* abort/complete remote DMA */ + Page0 = (0x00<<6), /* page select */ + Page1 = (0x01<<6), + Page2 = (0x02<<6), +}; + +enum { /* Page 0, read */ + Clda0 = 0x01, /* current local DMA address 0 */ + Clda1 = 0x02, /* current local DMA address 1 */ + Bnry = 0x03, /* boundary pointer (R/W) */ + Tsr = 0x04, /* transmit status register */ + Ncr = 0x05, /* number of collisions register */ + Fifo = 0x06, /* FIFO */ + Isr = 0x07, /* interrupt status register (R/W) */ + Crda0 = 0x08, /* current remote DMA address 0 */ + Crda1 = 0x08, /* current remote DMA address 1 */ + Rsr = 0x0C, /* receive status register */ + Cntr0 = 0x0D, /* frame alignment errors */ + Cntr1 = 0x0E, /* CRC errors */ + Cntr2 = 0x0F, /* missed packet errors */ +}; + +enum { /* Page 0, write */ + Pstart = 0x01, /* page start register */ + Pstop = 0x02, /* page stop register */ + Tpsr = 0x04, /* transmit page start address */ + Tbcr0 = 0x05, /* transmit byte count register 0 */ + Tbcr1 = 0x06, /* transmit byte count register 1 */ + Rsar0 = 0x08, /* remote start address register 0 */ + Rsar1 = 0x09, /* remote start address register 1 */ + Rbcr0 = 0x0A, /* remote byte count register 0 */ + Rbcr1 = 0x0B, /* remote byte count register 1 */ + Rcr = 0x0C, /* receive configuration register */ + Tcr = 0x0D, /* transmit configuration register */ + Dcr = 0x0E, /* data configuration register */ + Imr = 0x0F, /* interrupt mask */ +}; + +enum { /* Page 1, read/write */ + Par0 = 0x01, /* physical address register 0 */ + Curr = 0x07, /* current page register */ + Mar0 = 0x08, /* multicast address register 0 */ +}; + +enum { /* Interrupt Status Register */ + Prx = 0x01, /* packet received */ + Ptx = 0x02, /* packet transmitted */ + Rxe = 0x04, /* receive error */ + Txe = 0x08, /* transmit error */ + Ovw = 0x10, /* overwrite warning */ + Cnt = 0x20, /* counter overflow */ + Rdc = 0x40, /* remote DMA complete */ + Rst = 0x80, /* reset status */ +}; + +enum { /* Interrupt Mask Register */ + Prxe = 0x01, /* packet received interrupt enable */ + Ptxe = 0x02, /* packet transmitted interrupt enable */ + Rxee = 0x04, /* receive error interrupt enable */ + Txee = 0x08, /* transmit error interrupt enable */ + Ovwe = 0x10, /* overwrite warning interrupt enable */ + Cnte = 0x20, /* counter overflow interrupt enable */ + Rdce = 0x40, /* DMA complete interrupt enable */ +}; + +enum { /* Data Configuration register */ + Wts = 0x01, /* word transfer select */ + Bos = 0x02, /* byte order select */ + Las = 0x04, /* long address select */ + Ls = 0x08, /* loopback select */ + Arm = 0x10, /* auto-initialise remote */ + Ft1 = (0x00<<5), /* FIFO threshhold select 1 byte/word */ + Ft2 = (0x01<<5), /* FIFO threshhold select 2 bytes/words */ + Ft4 = (0x02<<5), /* FIFO threshhold select 4 bytes/words */ + Ft6 = (0x03<<5), /* FIFO threshhold select 6 bytes/words */ +}; + +enum { /* Transmit Configuration Register */ + Crc = 0x01, /* inhibit CRC */ + Lb = 0x02, /* internal loopback */ + Atd = 0x08, /* auto transmit disable */ + Ofst = 0x10, /* collision offset enable */ +}; + +enum { /* Transmit Status Register */ + Ptxok = 0x01, /* packet transmitted */ + Col = 0x04, /* transmit collided */ + Abt = 0x08, /* tranmit aborted */ + Crs = 0x10, /* carrier sense lost */ + Fu = 0x20, /* FIFO underrun */ + Cdh = 0x40, /* CD heartbeat */ + Owc = 0x80, /* out of window collision */ +}; + +enum { /* Receive Configuration Register */ + Sep = 0x01, /* save errored packets */ + Ar = 0x02, /* accept runt packets */ + Ab = 0x04, /* accept broadcast */ + Am = 0x08, /* accept multicast */ + Pro = 0x10, /* promiscuous physical */ + Mon = 0x20, /* monitor mode */ +}; + +enum { /* Receive Status Register */ + Prxok = 0x01, /* packet received intact */ + Crce = 0x02, /* CRC error */ + Fae = 0x04, /* frame alignment error */ + Fo = 0x08, /* FIFO overrun */ + Mpa = 0x10, /* missed packet */ + Phy = 0x20, /* physical/multicast address */ + Dis = 0x40, /* receiver disabled */ + Dfr = 0x80, /* deferring */ +}; + +typedef struct { + uchar status; + uchar next; + uchar len0; + uchar len1; +} Hdr; + +typedef struct { + Hdr; + uchar data[Dp8390BufSz-sizeof(Hdr)]; +} Buf; + +static void +dp8390disable(Ctlr *ctlr) +{ + Board *board = ctlr->board; + int timo; + + /* + * Stop the chip. Set the Stp bit and wait for the chip + * to finish whatever was on its tiny mind before it sets + * the Rst bit. + * We need the timeout because there may not be a real + * chip there if this is called when probing for a device + * at boot. + */ + outb(board->dp8390+Cr, Page0|RDMAabort|Stp); + for(timo = 10000; (inb(board->dp8390+Isr) & Rst) == 0 && timo; timo--) + ; +} + +void +dp8390reset(Ctlr *ctlr) +{ + Board *board = ctlr->board; + + /* + * This is the initialisation procedure described + * as 'mandatory' in the datasheet. + */ + dp8390disable(ctlr); + if(board->bit16) + outb(board->dp8390+Dcr, Ft4|Ls|Wts); + else + outb(board->dp8390+Dcr, Ft4|Ls); + + outb(board->dp8390+Rbcr0, 0); + outb(board->dp8390+Rbcr1, 0); + + outb(board->dp8390+Rcr, Ab); + outb(board->dp8390+Tcr, Lb); + + outb(board->dp8390+Bnry, board->pstart); + outb(board->dp8390+Pstart, board->pstart); + outb(board->dp8390+Pstop, board->pstop); + + outb(board->dp8390+Isr, 0xFF); + outb(board->dp8390+Imr, Ovwe|Txee|Rxee|Ptxe|Prxe); + + outb(board->dp8390+Cr, Page1|RDMAabort|Stp); + + /* + * Can't initialise ethernet address as we may + * not know it yet. + */ + outb(board->dp8390+Curr, board->pstart+1); + + /* + * Leave the chip initialised, + * but in internal loopback mode. + */ + outb(board->dp8390+Cr, Page0|RDMAabort|Sta); + + outb(board->dp8390+Tpsr, board->tstart); +} + +void +dp8390attach(Ctlr *ctlr) +{ + /* + * Enable the chip for transmit/receive. + * The init routine leaves the chip in internal + * loopback. + */ + outb(ctlr->board->dp8390+Tcr, 0); +} + +void +dp8390mode(Ctlr *ctlr, int on) +{ + if(on) + outb(ctlr->board->dp8390+Rcr, Pro|Ab); + else + outb(ctlr->board->dp8390+Rcr, Ab); +} + +void +dp8390setea(Ctlr *ctlr) +{ + Board *board = ctlr->board; + uchar cr; + int i; + + /* + * Set the ethernet address into the chip. + * Take care to restore the command register + * afterwards. We don't care about multicast + * addresses as we never set the multicast + * enable. + */ + cr = inb(board->dp8390+Cr); + outb(board->dp8390+Cr, Page1|(~Page0 & cr)); + for(i = 0; i < sizeof(ctlr->ea); i++) + outb(board->dp8390+Par0+i, ctlr->ea[i]); + outb(board->dp8390+Cr, cr); +} + +void* +dp8390read(Ctlr *ctlr, void *to, ulong from, ulong len) +{ + Board *board = ctlr->board; + uchar cr; + int timo; + + /* + * If the interface has shared memory, just + * do a memmove. + * In this case, 'from' is an index into the shared memory. + */ + if(board->ram){ + memmove(to, (void*)(board->ramstart+from), len); + return to; + } + + /* + * Read some data at offset 'from' in the board's memory + * using the DP8390 remote DMA facility, and place it at + * 'to' in main memory, via the I/O data port. + */ + cr = inb(board->dp8390+Cr); + outb(board->dp8390+Cr, Page0|RDMAabort|Sta); + outb(board->dp8390+Isr, Rdc); + + /* + * Set up the remote DMA address and count. + */ + if(board->bit16) + len = ROUNDUP(len, 2); + outb(board->dp8390+Rbcr0, len & 0xFF); + outb(board->dp8390+Rbcr1, (len>>8) & 0xFF); + outb(board->dp8390+Rsar0, from & 0xFF); + outb(board->dp8390+Rsar1, (from>>8) & 0xFF); + + /* + * Start the remote DMA read and suck the data + * out of the I/O port. + */ + outb(board->dp8390+Cr, Page0|RDMAread|Sta); + if(board->bit16) + inss(board->data, to, len/2); + else + insb(board->data, to, len); + + /* + * Wait for the remote DMA to complete. The timeout + * is necessary because we may call this routine on + * a non-existent chip during initialisation and, due + * to the miracles of the bus, we could get this far + * and still be talking to a slot full of nothing. + */ + for(timo = 10000; (inb(board->dp8390+Isr) & Rdc) == 0 && timo; timo--) + ; + + outb(board->dp8390+Isr, Rdc); + outb(board->dp8390+Cr, cr); + return to; +} + +void* +dp8390write(Ctlr *ctlr, ulong to, void *from, ulong len) +{ + Board *board = ctlr->board; + uchar cr; + + /* + * If the interface has shared memory, just + * do a memmove. + * In this case, 'to' is an index into the shared memory. + */ + if(board->ram){ + memmove((void*)(board->ramstart+to), from, len); + return (void*)to; + } + + /* + * Write some data to offset 'to' in the board's memory + * using the DP8390 remote DMA facility, reading it at + * 'from' in main memory, via the I/O data port. + */ + cr = inb(board->dp8390+Cr); + outb(board->dp8390+Cr, Page0|RDMAabort|Sta); + outb(board->dp8390+Isr, Rdc); + + /* + * Set up the remote DMA address and count. + * This is straight from the datasheet, hence + * the initial write to Rbcr0 and Cr. + */ + outb(board->dp8390+Rbcr0, 0xFF); + outb(board->dp8390+Cr, Page0|RDMAread|Sta); + + if(board->bit16) + len = ROUNDUP(len, 2); + outb(board->dp8390+Rbcr0, len & 0xFF); + outb(board->dp8390+Rbcr1, (len>>8) & 0xFF); + outb(board->dp8390+Rsar0, to & 0xFF); + outb(board->dp8390+Rsar1, (to>>8) & 0xFF); + + /* + * Start the remote DMA write and pump the data + * into the I/O port. + */ + outb(board->dp8390+Cr, Page0|RDMAwrite|Sta); + if(board->bit16) + outss(board->data, from, len/2); + else + outsb(board->data, from, len); + + /* + * Wait for the remote DMA to finish. We'll need + * a timeout here if this ever gets called before + * we know there really is a chip there. + */ + while((inb(board->dp8390+Isr) & Rdc) == 0) + ; + + outb(board->dp8390+Isr, Rdc); + outb(board->dp8390+Cr, cr); + return (void*)to; +} + +void +dp8390receive(Ctlr *ctlr) +{ + Board *board = ctlr->board; + RingBuf *ring; + uchar bnry, curr, next; + Hdr hdr; + ulong data; + int i, len; + + bnry = inb(board->dp8390+Bnry); + next = bnry+1; + if(next >= board->pstop) + next = board->pstart; + + for(i = 0; ; i++){ + outb(board->dp8390+Cr, Page1|RDMAabort|Sta); + curr = inb(board->dp8390+Curr); + outb(board->dp8390+Cr, Page0|RDMAabort|Sta); + if(next == curr) + break; + + /* + * Hack to keep away from the card's memory while it is receiving + * a packet. This is only a problem on the NCR 3170 Safari. + * + * We peek at the current local DMA registers and, if they are + * changing, wait. + */ + if(strcmp(arch->id, "NCRD.0") == 0){ + uchar a, b, c; + + for(;;delay(10)){ + a = inb(board->dp8390+Clda0); + b = inb(board->dp8390+Clda0); + if(a != b) + continue; + c = inb(board->dp8390+Clda0); + if(c != b) + continue; + break; + } + } + + ctlr->inpackets++; + + data = next*Dp8390BufSz; + dp8390read(ctlr, &hdr, data, sizeof(Hdr)); + len = ((hdr.len1<<8)|hdr.len0)-4; + + /* + * Chip is badly scrogged, reinitialise it. + * dp8390reset() calls the disable function. + * There's no need to reload the ethernet + * address. + * + * Untested. + */ + if(hdr.next < board->pstart || hdr.next >= board->pstop || len < 60){ +print("scrogged\n"); + dp8390reset(ctlr); + dp8390attach(ctlr); + return; + } + + ring = &ctlr->rb[ctlr->ri]; + if(ring->owner == Interface){ + ring->len = len; + data += sizeof(Hdr); + if((data+len) >= board->pstop*Dp8390BufSz){ + len = board->pstop*Dp8390BufSz - data; + dp8390read(ctlr, ring->pkt+len, board->pstart*Dp8390BufSz, + (data+ring->len) - board->pstop*Dp8390BufSz); + } + dp8390read(ctlr, ring->pkt, data, len); + ring->owner = Host; + ctlr->ri = NEXT(ctlr->ri, ctlr->nrb); + } + + next = hdr.next; + bnry = next-1; + if(bnry < board->pstart) + bnry = board->pstop-1; + outb(board->dp8390+Bnry, bnry); + } +} + +void +dp8390transmit(Ctlr *ctlr) +{ + Board *board; + RingBuf *ring; + int s; + + s = splhi(); + board = ctlr->board; + ring = &ctlr->tb[ctlr->ti]; + if(ring->busy == 0 && ring->owner == Interface){ + dp8390write(ctlr, board->tstart*Dp8390BufSz, ring->pkt, ring->len); + outb(board->dp8390+Tbcr0, ring->len & 0xFF); + outb(board->dp8390+Tbcr1, (ring->len>>8) & 0xFF); + outb(board->dp8390+Cr, Page0|RDMAabort|Txp|Sta); + ring->busy = 1; + } + splx(s); +} + +void +dp8390intr(Ctlr *ctlr) +{ + Board *board = ctlr->board; + RingBuf *ring; + uchar isr; + + /* + * While there is something of interest, + * clear all the interrupts and process. + */ + while(isr = inb(board->dp8390+Isr)){ + outb(board->dp8390+Isr, isr); + + if(isr & Ovw){ + /* + * Need to do some work here: + * stop the chip; + * clear out the ring; + * re-enable the chip. + * The procedure to put the chip back on + * an active network is to first put it + * into internal loopback mode, enable it, + * then take it out of loopback. + * + * Untested. + */ + ctlr->overflows++; + + dp8390disable(ctlr); + (*ctlr->board->receive)(ctlr); + outb(board->dp8390+Tcr, Lb); + outb(board->dp8390+Cr, Page0|RDMAabort|Sta); + dp8390attach(ctlr); + } + + /* + * We have received packets. + * Take a spin round the ring and + * wakeup the kproc. + */ + if(isr & (Rxe|Prx)){ + (*ctlr->board->receive)(ctlr); + wakeup(&ctlr->rr); + } + + /* + * Log errors and successful transmissions. + */ + if(isr & Txe) + ctlr->oerrs++; + if(isr & Rxe){ + ctlr->frames += inb(board->dp8390+Cntr0); + ctlr->crcs += inb(board->dp8390+Cntr1); + ctlr->buffs += inb(board->dp8390+Cntr2); + } + if(isr & Ptx) + ctlr->outpackets++; + + /* + * A packet completed transmission, successfully or + * not. Start transmission on the next buffered packet, + * and wake the output routine. + */ + if(isr & (Txe|Ptx)){ + ring = &ctlr->tb[ctlr->ti]; + ring->owner = Host; + ring->busy = 0; + ctlr->ti = NEXT(ctlr->ti, ctlr->ntb); + (*ctlr->board->transmit)(ctlr); + wakeup(&ctlr->tr); + } + } +} diff --git a/pc/io.h b/pc/io.h index 426e0b2e3fc0a0d9ed837a4ffd96908d1f992270..132dd0ddecc1f5c3da321f033c916dde645baf95 100644 --- a/pc/io.h +++ b/pc/io.h @@ -25,97 +25,3 @@ enum Syscallvec= 64, }; - -typedef struct EtherHw EtherHw; -typedef struct EtherBuf EtherBuf; -typedef struct EtherType EtherType; -typedef struct EtherCtlr EtherCtlr; - -struct EtherHw { - int (*reset)(EtherCtlr*); - void (*init)(EtherCtlr*); - void (*mode)(EtherCtlr*, int); - void (*online)(EtherCtlr*, int); - void (*receive)(EtherCtlr*); - void (*transmit)(EtherCtlr*); - void (*intr)(EtherCtlr*); - void (*tweak)(EtherCtlr*); - int addr; /* interface address */ - uchar *ram; /* interface shared memory address */ - int bt16; /* true if a 16 bit interface */ - int irq; /* interrupt level */ - int size; - uchar tstart; - uchar pstart; - uchar pstop; -}; - -struct EtherBuf { - uchar owner; - uchar busy; - ushort len; - uchar pkt[sizeof(Etherpkt)]; -}; - -enum { - Host = 0, /* buffer owned by host */ - Interface = 1, /* buffer owned by interface */ - - NType = 9, /* types/interface */ -}; - -/* - * one per ethernet packet type - */ -struct EtherType { - QLock; - Netprot; /* stat info */ - int type; /* ethernet type */ - int prom; /* promiscuous mode */ - Queue *q; - int inuse; - EtherCtlr *ctlr; -}; - -/* - * per ethernet - */ -struct EtherCtlr { - QLock; - - EtherHw *hw; - int present; - - ushort nrb; /* number of software receive buffers */ - ushort ntb; /* number of software transmit buffers */ - EtherBuf *rb; /* software receive buffers */ - EtherBuf *tb; /* software transmit buffers */ - - uchar ea[6]; /* ethernet address */ - uchar ba[6]; /* broadcast address */ - - Rendez rr; /* rendezvous for a receive buffer */ - ushort rh; /* first receive buffer belonging to host */ - ushort ri; /* first receive buffer belonging to interface */ - - Rendez tr; /* rendezvous for a transmit buffer */ - QLock tlock; /* semaphore on th */ - ushort th; /* first transmit buffer belonging to host */ - ushort ti; /* first transmit buffer belonging to interface */ - - EtherType type[NType]; - uchar prom; /* true if promiscuous mode */ - uchar kproc; /* true if kproc started */ - char name[NAMELEN]; /* name of kproc */ - Network net; - - Queue lbq; /* software loopback packet queue */ - - 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 */ -};