M pc/devether.c => pc/devether.c +273 -204
@@ 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);
}
A pc/ether.h => pc/ether.h +142 -0
@@ 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 */
+};
A pc/ether2000.c => pc/ether2000.c +108 -0
@@ 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 */
+};
A pc/ether503.c => pc/ether503.c +43 -0
@@ 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 */
+};
M pc/ether509.c => pc/ether509.c +95 -121
@@ 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 */
};
A pc/ether8003.c => pc/ether8003.c +204 -0
@@ 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 */
+};
A pc/ether8390.c => pc/ether8390.c +561 -0
@@ 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);
+ }
+ }
+}
M pc/io.h => pc/io.h +0 -94
@@ 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 */
-};