From e39f40f3792e2f5ca04f30da3315a9274ca41cf2 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 21 Jul 1995 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1995-07-21 --- pc/devether.c | 15 +- pc/ether2000.c | 6 +- pc/ether4100.c | 4 +- pc/ether509.c | 12 +- pc/ether79c970.c | 491 +++++++++++++++++++++++++++++++++++++++++++++++ pc/ether8003.c | 4 +- pc/ether8390.c | 20 +- pc/etherif.h | 5 +- pc/fns.h | 5 +- pc/pci.c | 53 +++-- 10 files changed, 566 insertions(+), 49 deletions(-) create mode 100644 pc/ether79c970.c diff --git a/pc/devether.c b/pc/devether.c index a6ebe18c391e9cb9c1f8b7e9dd4c28a56e94decd..d265197ddd6b33fb512a5d483c47bb1e6fec4f75 100644 --- a/pc/devether.c +++ b/pc/devether.c @@ -101,7 +101,7 @@ etherwstat(Chan *c, char *dp) } void -etherrloop(Ether *ctlr, Etherpkt *pkt, long len, int tome) +etherrloop(Ether *ctlr, Etherpkt *pkt, long len) { Block *bp; ushort type; @@ -111,7 +111,7 @@ etherrloop(Ether *ctlr, Etherpkt *pkt, long len, int tome) type = (pkt->type[0]<<8)|pkt->type[1]; ep = &ctlr->f[Ntypes]; for(fp = ctlr->f; fp < ep; fp++){ - if((f = *fp) && ((tome && f->type==type) || f->type < 0)){ + if((f = *fp) && (f->type == type || f->type < 0)){ if(f->type > -2){ if(qproduce(f->in, pkt->d, len) < 0) ctlr->soverflows++; @@ -139,18 +139,17 @@ etherrloop(Ether *ctlr, Etherpkt *pkt, long len, int tome) static int etherwloop(Ether *ctlr, Etherpkt *pkt, long len) { - int s, tome, bcast; + int s, different; - tome = memcmp(pkt->d, ctlr->ea, sizeof(pkt->d)) == 0; - bcast = memcmp(pkt->d, ctlr->bcast, sizeof(pkt->d)) == 0; - if(!tome && !bcast && ctlr->prom==0 && ctlr->all==0) + different = memcmp(pkt->d, ctlr->ea, sizeof(pkt->d)); + if(different && memcmp(pkt->d, ctlr->bcast, sizeof(pkt->d))) return 0; s = splhi(); - etherrloop(ctlr, pkt, len, tome||bcast); + etherrloop(ctlr, pkt, len); splx(s); - return tome; + return different == 0; } long diff --git a/pc/ether2000.c b/pc/ether2000.c index 8c35463c53fc8413c73c0e8f8223a39217448ddb..51f4b7bbf7154b4e3fa28534a472ce7538ffc852 100644 --- a/pc/ether2000.c +++ b/pc/ether2000.c @@ -48,8 +48,8 @@ reset(Ether *ether) ether->size = 16*1024; port = ether->port; - ether->private = malloc(sizeof(Dp8390)); - dp8390 = ether->private; + ether->ctlr = malloc(sizeof(Dp8390)); + dp8390 = ether->ctlr; dp8390->bit16 = 1; dp8390->ram = 0; @@ -81,7 +81,7 @@ reset(Ether *ether) memset(buf, 0, sizeof(buf)); dp8390read(dp8390, buf, 0, sizeof(buf)); if((buf[0x0E] & 0xFF) != 0x57 || (buf[0x0F] & 0xFF) != 0x57){ - free(ether->private); + free(ether->ctlr); return -1; } diff --git a/pc/ether4100.c b/pc/ether4100.c index b7933c464d48de6d1a3afe18a0075b517284eab6..9583cff06c9f20b85bf5de5b50dc4bd6c7fc5c98 100644 --- a/pc/ether4100.c +++ b/pc/ether4100.c @@ -52,8 +52,8 @@ reset(Ether *ether) ether->size = 16*1024; port = ether->port; - ether->private = malloc(sizeof(Dp8390)); - dp8390 = ether->private; + ether->ctlr = malloc(sizeof(Dp8390)); + dp8390 = ether->ctlr; dp8390->bit16 = 1; dp8390->ram = 0; diff --git a/pc/ether509.c b/pc/ether509.c index 34c66821c19cffb68d8e1c0fe102ecdd787e58a6..16c4a8417c46ba610301dea1d7b94c6a85c07329 100644 --- a/pc/ether509.c +++ b/pc/ether509.c @@ -190,7 +190,7 @@ receive(Ether *ether) /* * Copy the packet to whoever wants it. */ - etherrloop(ether, ðer->rpkt, len, 1); + etherrloop(ether, ðer->rpkt, len); } /* @@ -571,16 +571,16 @@ static PCIcfg* tcm590(Ether *ether) { PCIcfg* pcicfg; - static uchar devno = 0; - ulong port; + static int devno = 0; + int port; Adapter *ap; pcicfg = malloc(sizeof(PCIcfg)); - while(devno < 16){ + for(;;){ pcicfg->vid = 0x10B7; pcicfg->did = 0; - if(pcimatch(0, devno++, pcicfg) == 0) - continue; + if((devno = pcimatch(0, devno, pcicfg)) == -1) + break; port = pcicfg->baseaddr[0] & ~0x01; COMMAND(port, GlobalReset, 0); diff --git a/pc/ether79c970.c b/pc/ether79c970.c new file mode 100644 index 0000000000000000000000000000000000000000..29253797030c5e743ce8f7bfdde52b923bfc380d --- /dev/null +++ b/pc/ether79c970.c @@ -0,0 +1,491 @@ +/* + * AM79C970 + * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus + * To do: + * only issue transmit interrupt if necessary? + * dynamically increase rings as necessary? + * use Block's as receive buffers? + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" + +enum { + Lognrdre = 6, + Nrdre = (1<port; + outl(port+Rdp, Iena|Strt); +} + +static void +ringinit(Ctlr* ctlr) +{ + int i, x; + + /* + * Initialise the receive and transmit buffer rings. The ring + * entries must be aligned on 16-byte boundaries. + */ + if(ctlr->rdr == 0) + ctlr->rdr = xspanalloc(Nrdre*sizeof(Rdre), 0x10, 0); + if(ctlr->rrb == 0) + ctlr->rrb = xalloc(Nrdre*Rbsize); + x = PADDR(ctlr->rrb); + for(i = 0; i < Nrdre; i++){ + ctlr->rdr[i].rbadr = x; + x += Rbsize; + ctlr->rdr[i].rmd1 = Own|(-Rbsize & 0xFFFF); + } + ctlr->rdrx = 0; + + if(ctlr->tdr == 0) + ctlr->tdr = xspanalloc(Ntdre*sizeof(Tdre), 0x10, 0); + if(ctlr->trb == 0) + ctlr->trb = xalloc(Ntdre*Rbsize); + x = PADDR(ctlr->trb); + for(i = 0; i < Ntdre; i++){ + ctlr->tdr[i].tbadr = x; + x += Rbsize; + } + ctlr->tdrx = 0; +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + int port, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + /* + * Put the chip into promiscuous mode. First we must wait until + * anyone transmitting is done, then we can stop the chip and put + * it in promiscuous mode. Restarting is made harder by the chip + * reloading the transmit and receive descriptor pointers with their + * base addresses when Strt is set (unlike the older Lance chip), + * so the rings must be re-initialised. + */ + qlock(ðer->tlock); + + ilock(&ctlr->raplock); + outl(port+Rdp, Stop); + + outl(port+Rap, 15); + x = inl(port+Rdp) & ~Prom; + if(on) + x |= Prom; + outl(port+Rdp, x); + outl(port+Rap, 0); + + ringinit(ctlr); + outl(port+Rdp, Iena|Strt); + iunlock(&ctlr->raplock); + + qunlock(ðer->tlock); +} + +static int +owntdre(void* arg) +{ + return (((Tdre*)arg)->tmd1 & Own) == 0; +} + +static long +write(Ether* ether, void* buf, long n) +{ + int port; + Ctlr *ctlr; + Tdre *tdre; + Etherpkt *pkt; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * Wait for a transmit ring descriptor (and hence a buffer) to become + * free. If none become free after a reasonable period, give up. + */ + tdre = &ctlr->tdr[ctlr->tdrx]; + tsleep(&ctlr->trendez, owntdre, tdre, 100); + if(owntdre(tdre) == 0) + return 0; + + /* + * Copy the packet to the transmit buffer and fill in our + * source ethernet address. There's no need to pad to ETHERMINTU + * here as we set ApadXmit in CSR4. + */ + pkt = KADDR(tdre->tbadr); + memmove(pkt->d, buf, n); + memmove(pkt->s, ether->ea, sizeof(pkt->s)); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + tdre->tmd2 = 0; + tdre->tmd1 = Own|Stp|Enp|(-n & 0xFFFF); + ctlr->tdrx = NEXT(ctlr->tdrx, Ntdre); + outl(port+Rdp, Iena|Tdmd); + + ether->outpackets++; + return n; +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, csr0, status; + Ctlr *ctlr; + Rdre *rdre; + Etherpkt *pkt; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + csr0 = inl(port+Rdp); + outl(port+Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena); + if(csr0 & (Babl|Miss|Merr)) + print("AMD70C970#%d: csr0 = 0x%uX\n", ether->ctlrno, csr0); + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(csr0 & Rint){ + rdre = &ctlr->rdr[ctlr->rdrx]; + while(((status = rdre->rmd1) & Own) == 0){ + if(status & RxErr){ + if(status & RxBuff) + ether->buffs++; + if(status & RxCrc) + ether->crcs++; + if(status & RxOflo) + ether->overflows++; + } + else{ + ether->inpackets++; + pkt = KADDR(rdre->rbadr); + etherrloop(ether, pkt, (rdre->rmd2 & 0x0FFF)-4); + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + rdre->rmd2 = 0; + rdre->rmd1 = Own|(-Rbsize & 0xFFFF); + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + rdre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: wakeup anyone waiting for a free descriptor. + */ + if(csr0 & Tint) + wakeup(&ctlr->trendez); +} + +typedef struct Adapter Adapter; +struct Adapter { + Adapter* next; + int port; + PCIcfg* pcicfg; +}; +static Adapter *adapter; + +static PCIcfg* +amd79c90(Ether* ether) +{ + PCIcfg *pcicfg; + static int devno = 0; + int port; + Adapter *ap; + + pcicfg = malloc(sizeof(PCIcfg)); + for(;;){ + pcicfg->vid = 0x1022; + pcicfg->did = 0x2000; + if((devno = pcimatch(0, devno, pcicfg)) == -1) + break; + + port = pcicfg->baseaddr[0] & ~0x01; + if(ether->port == 0 || ether->port == port) + return pcicfg; + + ap = malloc(sizeof(Adapter)); + ap->pcicfg = pcicfg; + ap->port = port; + ap->next = adapter; + adapter = ap; + + pcicfg = malloc(sizeof(PCIcfg)); + } + free(pcicfg); + + return 0; +} + +static int +reset(Ether* ether) +{ + int port, x; + PCIcfg *pcicfg; + Adapter *ap, **app; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + /* + * Any adapter matches if no ether->port is supplied, otherwise the + * ports must match. First see if we've already found an adapter that fits + * the bill. If not then scan for another. + */ + port = 0; + pcicfg = 0; + for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){ + if(ether->port == 0 || ether->port == ap->port){ + port = ap->port; + pcicfg = ap->pcicfg; + *app = ap->next; + free(ap); + break; + } + } + if(port == 0 && (pcicfg = amd79c90(ether))){ + port = pcicfg->baseaddr[0] & ~0x01; + ether->irq = pcicfg->irq; + } + if(port == 0) + return -1; + + if(pcicfg) + free(pcicfg); + + /* + * How can we tell what mode we're in at this point - if we're in WORD + * mode then the only 32-bit access we are allowed to make is a write to + * the RDP, which forces the chip to DWORD mode; if we're in DWORD mode + * then we're not allowed to make any non-DWORD accesses? + * Assuming we do a DWORD write to the RDP, how can we tell what we're + * about to overwrite as we can't reliably access the RAP? + * + * Force DWORD mode by writing to RDP, doing a reset then writing to RDP + * again; at least we know what state we're in now. The value of RAP after + * a reset is 0, so the second DWORD write will be to CSR0. + * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access. + * Set the auto pad transmit in CSR4. + */ + outl(port+Rdp, 0x00); + inl(port+Sreset); + outl(port+Rdp, Stop); + + outl(port+Rap, 20); + outl(port+Bdp, 0x0002); + + outl(port+Rap, 4); + x = inl(port+Rdp) & 0xFFFF; + outl(port+Rdp, ApadXmt|x); + + outl(port+Rap, 0); + + /* + * Check if we are going to override the adapter's station address. + * If not, read it from the I/O-space and set in ether->ea prior to loading the + * station address in the initialisation block. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + x = inl(port+Aprom); + ether->ea[0] = x & 0xFF; + ether->ea[1] = (x>>8) & 0xFF; + ether->ea[2] = (x>>16) & 0xFF; + ether->ea[3] = (x>>24) & 0xFF; + x = inl(port+Aprom+4); + ether->ea[4] = x & 0xFF; + ether->ea[5] = (x>>8) & 0xFF; + } + + /* + * Allocate a controller structure and start to fill in the + * initialisation block (must be DWORD aligned). + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + + ctlr->iblock.rlen = Lognrdre<<4; + ctlr->iblock.tlen = Logntdre<<4; + memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr)); + + ringinit(ctlr); + ctlr->iblock.rdra = PADDR(ctlr->rdr); + ctlr->iblock.tdra = PADDR(ctlr->tdr); + + /* + * Point the chip at the initialisation block and tell it to go. + * Mask the Idon interrupt and poll for completion. Strt and interrupt + * enables will be set later when we're ready to attach to the network. + */ + x = PADDR(&ctlr->iblock); + outl(port+Rap, 1); + outl(port+Rdp, x & 0xFFFF); + outl(port+Rap, 2); + outl(port+Rdp, (x>>16) & 0xFFFF); + outl(port+Rap, 3); + outl(port+Rdp, Idonm); + outl(port+Rap, 0); + outl(port+Rdp, Init); + + while((inl(port+Rdp) & Idon) == 0) + ; + outl(port+Rdp, Idon|Stop); + + ether->port = port; + ether->attach = attach; + ether->write = write; + ether->interrupt = interrupt; + + ether->promiscuous = promiscuous; + ether->arg = ether; + + return 0; +} + +void +ether79c970link(void) +{ + addethercard("AMD79C970", reset); +} diff --git a/pc/ether8003.c b/pc/ether8003.c index fe21eac5b3a36d70aa8d9bbccb2c18f7ec9d145d..5349c1f3832a0c7c83b967e527f7488e757dd738 100644 --- a/pc/ether8003.c +++ b/pc/ether8003.c @@ -104,8 +104,8 @@ reset(Ether *ether) if(sum != 0xFF) return -1; - ether->private = malloc(sizeof(Dp8390)); - dp8390 = ether->private; + ether->ctlr = malloc(sizeof(Dp8390)); + dp8390 = ether->ctlr; dp8390->ram = 1; /* diff --git a/pc/ether8390.c b/pc/ether8390.c index 2c7bc7af1b3b36e6917da3ca4719b0c6277d648b..ec0b8e0893c6b384ad36c4cc4f71850ed44d8279 100644 --- a/pc/ether8390.c +++ b/pc/ether8390.c @@ -189,7 +189,7 @@ dp8390ring(Dp8390 *dp8390) void dp8390setea(Ether *ether) { - ulong port = ((Dp8390*)ether->private)->dp8390; + ulong port = ((Dp8390*)ether->ctlr)->dp8390; uchar cr; int i; @@ -210,7 +210,7 @@ dp8390setea(Ether *ether) void dp8390getea(Ether *ether) { - ulong port = ((Dp8390*)ether->private)->dp8390; + ulong port = ((Dp8390*)ether->ctlr)->dp8390; uchar cr; int i; @@ -384,7 +384,7 @@ receive(Ether *ether) Hdr hdr; ulong port, data, len, len1; - dp8390 = ether->private; + dp8390 = ether->ctlr; port = dp8390->dp8390; for(curr = getcurr(dp8390); dp8390->nxtpkt != curr; curr = getcurr(dp8390)){ ether->inpackets++; @@ -455,7 +455,7 @@ receive(Ether *ether) /* * Copy the packet to whoever wants it. */ - etherrloop(ether, ðer->rpkt, len, 1); + etherrloop(ether, ðer->rpkt, len); } /* @@ -484,7 +484,7 @@ write(Ether *ether, void *buf, long len) ulong port; Etherpkt *pkt; - dp8390 = ether->private; + dp8390 = ether->ctlr; port = dp8390->dp8390; tsleep(ðer->tr, istxavail, ether, 10000); @@ -534,7 +534,7 @@ overflow(Ether *ether) uchar txp; int resend; - dp8390 = ether->private; + dp8390 = ether->ctlr; port = dp8390->dp8390; /* @@ -572,7 +572,7 @@ interrupt(Ureg *ur, void *arg) USED(ur); ether = arg; - dp8390 = ether->private; + dp8390 = ether->ctlr; port = dp8390->dp8390; /* @@ -631,7 +631,7 @@ interrupt(Ureg *ur, void *arg) static void promiscuous(void *arg, int on) { - Dp8390 *dp8390 = ((Ether*)arg)->private; + Dp8390 *dp8390 = ((Ether*)arg)->ctlr; /* * Set/reset promiscuous mode. @@ -645,7 +645,7 @@ promiscuous(void *arg, int on) static void attach(Ether *ether) { - Dp8390 *dp8390 = ether->private; + Dp8390 *dp8390 = ether->ctlr; /* * Enable the chip for transmit/receive. @@ -663,7 +663,7 @@ dp8390reset(Ether *ether) Dp8390 *dp8390; ulong port; - dp8390 = ether->private; + dp8390 = ether->ctlr; port = dp8390->dp8390; /* diff --git a/pc/etherif.h b/pc/etherif.h index 27b18e3837450965bb8bb375c072e46075e7667d..8d49a36d53bbeee75a6c015ba68f25fb0b07bab7 100644 --- a/pc/etherif.h +++ b/pc/etherif.h @@ -6,11 +6,12 @@ enum { typedef struct Ether Ether; struct Ether { ISAConf; /* hardware info */ + int ctlrno; void (*attach)(Ether*); /* filled in by reset routine */ long (*write)(Ether*, void*, long); void (*interrupt)(Ureg*, void*); - void *private; + void *ctlr; Etherpkt tpkt; /* transmit buffer */ Etherpkt rpkt; /* receive buffer */ @@ -22,7 +23,7 @@ struct Ether { Netif; }; -extern void etherrloop(Ether*, Etherpkt*, long, int); +extern void etherrloop(Ether*, Etherpkt*, long); extern void addethercard(char*, int(*)(Ether*)); /* diff --git a/pc/fns.h b/pc/fns.h index 297fd3e0f9a2f9807ad1d3b74bccb2512c2963bf..df22057800e5e03a6b2f12d11241c423251b6d63 100644 --- a/pc/fns.h +++ b/pc/fns.h @@ -62,8 +62,9 @@ void outs(int, ushort); void outss(int, void*, int); void outl(int, ulong); void outsl(int, void*, int); -void pcicfgr(uchar, uchar, uchar, uchar, void*, int); -int pcimatch(uchar, uchar, PCIcfg*); +void pcicfgr(int, int, int, int, void*, int); +void pcicfgw(int, int, int, int, void*, int); +int pcimatch(int, int, PCIcfg*); PCMmap* pcmmap(int, ulong, int, int); int pcmspecial(char*, ISAConf*); void pcmspecialclose(int); diff --git a/pc/pci.c b/pc/pci.c index 8af745130d1441493bdbee7d34c403f822d74f5f..984aee8cc4a1d3fa57400d252c40214c81174aa1 100644 --- a/pc/pci.c +++ b/pc/pci.c @@ -14,10 +14,10 @@ static Lock pcicfglock; * nbytes are DWORD aligned. */ void -pcicfgr(uchar busno, uchar devno, uchar funcno, uchar regno, void* data, int nbytes) +pcicfgr(int busno, int devno, int funcno, int regno, void* data, int nbytes) { - ulong base, *p; - int len; + ulong* p; + int base, len; lock(&pcicfglock); outb(PCIcse, 0x80|((funcno & 0x07)<<1)); @@ -28,7 +28,29 @@ pcicfgr(uchar busno, uchar devno, uchar funcno, uchar regno, void* data, int nby for(len = nbytes/sizeof(ulong); len > 0; len--){ *p = inl(base); p++; - base += sizeof(ulong); + base += sizeof(*p); + } + + outb(PCIcse, 0x00); + unlock(&pcicfglock); +} + +void +pcicfgw(int busno, int devno, int funcno, int regno, void* data, int nbytes) +{ + ulong* p; + int base, len; + + lock(&pcicfglock); + outb(PCIcse, 0x80|((funcno & 0x07)<<1)); + outb(PCIforward, busno); + + base = (0xC000|(devno<<8)) + regno; + p = data; + for(len = nbytes/sizeof(ulong); len > 0; len--){ + outl(base, *p); + p++; + base += sizeof(*p); } outb(PCIcse, 0x00); @@ -36,17 +58,20 @@ pcicfgr(uchar busno, uchar devno, uchar funcno, uchar regno, void* data, int nby } int -pcimatch(uchar busno, uchar devno, PCIcfg* pcicfg) +pcimatch(int busno, int devno, PCIcfg* pcicfg) { ulong l; - l = 0; - pcicfgr(busno, devno, 0, 0, &l, sizeof(ulong)); - if((l & 0xFFFF) != pcicfg->vid) - return 0; - if(pcicfg->did && ((l>>16) & 0xFFFF) != pcicfg->did) - return 0; - pcicfgr(busno, devno, 0, 0, pcicfg, sizeof(PCIcfg)); - - return 1; + while(devno < MaxPCI){ + l = 0; + pcicfgr(busno, devno, 0, 0, &l, sizeof(ulong)); + devno++; + if((l & 0xFFFF) != pcicfg->vid) + continue; + if(pcicfg->did && ((l>>16) & 0xFFFF) != pcicfg->did) + continue; + pcicfgr(busno, devno-1, 0, 0, pcicfg, sizeof(PCIcfg)); + return devno; + } + return -1; }