From 774f0f5d9eac9404e39c6ae351358d5f5d520f58 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 7 Jun 1996 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1996-06-07 --- pc/devether.c | 20 +- pc/dma.c | 37 +- pc/ether509.c | 785 ----------------------------- pc/ether589.c | 24 +- pc/ether79c970.c | 68 ++- pc/ether82557.c | 150 ++++-- pc/etherelnk3.c | 1235 ++++++++++++++++++++++++++++++++++++++++++++++ pc/etherif.h | 3 +- pc/fns.h | 4 - pc/scsi.c | 107 +++- port/devsd.c | 124 +++-- port/portdat.h | 26 +- port/portfns.h | 1 + 13 files changed, 1598 insertions(+), 986 deletions(-) delete mode 100644 pc/ether509.c create mode 100644 pc/etherelnk3.c diff --git a/pc/devether.c b/pc/devether.c index 8c6457ae110f5010dd14af07c2be6683442a73e7..553f4f15d220c85d73597e6d9ee038964627dead 100644 --- a/pc/devether.c +++ b/pc/devether.c @@ -78,7 +78,21 @@ etherclose(Chan *c) long etherread(Chan *c, void *buf, long n, ulong offset) { - return netifread(ether[c->dev], c, buf, n, offset); + Ether *ctlr; + + ctlr = ether[c->dev]; + if((c->qid.path & CHDIR) == 0 && ctlr->ifstat){ + /* + * With some controllers it is necessary to reach + * into the chip to extract statistics. + */ + if(NETTYPE(c->qid.path) == Nifstatqid) + return (*ctlr->ifstat)(ctlr, buf, n, offset); + else if(NETTYPE(c->qid.path) == Nstatqid) + (*ctlr->ifstat)(ctlr, buf, 0, offset); + } + + return netifread(ctlr, c, buf, n, offset); } Block* @@ -233,7 +247,7 @@ etherreset(void) ctlr->irq = 9; setvec(Int0vec+ctlr->irq, ctlr->interrupt, ctlr); - print("ether%d: %s: %dMbps port 0x%luX irq %d", + print("ether#%d: %s: %dMbps port 0x%luX irq %d", ctlrno, ctlr->type, ctlr->mbps, ctlr->port, ctlr->irq); if(ctlr->mem) print(" addr 0x%luX", ctlr->mem & ~KZERO); @@ -245,7 +259,7 @@ etherreset(void) print("\n"); if(ctlr->mbps == 100) - netifinit(ctlr, "ether", Ntypes, 100*1024); + netifinit(ctlr, "ether", Ntypes, 128*1024); else netifinit(ctlr, "ether", Ntypes, 32*1024); ctlr->alen = Eaddrlen; diff --git a/pc/dma.c b/pc/dma.c index e8054adb508a2bec71c38e80154f87f0e881cc12..dde7baa4cd0ac0ed33365ac5c7bd4ac475d53603 100644 --- a/pc/dma.c +++ b/pc/dma.c @@ -4,9 +4,6 @@ #include "dat.h" #include "fns.h" -/* - * headland chip set for the safari. - */ typedef struct DMAport DMAport; typedef struct DMA DMA; typedef struct DMAxfer DMAxfer; @@ -33,8 +30,9 @@ enum */ struct DMAxfer { - Page pg; /* page used by dma */ - void *va; /* virtual address destination/src */ + ulong bpa; /* bounce buffer physical address */ + void* bva; /* bounce buffer virtual address */ + void* va; /* virtual address destination/src */ long len; /* bytes to be transferred */ int isread; }; @@ -95,8 +93,8 @@ dmainit(void) dp = &dma[i]; for(chan = 0; chan < 4; chan++){ xp = &dp->x[chan]; - xp->pg.pa = (ulong)xspanalloc(BY2PG, BY2PG, 0); - xp->pg.va = KZERO|xp->pg.pa; + xp->bva = xspanalloc(BY2PG, BY2PG, 0); + xp->bpa = PADDR(xp->bva); xp->len = 0; xp->isread = 0; } @@ -117,9 +115,9 @@ long dmasetup(int chan, void *va, long len, int isread) { DMA *dp; - DMAxfer *xp; ulong pa; uchar mode; + DMAxfer *xp; dp = &dma[(chan>>2)&1]; chan = chan & 3; @@ -132,16 +130,17 @@ dmasetup(int chan, void *va, long len, int isread) pa = PADDR(va); if((((ulong)va)&0xF0000000) != KZERO || (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000) - || pa > 16*MB){ + || pa > 16*MB) { if(len > BY2PG) len = BY2PG; if(!isread) - memmove((void*)(xp->pg.va), va, len); + memmove(xp->bva, va, len); xp->va = va; xp->len = len; xp->isread = isread; - pa = xp->pg.pa; - } else + pa = xp->bpa; + } + else xp->len = 0; /* @@ -149,7 +148,7 @@ dmasetup(int chan, void *va, long len, int isread) */ ilock(dp); mode = (isread ? 0x44 : 0x48) | chan; - outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */ + outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */ outb(dp->page[chan], pa>>16); outb(dp->cbp, 0); /* set count & address to their first byte */ outb(dp->addr[chan], pa>>dp->shift); /* set address */ @@ -203,16 +202,6 @@ dmaend(int chan) /* * copy out of temporary page */ - memmove(xp->va, (void*)(xp->pg.va), xp->len); + memmove(xp->va, xp->bva, xp->len); xp->len = 0; } - -void -dmaemode(int chan, int value) -{ - if(chan < 4) - outb(0x40b, value|chan); - else - outb(0x4d6, value|chan); - -} diff --git a/pc/ether509.c b/pc/ether509.c deleted file mode 100644 index 65c9111523348e230243da739c6d3b5318742b1b..0000000000000000000000000000000000000000 --- a/pc/ether509.c +++ /dev/null @@ -1,785 +0,0 @@ -#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 { - IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */ - /* Commands */ - GlobalReset = 0x00, /* Global Reset */ - SelectWindow = 0x01, /* SelectWindow command */ - StartCoax = 0x02, /* Start Coaxial Transceiver */ - RxDisable = 0x03, /* RX Disable */ - RxEnable = 0x04, /* RX Enable */ - RxReset = 0x05, /* RX Reset */ - RxDiscard = 0x08, /* RX Discard Top Packet */ - TxEnable = 0x09, /* TX Enable */ - TxDisable = 0x0A, /* TX Disable */ - TxReset = 0x0B, /* TX Reset */ - AckIntr = 0x0D, /* Acknowledge Interrupt */ - SetIntrMask = 0x0E, /* Set Interrupt Mask */ - SetReadZeroMask = 0x0F, /* Set Read Zero Mask */ - SetRxFilter = 0x10, /* Set RX Filter */ - SetTxAvailable = 0x12, /* Set TX Available Threshold */ - - /* RX Filter Command Bits */ - MyEtherAddr = 0x01, /* Individual address */ - Multicast = 0x02, /* Group (multicast) addresses */ - Broadcast = 0x04, /* Broadcast address */ - Promiscuous = 0x08, /* All addresses (promiscuous mode) */ - - /* Window Register Offsets */ - Command = 0x0E, /* all windows */ - Status = 0x0E, - - ManufacturerID = 0x00, /* window 0 */ - ProductID = 0x02, - ConfigControl = 0x04, - AddressConfig = 0x06, - ResourceConfig = 0x08, - EEPROMcmd = 0x0A, - EEPROMdata = 0x0C, - - /* AddressConfig Bits */ - XcvrTypeMask = 0xC000, /* Transceiver Type Select */ - Xcvr10BaseT = 0x0000, - XcvrAUI = 0x4000, - XcvrBNC = 0xC000, - - /* ConfigControl */ - Rst = 0x04, /* Reset Adapter */ - Ena = 0x01, /* Enable Adapter */ - - Fifo = 0x00, /* window 1 */ - RxStatus = 0x08, - Timer = 0x0A, - TxStatus = 0x0B, - TxFreeBytes = 0x0C, - - /* Status/Interrupt Bits */ - Latch = 0x0001, /* Interrupt Latch */ - Failure = 0x0002, /* Adapter Failure */ - TxComplete = 0x0004, /* TX Complete */ - TxAvailable = 0x0008, /* TX Available */ - RxComplete = 0x0010, /* RX Complete */ - Update = 0x0080, /* Update Statistics */ - AllIntr = 0x00FE, /* All Interrupt Bits */ - CmdInProgress = 0x1000, /* Command In Progress */ - - /* TxStatus Bits */ - TxJabber = 0x20, /* Jabber Error */ - TxUnderrun = 0x10, /* Underrun */ - TxMaxColl = 0x08, /* Maximum Collisions */ - - /* RxStatus Bits */ - RxByteMask = 0x07FF, /* RX Bytes (0-1514) */ - RxErrMask = 0x3800, /* Type of Error: */ - RxErrOverrun = 0x0000, /* Overrrun */ - RxErrOversize = 0x0800, /* Oversize Packet (>1514) */ - RxErrDribble = 0x1000, /* Dribble Bit(s) */ - RxErrRunt = 0x1800, /* Runt Packet */ - RxErrFraming = 0x2000, /* Alignment (Framing) */ - RxErrCRC = 0x2800, /* CRC */ - RxError = 0x4000, /* Error */ - RxEmpty = 0x8000, /* Incomplete or FIFO empty */ - - InternalCgf = 0x00, /* window 3 */ - - FIFOdiag = 0x04, /* window 4 */ - MediaStatus = 0x0A, - - /* FIFOdiag bits */ - TxOverrun = 0x0400, /* TX Overrrun */ - RxOverrun = 0x0800, /* RX Overrun */ - RxStatusOverrun = 0x1000, /* RX Status Overrun */ - RxUnderrun = 0x2000, /* RX Underrun */ - RxReceiving = 0x8000, /* RX Receiving */ - - /* MediaStatus bits */ - JabberEna = 0x0040, /* Jabber Enabled (writeable) */ - LinkBeatEna = 0x0080, /* Link Beat Enabled (writeable) */ - LinkBeatOk = 0x0800, /* Valid link beat detected (ro) */ -}; - -#define COMMAND(port, cmd, a) outs(port+Command, ((cmd)<<11)|(a)) - -static void -attach(Ether *ether) -{ - ulong port; - int x; - - port = ether->port; - /* - * Set the receiver packet filter for our own and - * and broadcast addresses, set the interrupt masks - * for all interrupts, and enable the receiver and transmitter. - * The only interrupt we should see under normal conditions - * is the receiver interrupt. If the transmit FIFO fills up, - * we will also see TxAvailable interrupts. - * Disable Update interrupts for now. - */ - x = Broadcast|MyEtherAddr; - if(ether->prom) - x |= Promiscuous; - COMMAND(port, SetRxFilter, x); - COMMAND(port, SetReadZeroMask, (AllIntr|Latch) & ~Update); - COMMAND(port, SetIntrMask, (AllIntr|Latch) & ~Update); - COMMAND(port, RxEnable, 0); - COMMAND(port, TxEnable, 0); -} - -static void -promiscuous(void *arg, int on) -{ - ulong port; - - port = ((Ether*)arg)->port; - if(on) - COMMAND(port, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr); - else - COMMAND(port, SetRxFilter, Broadcast|MyEtherAddr); -} - -static void -receive(Ether *ether) -{ - ushort status; - int len; - ulong port; - - port = ether->port; - while(((status = ins(port+RxStatus)) & RxEmpty) == 0){ - /* - * If we had an error, log it and continue. - */ - if(status & RxError){ - switch(status & RxErrMask){ - - case RxErrOverrun: /* Overrrun */ - ether->overflows++; - break; - - case RxErrOversize: /* Oversize Packet (>1514) */ - case RxErrRunt: /* Runt Packet */ - ether->buffs++; - break; - case RxErrFraming: /* Alignment (Framing) */ - ether->frames++; - break; - - case RxErrCRC: /* CRC */ - ether->crcs++; - break; - } - } - else { - /* - * We have a packet. Read it into the - * buffer. The CRC is already stripped off. - * Must read len bytes padded to a - * doubleword. We can pick them out 32-bits - * at a time. - */ - ether->inpackets++; - len = (status & RxByteMask); - insl(port+Fifo, ðer->rpkt, HOWMANY(len, 4)); - - /* - * Copy the packet to whoever wants it. - */ - etherrloop(ether, ðer->rpkt, len); - } - - /* - * Discard the packet as we're done with it. - * Wait for discard to complete. - */ - COMMAND(port, RxDiscard, 0); - while(ins(port+Status) & CmdInProgress) - ; - } -} - -static int -istxfifo(void *arg) -{ - Ether *ether; - ushort len; - ulong port; - - ether = arg; - port = ether->port; - /* - * If there's no room in the FIFO for this packet, - * set up an interrupt for when space becomes available. - * Output packet must be a multiple of 4 in length and - * we need 4 bytes for the preamble. - * Assume here that when we are called (via tsleep) that - * we are safe from interrupts. - */ - len = ROUNDUP(ether->tlen, 4); - if(len+4 > ins(port+TxFreeBytes)){ - COMMAND(port, SetTxAvailable, len); - return 0; - } - return 1; -} - -static long -write(Ether *ether, void *buf, long n) -{ - ushort len; - ulong port; - - port = ether->port; - - ether->tlen = n; - len = ROUNDUP(ether->tlen, 4); - tsleep(ðer->tr, istxfifo, ether, 10000); - if(len+4 > ins(port+TxFreeBytes)){ - print("ether509: transmitter jammed\n"); - return 0; - } - - /* - * We know there's room, copy the packet to the FIFO. - * To save copying the packet into a local buffer just - * so we can set the source address, stuff the packet - * into the FIFO in 3 pieces. - * Transmission won't start until the entire packet is - * in the FIFO, so it's OK to fault here. - */ - outs(port+Fifo, ether->tlen); - outs(port+Fifo, 0); - outss(port+Fifo, buf, Eaddrlen/2); - outss(port+Fifo, ether->ea, Eaddrlen/2); - outsl(port+Fifo, (uchar*)buf+2*Eaddrlen, (len-2*Eaddrlen)/4); - ether->outpackets++; - - return n; -} - -static ushort -getdiag(Ether *ether) -{ - ushort bytes; - ulong port; - - port = ether->port; - COMMAND(port, SelectWindow, 4); - bytes = ins(port+FIFOdiag); - COMMAND(port, SelectWindow, 1); - return bytes & 0xFFFF; -} - -static void -interrupt(Ureg *ur, void *arg) -{ - ushort status, diag; - uchar txstatus, x; - ulong port; - Ether *ether; - - USED(ur); - ether = arg; - port = ether->port; - - for(;;){ - /* - * Clear the interrupt latch. - * It's possible to receive a packet and for another - * to become complete before we exit the interrupt - * handler so this must be done first to ensure another - * interrupt will occur. - */ - COMMAND(port, AckIntr, Latch); - status = ins(port+Status); - if((status & AllIntr) == 0) - break; - - if(status & Failure){ - /* - * Adapter failure, try to find out why. - * Reset if necessary. - * What happens if Tx is active and we reset, - * need to retransmit? - * This probably isn't right. - */ - diag = getdiag(ether); - print("ether509: status #%ux, diag #%ux\n", status, diag); - - if(diag & TxOverrun){ - COMMAND(port, TxReset, 0); - COMMAND(port, TxEnable, 0); - wakeup(ðer->tr); - } - - if(diag & RxUnderrun){ - COMMAND(port, RxReset, 0); - attach(ether); - } - - return; - } - - if(status & RxComplete){ - receive(ether); - status &= ~RxComplete; - } - - if(status & TxComplete){ - /* - * Pop the TX Status stack, accumulating errors. - * If there was a Jabber or Underrun error, reset - * the transmitter. For all conditions enable - * the transmitter. - */ - txstatus = 0; - do{ - if(x = inb(port+TxStatus)) - outb(port+TxStatus, 0); - txstatus |= x; - }while(ins(port+Status) & TxComplete); - - if(txstatus & (TxJabber|TxUnderrun)) - COMMAND(port, TxReset, 0); - COMMAND(port, TxEnable, 0); - ether->oerrs++; - } - - if(status & (TxAvailable|TxComplete)){ - /* - * Reset the Tx FIFO threshold. - */ - if(status & TxAvailable){ - COMMAND(port, AckIntr, TxAvailable); - wakeup(ðer->tr); - } - status &= ~(TxAvailable|TxComplete); - } - - /* - * Panic if there are any interrupt bits on we haven't - * dealt with. Should deal with UP (Update Statistics) - * for happier coexistence with Windows drivers. - */ - if(status & AllIntr) - panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(ether)); - } -} - -typedef struct Adapter Adapter; -struct Adapter { - Adapter* next; - ulong port; - PCIcfg* pcicfg; -}; -static Adapter *adapter; - -/* - * 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. - */ -static void -idseq(void) -{ - int i; - uchar al; - static int reset, untag; - - /* - * One time only: - * reset any adapters listening - */ - if(reset == 0){ - outb(IDport, 0); - outb(IDport, 0); - outb(IDport, 0xC0); - delay(20); - reset = 1; - } - - outb(IDport, 0); - outb(IDport, 0); - for(al = 0xFF, i = 0; i < 255; i++){ - outb(IDport, al); - if(al & 0x80){ - al <<= 1; - al ^= 0xCF; - } - else - al <<= 1; - } - - /* - * One time only: - * write ID sequence to get the attention of all adapters; - * untag all adapters. - * If we do a global reset here on all adapters we'll confuse any - * ISA cards configured for EISA mode. - */ - if(untag == 0){ - outb(IDport, 0xD0); - untag = 1; - } -} - -static ulong -activate(void) -{ - int i; - ushort x, acr; - - /* - * Do the little configuration dance: - * - * 2. write the ID sequence to get to command state. - */ - idseq(); - - /* - * 3. Read the Manufacturer ID from the EEPROM. - * This is done by writing the IDPort with 0x87 (0x80 - * is the 'read EEPROM' command, 0x07 is the offset of - * the Manufacturer ID field in the EEPROM). - * The data comes back 1 bit at a time. - * We seem to need a delay here between reading the bits. - * - * If the ID doesn't match, there are no more adapters. - */ - outb(IDport, 0x87); - delay(20); - for(x = 0, i = 0; i < 16; i++){ - delay(20); - x <<= 1; - x |= inb(IDport) & 0x01; - } - if(x != 0x6D50) - return 0; - - /* - * 3. Read the Address Configuration from the EEPROM. - * The Address Configuration field is at offset 0x08 in the EEPROM). - */ - outb(IDport, 0x88); - for(acr = 0, i = 0; i < 16; i++){ - delay(20); - acr <<= 1; - acr |= inb(IDport) & 0x01; - } - - return (acr & 0x1F)*0x10 + 0x200; -} - -static ulong -tcm509(Ether *ether) -{ - ulong port; - Adapter *ap; - - /* - * Attempt to activate adapters until one matches the - * address criteria. If adapter is set for EISA mode (0x3F0), - * tag it and ignore. Otherwise, activate it fully. - */ - while(port = activate()){ - /* - * 6. Tag the adapter so it won't respond in future. - */ - outb(IDport, 0xD1); - if(port == 0x3F0) - continue; - - /* - * 6. Activate the adapter by writing the Activate command - * (0xFF). - */ - outb(IDport, 0xFF); - delay(20); - - /* - * 8. Can now talk to the adapter's I/O base addresses. - * Use the I/O base address from the acr just read. - * - * Enable the adapter. - */ - while(ins(port+Status) & CmdInProgress) - ; - COMMAND(port, SelectWindow, 0); - outs(port+ConfigControl, Ena); - - COMMAND(port, TxReset, 0); - COMMAND(port, RxReset, 0); - COMMAND(port, AckIntr, 0xFF); - - if(ether->port == 0 || ether->port == port) - return port; - - ap = malloc(sizeof(Adapter)); - ap->port = port; - ap->next = adapter; - adapter = ap; - } - - return 0; -} - -static ulong -tcm579(Ether *ether) -{ - static int slot = 1; - ushort x; - ulong port; - Adapter *ap; - - /* - * First time through, check if this is an EISA machine. - * If not, nothing to do. - */ - if(slot == 1 && strncmp((char*)(KZERO|0xFFFD9), "EISA", 4)) - return 0; - - /* - * Continue through the EISA slots looking for a match on both - * 3COM as the manufacturer and 3C579 or 3C579-TP as the product. - * If we find an adapter, select window 0, enable it and clear - * out any lingering status and interrupts. - * Trying to do a GlobalReset here to re-init the card (as in the - * 509 code) doesn't seem to work. - */ - while(slot < MaxEISA){ - port = slot++*0x1000; - if(ins(port+0xC80+ManufacturerID) != 0x6D50) - continue; - x = ins(port+0xC80+ProductID); - if((x & 0xF0FF) != 0x9050/* || (x != 0x9350 && x != 0x9250)*/) - continue; - - COMMAND(port, SelectWindow, 0); - outs(port+ConfigControl, Ena); - - COMMAND(port, TxReset, 0); - COMMAND(port, RxReset, 0); - COMMAND(port, AckIntr, 0xFF); - - if(ether->port == 0 || ether->port == port) - return port; - - ap = malloc(sizeof(Adapter)); - ap->port = port; - ap->next = adapter; - adapter = ap; - } - - return 0; -} - -static PCIcfg* -tcm590(Ether *ether) -{ - PCIcfg* pcicfg; - static int devno = 0; - int port; - Adapter *ap; - - pcicfg = malloc(sizeof(PCIcfg)); - for(;;){ - pcicfg->vid = 0x10B7; - pcicfg->did = 0; - if((devno = pcimatch(0, devno, pcicfg)) == -1) - break; - - port = pcicfg->baseaddr[0] & ~0x01; - COMMAND(port, GlobalReset, 0); - while(ins(port+Status) & CmdInProgress) - ; - - 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 ulong -tcm589(ISAConf *isa) -{ - if(strcmp(isa->type, "3C589") != 0) - return 0; - - /* - * The 3com manual register description says that this a noop for - * PCMCIA but the flow chart at the end shows it. - */ - COMMAND(isa->port, SelectWindow, 0); - outs(isa->port+ConfigControl, Ena); - return isa->port; -} - -/* - * Get configuration parameters. - */ -int -ether509reset(Ether *ether) -{ - int i, eax; - uchar ea[Eaddrlen]; - ushort x, acr; - ulong l, port; - Adapter *ap, **app; - PCIcfg *pcicfg; - - /* - * Any adapter matches if no ether->port is supplied, - * otherwise the ports must match. - * See if we've already found an adapter that fits - * the bill. - * If no match then try for an EISA card, an ISA card - * and finally for a PCMCIA card. - */ - 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) - port = tcm589(ether); - if(port == 0 && (pcicfg = tcm590(ether))) - port = pcicfg->baseaddr[0] & ~0x01; - if(port == 0) - port = tcm579(ether); - if(port == 0) - port = tcm509(ether); - if(port == 0) - return -1; - - /* - * Read the IRQ from the Resource Configuration Register, - * the ethernet address from the EEPROM, and the address configuration. - * The EEPROM command is 8 bits, the lower 6 bits being - * the address offset. - */ - COMMAND(port, SelectWindow, 0); - if(strcmp(ether->type, "3C589") != 0 && pcicfg == 0) - ether->irq = (ins(port+ResourceConfig)>>12) & 0x0F; - for(eax = 0, i = 0; i < 3; i++, eax += 2){ - while(ins(port+EEPROMcmd) & 0x8000) - ; - outs(port+EEPROMcmd, (2<<6)|i); - while(ins(port+EEPROMcmd) & 0x8000) - ; - x = ins(port+EEPROMdata); - ea[eax] = (x>>8) & 0xFF; - ea[eax+1] = x & 0xFF; - } - if(pcicfg){ - ether->irq = pcicfg->irq; - COMMAND(port, SelectWindow, 3); - acr = XcvrAUI; - l = inl(port+InternalCgf); - switch(l & 0x700000){ - - case 0x000000: - acr = Xcvr10BaseT; - break; - - case 0x300000: - acr = XcvrBNC; - break; - } - free(pcicfg); - } - else - acr = ins(port+AddressConfig); - - /* - * Finished with window 0. Now set the ethernet address - * in window 2. - * Commands have the format 'CCCCCAAAAAAAAAAA' where C - * is a bit in the command and A is a bit in the argument. - */ - COMMAND(port, SelectWindow, 2); - if((ether->ea[0]|ether->ea[1]|ether->ea[2]|ether->ea[3]|ether->ea[4]|ether->ea[5]) == 0){ - for(i = 0; i < sizeof(ether->ea); i++) - ether->ea[i] = ea[i]; - } - for(i = 0; i < Eaddrlen; i++) - outb(port+i, ether->ea[i]); - - /* - * Enable the transceiver if necessary. - */ - COMMAND(port, SelectWindow, 4); - x = ins(port+MediaStatus) & ~(LinkBeatEna|JabberEna); - outs(port+MediaStatus, x); - switch(acr & XcvrTypeMask){ - - case Xcvr10BaseT: - /* - * Enable Link Beat and Jabber to start the - * transceiver. - */ - outs(port+MediaStatus, x|LinkBeatEna|JabberEna); - break; - - case XcvrBNC: - /* - * Start the DC-DC converter. - * Wait > 800 microseconds. - */ - COMMAND(port, StartCoax, 0); - delay(1); - break; - } - - /* - * Set window 1 for normal operation. - * Clear out any lingering Tx status. - */ - COMMAND(port, SelectWindow, 1); - while(inb(port+TxStatus)) - outb(port+TxStatus, 0); - - ether->port = port; - - /* - * Set up the software configuration. - */ - ether->attach = attach; - ether->write = write; - ether->interrupt = interrupt; - - ether->promiscuous = promiscuous; - ether->arg = ether; - - return 0; -} - -void -ether509link(void) -{ - addethercard("3C509", ether509reset); -} diff --git a/pc/ether589.c b/pc/ether589.c index 6e2b7127cd002d7ff7b88a31d8cfa47d70eea57f..dc93ec8856916db50b5114d835020e3199c927dc 100644 --- a/pc/ether589.c +++ b/pc/ether589.c @@ -40,7 +40,7 @@ enum { #define COMMAND(port, cmd, a) outs(port+Command, ((cmd)<<11)|(a)) -extern int ether509reset(Ether*); +extern int etherelnk3reset(Ether*); static int configASIC(Ether *ether, int port, int xcvr) @@ -50,25 +50,17 @@ configASIC(Ether *ether, int port, int xcvr) /* set Window 0 configuration registers */ COMMAND(port, SelectWindow, 0); + x = ins(port+ConfigControl); + outs(port+ConfigControl, x|1); + /* ROM size & base - must be set before we can access ROM */ /* transceiver type is 2 for 'figure it out' */ - x = ins(port + AddressConfig); - outs(port + AddressConfig, (x & 0xf0) | xcvr); + outs(port + AddressConfig, xcvr); /* IRQ must be 3 on 3C589 */ - x = ins(port + ResourceConfig); - outs(port + ResourceConfig, 0x3f00 | (x&0xff)); - - /* move product ID to register */ - while(ins(port+EEPROMcmd) & 0x8000) - ; - outs(port+EEPROMcmd, (2<<6)|3); - while(ins(port+EEPROMcmd) & 0x8000) - ; - x = ins(port+EEPROMdata); - outs(port + ProductID, x); - - return ether509reset(ether); + outs(port + ResourceConfig, 0x3F00); + + return etherelnk3reset(ether); } static int diff --git a/pc/ether79c970.c b/pc/ether79c970.c index 85455babd7688ead44c9bcd24d275f1df797b5c5..c89bd00e9973221608878ad78a2bedb835f2a60e 100644 --- a/pc/ether79c970.c +++ b/pc/ether79c970.c @@ -2,6 +2,8 @@ * AM79C970 * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus * To do: + * bag the tsleep in write, buffer them up; + * loop in interrupt routine until all done; * only issue transmit interrupt if necessary? * dynamically increase rings as necessary? * use Block's as receive buffers? @@ -236,9 +238,9 @@ write(Ether* ether, void* buf, long n) return 0; /* - * Copy the packet to the transmit buffer and fill in our + * Copy the packet to the transmit buffer and fill in the * source ethernet address. There's no need to pad to ETHERMINTU - * here as we set ApadXmit in CSR4. + * here as ApadXmit is set in CSR4. */ pkt = KADDR(tdre->tbadr); memmove(pkt->d, buf, n); @@ -324,38 +326,37 @@ typedef struct Adapter Adapter; struct Adapter { Adapter* next; int port; - PCIcfg* pcicfg; + int irq; }; static Adapter *adapter; -static PCIcfg* +static int amd79c90(Ether* ether) { - PCIcfg *pcicfg; + PCIcfg pcicfg; static int devno = 0; - int port; + int irq, port; Adapter *ap; - pcicfg = malloc(sizeof(PCIcfg)); for(;;){ - pcicfg->vid = 0x1022; - pcicfg->did = 0x2000; - if((devno = pcimatch(0, devno, pcicfg)) == -1) + 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; + port = pcicfg.baseaddr[0] & ~0x01; + irq = pcicfg.irq; + if(ether->port == 0 || ether->port == port){ + ether->irq = irq; + return port; + } ap = malloc(sizeof(Adapter)); - ap->pcicfg = pcicfg; ap->port = port; + ap->irq = irq; ap->next = adapter; adapter = ap; - - pcicfg = malloc(sizeof(PCIcfg)); } - free(pcicfg); return 0; } @@ -364,7 +365,6 @@ static int reset(Ether* ether) { int port, x; - PCIcfg *pcicfg; Adapter *ap, **app; uchar ea[Eaddrlen]; Ctlr *ctlr; @@ -375,37 +375,29 @@ reset(Ether* ether) * 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; + ether->irq = ap->irq; *app = ap->next; free(ap); break; } } - if(port == 0 && (pcicfg = amd79c90(ether))){ - port = pcicfg->baseaddr[0] & ~0x01; - ether->irq = pcicfg->irq; - } - if(port == 0) + if(port == 0 && (port = amd79c90(ether)) == 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? + * How to tell what mode the chip is in at this point - if it's in WORD + * mode then the only 32-bit access allowedis a write to the RDP, which + * forces the chip to DWORD mode; if it's in DWORD mode then only DWORD + * accesses are allowed? + * Assuming a DWORD write is done to the RDP, what will be overwritten as + * the RAP can't reliably be accessed? * * 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. + * again. 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. */ @@ -423,7 +415,7 @@ reset(Ether* ether) outl(port+Rap, 0); /* - * Check if we are going to override the adapter's station address. + * Check if the adapter's station address is to be over-ridden. * If not, read it from the I/O-space and set in ether->ea prior to loading the * station address in the initialisation block. */ @@ -457,7 +449,7 @@ reset(Ether* ether) /* * 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. + * enables will be set later when attaching to the network. */ x = PADDR(&ctlr->iblock); outl(port+Rap, 1); diff --git a/pc/ether82557.c b/pc/ether82557.c index 7d3dff054c8133f179937f00a4ddc506a8239f17..170efb7161d421f0927e441437e3bcc2493f18e1 100644 --- a/pc/ether82557.c +++ b/pc/ether82557.c @@ -6,9 +6,7 @@ * the PCI scanning code could be made common to other adapters; * PCI code needs rewritten to handle byte, word, dword accesses * and using the devno as a bus+dev+function triplet; - * tidy/fix locking; - * optionally use memory-mapped registers; - * stats. + * optionally use memory-mapped registers. */ #include "u.h" #include "../port/lib.h" @@ -141,7 +139,7 @@ enum { /* count */ typedef struct Cb { int command; ulong link; - uchar data[24]; /* CbIAS + CbConfigure */ + uchar data[24]; /* CbIAS + CbConfigure */ } Cb; typedef struct TxCB { @@ -185,16 +183,18 @@ typedef struct Ctlr { uchar configdata[24]; - Lock rlock; + Lock rlock; /* registers */ - Block* rfd[Nrfd]; + Block* rfd[Nrfd]; /* receive side */ int rfdl; int rfdx; - Lock cbqlock; - Block* cbqhead; + Block* cbqhead; /* transmit side */ Block* cbqtail; int cbqbusy; + + Lock dlock; /* dump statistical counters */ + ulong dump[17]; } Ctlr; static uchar configdata[24] = { @@ -239,9 +239,9 @@ custart(Ctlr* ctlr) } ctlr->cbqbusy = 1; - csr32w(ctlr, General, PADDR(ctlr->cbqhead->rp)); while(csr8r(ctlr, Command)) ; + csr32w(ctlr, General, PADDR(ctlr->cbqhead->rp)); csr8w(ctlr, Command, CUstart); } @@ -250,7 +250,6 @@ action(Ctlr* ctlr, Block* bp) { Cb *cb; - ilock(&ctlr->cbqlock); cb = (Cb*)bp->rp; cb->command |= CbEL; @@ -266,8 +265,6 @@ action(Ctlr* ctlr, Block* bp) if(ctlr->cbqbusy == 0) custart(ctlr); - - iunlock(&ctlr->cbqlock); } static void @@ -280,23 +277,20 @@ attach(Ether* ether) ilock(&ctlr->rlock); status = csr16r(ctlr, Status); if((status & RUstatus) == RUidle){ - csr32w(ctlr, General, PADDR(ctlr->rfd[ctlr->rfdx]->rp)); while(csr8r(ctlr, Command)) ; + csr32w(ctlr, General, PADDR(ctlr->rfd[ctlr->rfdx]->rp)); csr8w(ctlr, Command, RUstart); } iunlock(&ctlr->rlock); } -static void -configure(void* arg, int promiscuous) +static Block* +configure(Ctlr* ctlr, int promiscuous) { - Ctlr *ctlr; Block *bp; Cb *cb; - ctlr = ((Ether*)arg)->ctlr; - bp = allocb(sizeof(Cb)); cb = (Cb*)bp->rp; bp->wp += sizeof(Cb); @@ -306,12 +300,84 @@ configure(void* arg, int promiscuous) memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata)); if(promiscuous) cb->data[15] |= 0x01; + + return bp; +} + +static void +promiscuous(void* arg, int on) +{ + Ctlr *ctlr; + Block *bp; + + ctlr = ((Ether*)arg)->ctlr; + bp = configure(ctlr, on); + + ilock(&ctlr->rlock); action(ctlr, bp); + iunlock(&ctlr->rlock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char buf[512]; + int len; + + ctlr = ether->ctlr; + lock(&ctlr->dlock); + ctlr->dump[16] = 0; + + ilock(&ctlr->rlock); + while(csr8r(ctlr, Command)) + ; + csr8w(ctlr, Command, DumpSC); + iunlock(&ctlr->rlock); + + /* + * Wait for completion status, should be 0xA005. + */ + while(ctlr->dump[16] == 0) + ; + + ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3]; + ether->crcs = ctlr->dump[10]; + ether->frames = ctlr->dump[11]; + ether->buffs = ctlr->dump[12]+ctlr->dump[15]; + ether->overflows = ctlr->dump[13]; + + if(n == 0){ + unlock(&ctlr->dlock); + return 0; + } + + len = sprint(buf, "transmit good frames: %ld\n", ctlr->dump[0]); + len += sprint(buf+len, "transmit maximum collisions errors: %ld\n", ctlr->dump[1]); + len += sprint(buf+len, "transmit late collisions errors: %ld\n", ctlr->dump[2]); + len += sprint(buf+len, "transmit underrun errors: %ld\n", ctlr->dump[3]); + len += sprint(buf+len, "transmit lost carrier sense: %ld\n", ctlr->dump[4]); + len += sprint(buf+len, "transmit deferred: %ld\n", ctlr->dump[5]); + len += sprint(buf+len, "transmit single collisions: %ld\n", ctlr->dump[6]); + len += sprint(buf+len, "transmit multiple collisions: %ld\n", ctlr->dump[7]); + len += sprint(buf+len, "transmit total collisions: %ld\n", ctlr->dump[8]); + len += sprint(buf+len, "receive good frames: %ld\n", ctlr->dump[9]); + len += sprint(buf+len, "receive CRC errors: %ld\n", ctlr->dump[10]); + len += sprint(buf+len, "receive alignment errors: %ld\n", ctlr->dump[11]); + len += sprint(buf+len, "receive resource errors: %ld\n", ctlr->dump[12]); + len += sprint(buf+len, "receive overrun errors: %ld\n", ctlr->dump[13]); + len += sprint(buf+len, "receive collision detect errors: %ld\n", ctlr->dump[14]); + sprint(buf+len, "receive short frame errors: %ld\n", ctlr->dump[15]); + + unlock(&ctlr->dlock); + + return readstr(offset, a, n, buf); } static long write(Ether* ether, void* buf, long n) { + Ctlr *ctlr; Block *bp; TxCB *txcb; @@ -330,7 +396,10 @@ write(Ether* ether, void* buf, long n) memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); bp->wp += n; - action(ether->ctlr, bp); + ctlr = ether->ctlr; + ilock(&ctlr->rlock); + action(ctlr, bp); + iunlock(&ctlr->rlock); ether->outpackets++; @@ -349,19 +418,23 @@ interrupt(Ureg*, void* arg) ether = arg; ctlr = ether->ctlr; + ilock(&ctlr->rlock); for(;;){ status = csr16r(ctlr, Status); csr8w(ctlr, Ack, (status>>8) & 0xFF); - if((status & (StatCX|StatFR|StatCNA|StatRNR)) == 0) - return; + if((status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) == 0) + break; if(status & StatFR){ bp = ctlr->rfd[ctlr->rfdx]; rfd = (Rfd*)bp->rp; while(rfd->field & RfdC){ - etherrloop(ether, rfd, rfd->count & 0x3FFF); - ether->inpackets++; + if(rfd->field & RfdOK){ + etherrloop(ether, rfd, rfd->count & 0x3FFF); + ether->inpackets++; + } +else print("%s#%d: rfd->field %uX\n", ctlr->type, ctlr->ctlrno, rfd->field); /* * Reinitialise the frame for reception and bump @@ -390,12 +463,12 @@ interrupt(Ureg*, void* arg) while(csr8r(ctlr, Command)) ; csr8w(ctlr, Command, RUresume); +print("%s#%d: status %uX\n", ctlr->type, ctlr->ctlrno, status); status &= ~StatRNR; } if(status & StatCNA){ - lock(&ctlr->cbqlock); while(bp = ctlr->cbqhead){ if((((Cb*)bp->rp)->command & CbC) == 0) break; @@ -403,7 +476,6 @@ interrupt(Ureg*, void* arg) freeb(bp); } custart(ctlr); - unlock(&ctlr->cbqlock); status &= ~StatCNA; } @@ -411,6 +483,7 @@ interrupt(Ureg*, void* arg) if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) panic("%s#%d: status %uX\n", ctlr->type, ctlr->ctlrno, status); } + iunlock(&ctlr->rlock); } static void @@ -612,21 +685,26 @@ reset(Ether* ether) ctlr->type = ether->type; ctlr->port = port; + ilock(&ctlr->rlock); + csr32w(ctlr, Port, 0); delay(1); - pcicfgr(0, pcidevno, 0, 0x04, &x, 4); - if((x & 0x05) != 0x05) - print("PCI command = %uX\n", x); - - csr32w(ctlr, General, 0); while(csr8r(ctlr, Command)) ; + csr32w(ctlr, General, 0); csr8w(ctlr, Command, LoadRUB); + while(csr8r(ctlr, Command)) ; csr8w(ctlr, Command, LoadCUB); + while(csr8r(ctlr, Command)) + ; + csr32w(ctlr, General, PADDR(ctlr->dump)); + csr8w(ctlr, Command, LoadDCA); + + /* * Initialise the receive frame and configuration areas. */ @@ -638,8 +716,8 @@ reset(Ether* ether) * configuration. However, should check for the existence of the PHY * and, if found, check whether to use 82503 (serial) or MII (nibble) * mode. Verify the PHY is a National Semiconductor DP83840 by looking - * at the Organizationally Unique Identifier (OUI) in registers 2 and 3 - * which should be 0x80017. + * at the Organizationally Unique Identifier (OUI) in registers 2 and + * 3 which should be 0x80017. */ for(i = 1; i < 32; i++){ if((x = dp83840r(ctlr, i, 2)) == 0xFFFF) @@ -661,7 +739,8 @@ reset(Ether* ether) /* * Load the chip configuration */ - configure(ether, 0); + bp = configure(ctlr, 0); + action(ctlr, bp); /* * Check if the adapter's station address is to be overridden. @@ -686,6 +765,8 @@ reset(Ether* ether) memmove(cb->data, ether->ea, Eaddrlen); action(ctlr, bp); + iunlock(&ctlr->rlock); + /* * Linkage to the generic ethernet driver. */ @@ -693,8 +774,9 @@ reset(Ether* ether) ether->attach = attach; ether->write = write; ether->interrupt = interrupt; + ether->ifstat = ifstat; - ether->promiscuous = configure; + ether->promiscuous = promiscuous; ether->arg = ether; return 0; diff --git a/pc/etherelnk3.c b/pc/etherelnk3.c new file mode 100644 index 0000000000000000000000000000000000000000..7f3047604a69dccea91b7a2c444848ba7b3632b1 --- /dev/null +++ b/pc/etherelnk3.c @@ -0,0 +1,1235 @@ +/* + * Etherlink III and Fast EtherLink adapters. + * To do: + * check robustness in the face of errors; + * autoSelect; + * PCI latency timer and master enable; + * errata list. + * + * Product ID: + * 9150 ISA 3C509[B] + * 9050 ISA 3C509[B]-TP + * 9450 ISA 3C509[B]-COMBO + * 9550 ISA 3C509[B]-TPO + * + * 9350 EISA 3C579 + * 9250 EISA 3C579-TP + * + * 5920 EISA 3C592-[TP|COMBO|TPO] + * 5970 EISA 3C597-TX Fast Etherlink 10BASE-T/100BASE-TX + * 5971 EISA 3C597-T4 Fast Etherlink 10BASE-T/100BASE-T4 + * 5972 EISA 3C597-MII Fast Etherlink 10BASE-T/MII + * + * 5900 PCI 3C590-[TP|COMBO|TPO] + * 5950 PCI 3C595-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * 5951 PCI 3C595-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4 + * 5952 PCI 3C595-MII Fast Etherlink 10BASE-T/MII + * + * 9058 PCMCIA 3C589[B]-[TP|COMBO] + * + * 627C MCA 3C529 + * 627D MCA 3C529-TP + */ +#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 { + IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */ +}; + +enum { /* all windows */ + Command = 0x000E, + IntStatus = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + EnableDcConverter = 0x0002, + RxDisable = 0x0003, + RxEnable = 0x0004, + RxReset = 0x0005, + TxDone = 0x0007, + RxDiscard = 0x0008, + TxEnable = 0x0009, + TxDisable = 0x000A, + TxReset = 0x000B, + RequestInterrupt = 0x000C, + AcknowledgeInterrupt = 0x000D, + SetInterruptEnable = 0x000E, + SetIndicationEnable = 0x000F, /* SetReadZeroMask */ + SetRxFilter = 0x0010, + SetRxEarlyThresh = 0x0011, + SetTxAvailableThresh = 0x0012, + SetTxStartThresh = 0x0013, + StartDma = 0x0014, /* initiate busmaster operation */ + StatisticsEnable = 0x0015, + StatisticsDisable = 0x0016, + DisableDcConverter = 0x0017, + SetTxReclaimThresh = 0x0018, /* PIO-only adapters */ + PowerUp = 0x001B, /* not all adapters */ + PowerDownFull = 0x001C, /* not all adapters */ + PowerAuto = 0x001D, /* not all adapters */ +}; + +enum { /* (Global|Rx|Tx)Reset command bits */ + tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */ + endecReset = 0x0002, /* internal Ethernet encoder/decoder */ + networkReset = 0x0004, /* network interface logic */ + fifoReset = 0x0008, /* FIFO control logic */ + aismReset = 0x0010, /* autoinitialise state-machine logic */ + hostReset = 0x0020, /* bus interface logic */ + dmaReset = 0x0040, /* bus master logic */ + vcoReset = 0x0080, /* on-board 10Mbps VCO */ + + resetMask = 0x00FF, +}; + +enum { /* SetRxFilter command bits */ + receiveIndividual = 0x0001, /* match station address */ + receiveMulticast = 0x0002, + receiveBroadcast = 0x0004, + receiveAllFrames = 0x0008, /* promiscuous */ +}; + +enum { /* StartDma command bits */ + Upload = 0x0000, /* transfer data from adapter to memory */ + Download = 0x0001, /* transfer data from memory to adapter */ +}; + +enum { /* IntStatus bits */ + interruptLatch = 0x0001, + hostError = 0x0002, /* Adapter Failure */ + txComplete = 0x0004, + txAvailable = 0x0008, + rxComplete = 0x0010, + rxEarly = 0x0020, + intRequested = 0x0040, + updateStats = 0x0080, + transferInt = 0x0100, /* Bus Master Transfer Complete */ + busMasterInProgress = 0x0800, + commandInProgress = 0x1000, + + interruptMask = 0x01FE, +}; + +#define COMMAND(port, cmd, a) outs((port)+Command, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatus) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromBusy = 0x8000, +}; + +#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a)) +#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy) +#define EEPROMDATA(port) ins((port)+EepromData) + +enum { /* Window 1 - operating set */ + Wop = 0x0001, + /* registers */ + Fifo = 0x0000, + RxError = 0x0004, /* 3C59[0257] only */ + RxStatus = 0x0008, + Timer = 0x000A, + TxStatus = 0x000B, + TxFree = 0x000C, + /* RxError bits */ + rxOverrun = 0x0001, + runtFrame = 0x0002, + alignmentError = 0x0004, /* Framing */ + crcError = 0x0008, + oversizedFrame = 0x0010, + dribbleBits = 0x0080, + /* RxStatus bits */ + rxBytes = 0x1FFF, /* 3C59[0257] mask */ + rxBytes9 = 0x07FF, /* 3C5[078]9 mask */ + rxError9 = 0x3800, /* 3C5[078]9 error mask */ + rxOverrun9 = 0x0000, + oversizedFrame9 = 0x0800, + dribbleBits9 = 0x1000, + runtFrame9 = 0x1800, + alignmentError9 = 0x2000, /* Framing */ + crcError9 = 0x2800, + rxError = 0x4000, + rxIncomplete = 0x8000, + /* TxStatus Bits */ + txStatusOverflow = 0x0004, + maxCollisions = 0x0008, + txUnderrun = 0x0010, + txJabber = 0x0020, + interruptRequested = 0x0040, + txStatusComplete = 0x0080, +}; + +enum { /* Window 2 - station address */ + Wstation = 0x0002, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + OtherInt = 0x0004, /* 3C59[0257] */ + RomControl = 0x0006, /* 3C509B, 3C59[27] */ + MacControl = 0x0006, /* 3C59[0257] */ + ResetOptions = 0x0008, /* 3C59[0257] */ + RxFree = 0x000A, + /* InternalConfig bits */ + xcvr10BaseT = 0x00000000, + xcvrAui = 0x00100000, /* 10BASE5 */ + xcvr10Base2 = 0x00300000, + xcvr100BaseTX = 0x00400000, + xcvr100BaseFX = 0x00500000, + xcvrMii = 0x00600000, + xcvrMask = 0x00700000, + autoSelect = 0x01000000, + /* MacControl bits */ + deferExtendEnable = 0x0001, + deferTimerSelect = 0x001E, /* mask */ + fullDuplexEnable = 0x0020, + allowLargePackets = 0x0040, + /* ResetOptions bits */ + baseT4Available = 0x0001, + baseTXAvailable = 0x0002, + baseFXAvailable = 0x0004, + base10TAvailable = 0x0008, + coaxAvailable = 0x0010, + auiAvailable = 0x0020, + miiAvailable = 0x0040, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + VcoDiagnostic = 0x0002, + FifoDiagnostic = 0x0004, + NetworkDiagnostic = 0x0006, + PhysicalMgmt = 0x0008, + MediaStatus = 0x000A, + BadSSD = 0x000C, + /* FifoDiagnostic bits */ + txOverrun = 0x0400, + rxUnderrun = 0x2000, + receiving = 0x8000, + /* MediaStatus bits */ + dataRate100 = 0x0002, + crcStripDisable = 0x0004, + enableSqeStats = 0x0008, + collisionDetect = 0x0010, + carrierSense = 0x0020, + jabberGuardEnable = 0x0040, + linkBeatEnable = 0x0080, + jabberDetect = 0x0200, + polarityReversed = 0x0400, + linkBeatDetect = 0x0800, + txInProg = 0x1000, + dcConverterEnabled = 0x4000, + auiDisable = 0x8000, +}; + +enum { /* Window 5 - internal state */ + Wstate = 0x0005, + /* registers */ + TxStartThresh = 0x0000, + TxAvalableThresh = 0x0002, + RxEarlyThresh = 0x0006, + RxFilter = 0x0008, + InterruptEnable = 0x000A, + IndicationEnable = 0x000C, +}; + +enum { /* Window 6 - statistics */ + Wstatistics = 0x0006, + /* registers */ + CarrierLost = 0x0000, + SqeErrors = 0x0001, + MultipleColls = 0x0002, + SingleCollFrames = 0x0003, + LateCollisions = 0x0004, + RxOverruns = 0x0005, + FramesXmittedOk = 0x0006, + FramesRcvdOk = 0x0007, + FramesDeferred = 0x0008, + UpperFramesOk = 0x0009, + BytesRcvdOk = 0x000A, + BytesXmittedOk = 0x000C, +}; + +enum { /* Window 7 - bus master operations */ + Wmaster = 0x0007, + /* registers */ + MasterAddress = 0x0000, + MasterLen = 0x0006, + MasterStatus = 0x000C, + /* MasterStatus bits */ + masterAbort = 0x0001, + targetAbort = 0x0002, + targetRetry = 0x0004, + targetDisc = 0x0008, + masterDownload = 0x1000, + masterUpload = 0x4000, + masterInProgress = 0x8000, + + masterMask = 0xD00F, +}; + +typedef struct { + Lock wlock; /* window access */ + + int attached; + int busmaster; + Block* rbp[2]; /* receive buffers */ + int rbpix; + + Block* txqhead; /* transmit queue */ + Block* txqtail; + int txthreshold; + int txbusy; + + long interrupts; /* statistics */ + long timer; + + long carrierlost; + long sqeerrors; + long multiplecolls; + long singlecollframes; + long latecollisions; + long rxoverruns; + long framesxmittedok; + long framesrcvdok; + long framesdeferred; + long bytesrcvdok; + long bytesxmittedok; + long badssd; + + int xcvr; /* transceiver type */ + int rxstatus9; /* old-style RxStatus register */ +} Ctlr; + +static Block* +allocrbp(void) +{ + Block *bp; + ulong addr; + + /* + * The receive buffers must be on a 32-byte + * boundary for EISA busmastering. + */ + bp = allocb(ROUNDUP(sizeof(Etherpkt), 4) + 31); + addr = (ulong)bp->base; + addr = ROUNDUP(addr, 32); + bp->rp = (uchar*)addr; + + return bp; +} + +static uchar* +startdma(Ether* ether, ulong address) +{ + int port, status, w; + uchar *wp; + + port = ether->port; + + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wmaster); + + wp = KADDR(inl(port+MasterAddress)); + status = ins(port+MasterStatus); + if(status & (masterInProgress|targetAbort|masterAbort)) + print("elnk3#%d: BM status 0x%uX\n", ether->ctlrno, status); + outs(port+MasterStatus, masterMask); + outl(port+MasterAddress, address); + outs(port+MasterLen, sizeof(Etherpkt)); + COMMAND(port, StartDma, Upload); + + COMMAND(port, SelectRegisterWindow, w); + return wp; +} + +static void +promiscuous(void* arg, int on) +{ + int filter, port; + + port = ((Ether*)arg)->port; + + filter = receiveBroadcast|receiveIndividual; + if(on) + filter |= receiveAllFrames; + COMMAND(port, SetRxFilter, filter); +} + +static void +attach(Ether* ether) +{ + int port, x; + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->wlock); + if(ctlr->attached){ + iunlock(&ctlr->wlock); + return; + } + + port = ether->port; + + /* + * Set the receiver packet filter for this and broadcast addresses, + * set the interrupt masks for all interrupts, enable the receiver + * and transmitter. + */ + promiscuous(ether, ether->prom); + + x = interruptMask|interruptLatch; + if(ctlr->busmaster) + x &= ~(rxEarly|rxComplete); + COMMAND(port, SetIndicationEnable, x); + COMMAND(port, SetInterruptEnable, x); + + COMMAND(port, RxEnable, 0); + COMMAND(port, TxEnable, 0); + + /* + * Prime the busmaster channel for receiving directly into a + * receive packet buffer if necessary. + */ + ctlr->rbpix = 0; + if(ctlr->busmaster) + startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp)); + + ctlr->attached = 1; + iunlock(&ctlr->wlock); +} + +static void +statistics(Ether* ether) +{ + int port, u, w; + Ctlr *ctlr; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * 3C59[27] require a read between a PIO write and + * reading a statistics register. + */ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wstatistics); + STATUS(port); + + ctlr->carrierlost += inb(port+CarrierLost) & 0xFF; + ctlr->sqeerrors += inb(port+SqeErrors) & 0xFF; + ctlr->multiplecolls += inb(port+MultipleColls) & 0xFF; + ctlr->singlecollframes += inb(port+SingleCollFrames) & 0xFF; + ctlr->latecollisions += inb(port+LateCollisions) & 0xFF; + ctlr->rxoverruns += inb(port+RxOverruns) & 0xFF; + ctlr->framesxmittedok += inb(port+FramesXmittedOk) & 0xFF; + ctlr->framesrcvdok += inb(port+FramesRcvdOk) & 0xFF; + u = inb(port+UpperFramesOk) & 0xFF; + ctlr->framesxmittedok += (u & 0x30)<<4; + ctlr->framesrcvdok += (u & 0x03)<<8; + ctlr->framesdeferred += inb(port+FramesDeferred) & 0xFF; + ctlr->bytesrcvdok += ins(port+BytesRcvdOk) & 0xFFFF; + ctlr->bytesxmittedok += ins(port+BytesXmittedOk) & 0xFFFF; + + if(ctlr->xcvr == xcvr100BaseTX || ctlr->xcvr == xcvr100BaseFX){ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + STATUS(port); + ctlr->badssd += inb(port+BadSSD); + } + + COMMAND(port, SelectRegisterWindow, w); +} + +static void +transmit(Ether* ether) +{ + int port, len; + Ctlr *ctlr; + Block *bp; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * Attempt to top-up the transmit FIFO. If there's room simply + * stuff in the packet length (unpadded to a dword boundary), the + * packet data (padded) and remove the packet from the queue. + * If there's no room post an interrupt for when there is. + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr->wlock already locked + * and the correct register window (Wop) in place. + */ + while(bp = ctlr->txqhead){ + len = ROUNDUP(BLEN(bp), 4); + if(len+4 <= ins(port+TxFree)){ + outl(port+Fifo, BLEN(bp)); + outsl(port+Fifo, bp->rp, len/4); + + ctlr->txqhead = bp->next; + freeb(bp); + + ether->outpackets++; + } + else if(ctlr->txbusy == 0){ + ctlr->txbusy = 1; + COMMAND(port, SetTxAvailableThresh, len); + return; + } + } +} + +static long +write(Ether* ether, void* buf, long n) +{ + Ctlr *ctlr; + Block *bp; + int port, w; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * Pack the write request up in a buffer, give it a source address + * and place it on the end of the transmit queue. The data written to the + * FIFO must be padded to a dword boundary, hence the ROUNDUP allocation. + * Call transmit() to stuff it into the TxFIFO if possible. + */ + bp = allocb(ROUNDUP(n, 4)); + memmove(bp->wp, buf, n); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += n; + + ilock(&ctlr->wlock); + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->txqhead == 0) + ctlr->txqhead = bp; + else + ctlr->txqtail->next = bp; + ctlr->txqtail = bp; + if(ctlr->txbusy == 0) + transmit(ether); + COMMAND(port, SelectRegisterWindow, w); + iunlock(&ctlr->wlock); + + return n; +} + +static void +receive(Ether* ether) +{ + int len, port, rxerror, rxstatus; + Ctlr *ctlr; + Block *bp; + + port = ether->port; + ctlr = ether->ctlr; + + while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ + if(ctlr->busmaster && (STATUS(port) & busMasterInProgress)) + break; + + /* + * If there was an error, log it and continue. + * Unfortunately the 3C5[078]9 has the error info in the status register + * and the 3C59[0257] implement a separate RxError register. + */ + if(rxstatus & rxError){ + if(ctlr->rxstatus9){ +iprint("R%uX|", rxstatus); + switch(rxstatus & rxError9){ + + case rxOverrun9: + ether->overflows++; + break; + + case oversizedFrame9: + case runtFrame9: + ether->buffs++; + break; + + case alignmentError9: + ether->frames++; + break; + + case crcError9: + ether->crcs++; + break; + + } + } + else{ + rxerror = inb(port+RxError); +iprint("R%uX|", rxerror); + if(rxerror & rxOverrun) + ether->overflows++; + if(rxerror & (oversizedFrame|runtFrame)) + ether->buffs++; + if(rxerror & alignmentError) + ether->frames++; + if(rxerror & crcError) + ether->crcs++; + } + + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster) + startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp)); + } + else{ + ether->inpackets++; + bp = ctlr->rbp[ctlr->rbpix]; + + if(ctlr->busmaster == 0){ + len = (rxstatus & rxBytes9); + bp->wp = bp->rp + len; + insl(port+Fifo, bp->rp, HOWMANY(len, 4)); + } + + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster){ + ctlr->rbpix ^= 1; + bp->wp = startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp)); + } + + etherrloop(ether, (Etherpkt*)bp->rp, BLEN(bp)); + } + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, status, txstatus, w, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + lock(&ctlr->wlock); + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + + ctlr->interrupts++; + ctlr->timer += inb(port+Timer) & 0xFF; + for(;;){ + /* + * Clear the interrupt latch. + * It's possible to receive a packet and for another + * to become complete before exiting the interrupt + * handler so this must be done first to ensure another + * interrupt will occur. + */ + COMMAND(port, AcknowledgeInterrupt, interruptLatch); + status = STATUS(port); + if((status & interruptMask) == 0) + break; + + if(status & hostError){ + /* + * Adapter failure, try to find out why, reset if + * necessary. What happens if Tx is active and a reset + * occurs, need to retransmit? This probably isn't right. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+FifoDiagnostic); + COMMAND(port, SelectRegisterWindow, Wop); + print("elnk3#%d: status 0x%uX, diag 0x%uX\n", + ether->ctlrno, status, x); + + if(x & txOverrun){ + COMMAND(port, TxReset, 0); + COMMAND(port, TxEnable, 0); + wakeup(ðer->tr); + } + + if(x & rxUnderrun){ + /* + * This shouldn't happen... + * Need to restart any busmastering? + */ + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxEnable, 0); + } + + status &= ~hostError; + } + + if(status & (transferInt|rxComplete)){ + receive(ether); + status &= ~(transferInt|rxComplete); + } + + if(status & txComplete){ + /* + * Pop the TxStatus stack, accumulating errors. + * Adjust the TX start threshold if there was an underrun. + * If there was a Jabber or Underrun error, reset + * the transmitter. + * For all conditions enable the transmitter. + */ + txstatus = 0; + do{ + if(x = inb(port+TxStatus)) + outb(port+TxStatus, 0); + txstatus |= x; + }while(STATUS(port) & txComplete); + + if(txstatus & txUnderrun){ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + while(ins(port+MediaStatus) & txInProg) + ; + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->txthreshold < ETHERMAXTU) + ctlr->txthreshold += ETHERMINTU; + } + + if(txstatus & (txJabber|txUnderrun)){ + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold); + } + COMMAND(port, TxEnable, 0); + ether->oerrs++; + status &= ~txComplete; + status |= txAvailable; + } + + if(status & txAvailable){ + COMMAND(port, AcknowledgeInterrupt, txAvailable); + ctlr->txbusy = 0; + transmit(ether); + status &= ~txAvailable; + } + + if(status & updateStats){ + statistics(ether); + status &= ~updateStats; + } + + /* + * Panic if there are any interrupts not dealt with. + */ + if(status & interruptMask) + panic("elnk3#%d: interrupt mask 0x%uX\n", ether->ctlrno, status); + } + + COMMAND(port, SelectRegisterWindow, w); + unlock(&ctlr->wlock); +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char buf[512]; + int len; + + if(n == 0) + return 0; + + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + statistics(ether); + iunlock(&ctlr->wlock); + + len = sprint(buf, "interrupts: %ld\n", ctlr->interrupts); + len += sprint(buf+len, "timer: %ld\n", ctlr->timer); + len += sprint(buf+len, "carrierlost: %ld\n", ctlr->carrierlost); + len += sprint(buf+len, "sqeerrors: %ld\n", ctlr->sqeerrors); + len += sprint(buf+len, "multiplecolls: %ld\n", ctlr->multiplecolls); + len += sprint(buf+len, "singlecollframes: %ld\n", ctlr->singlecollframes); + len += sprint(buf+len, "latecollisions: %ld\n", ctlr->latecollisions); + len += sprint(buf+len, "rxoverruns: %ld\n", ctlr->rxoverruns); + len += sprint(buf+len, "framesxmittedok: %ld\n", ctlr->framesxmittedok); + len += sprint(buf+len, "framesrcvdok: %ld\n", ctlr->framesrcvdok); + len += sprint(buf+len, "framesdeferred: %ld\n", ctlr->framesdeferred); + len += sprint(buf+len, "bytesrcvdok: %ld\n", ctlr->bytesrcvdok); + len += sprint(buf+len, "bytesxmittedok: %ld\n", ctlr->bytesxmittedok); + sprint(buf+len, "badssd: %ld\n", ctlr->badssd); + + return readstr(offset, a, n, buf); +} + +typedef struct Adapter Adapter; +struct Adapter { + Adapter* next; + int port; + int irq; +}; +static Adapter *adapter; + +/* + * 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. + */ +static void +idseq(void) +{ + int i; + uchar al; + static int reset, untag; + + /* + * One time only: + * reset any adapters listening + */ + if(reset == 0){ + outb(IDport, 0); + outb(IDport, 0); + outb(IDport, 0xC0); + delay(20); + reset = 1; + } + + outb(IDport, 0); + outb(IDport, 0); + for(al = 0xFF, i = 0; i < 255; i++){ + outb(IDport, al); + if(al & 0x80){ + al <<= 1; + al ^= 0xCF; + } + else + al <<= 1; + } + + /* + * One time only: + * write ID sequence to get the attention of all adapters; + * untag all adapters. + * If we do a global reset here on all adapters we'll confuse any + * ISA cards configured for EISA mode. + */ + if(untag == 0){ + outb(IDport, 0xD0); + untag = 1; + } +} + +static ulong +activate(void) +{ + int i; + ushort x, acr; + + /* + * Do the little configuration dance: + * + * 2. write the ID sequence to get to command state. + */ + idseq(); + + /* + * 3. Read the Manufacturer ID from the EEPROM. + * This is done by writing the IDPort with 0x87 (0x80 + * is the 'read EEPROM' command, 0x07 is the offset of + * the Manufacturer ID field in the EEPROM). + * The data comes back 1 bit at a time. + * We seem to need a delay here between reading the bits. + * + * If the ID doesn't match, there are no more adapters. + */ + outb(IDport, 0x87); + delay(20); + for(x = 0, i = 0; i < 16; i++){ + delay(20); + x <<= 1; + x |= inb(IDport) & 0x01; + } + if(x != 0x6D50) + return 0; + + /* + * 3. Read the Address Configuration from the EEPROM. + * The Address Configuration field is at offset 0x08 in the EEPROM). + */ + outb(IDport, 0x88); + for(acr = 0, i = 0; i < 16; i++){ + delay(20); + acr <<= 1; + acr |= inb(IDport) & 0x01; + } + + return (acr & 0x1F)*0x10 + 0x200; +} + +static ulong +tcm509isa(Ether* ether) +{ + int irq, port; + Adapter *ap; + + /* + * Attempt to activate adapters until one matches the + * address criteria. If adapter is set for EISA mode (0x3F0), + * tag it and ignore. Otherwise, activate it fully. + */ + while(port = activate()){ + /* + * 6. Tag the adapter so it won't respond in future. + */ + outb(IDport, 0xD1); + if(port == 0x3F0) + continue; + + /* + * 6. Activate the adapter by writing the Activate command + * (0xFF). + */ + outb(IDport, 0xFF); + delay(20); + + /* + * 8. Can now talk to the adapter's I/O base addresses. + * Use the I/O base address from the acr just read. + * + * Enable the adapter and clear out any lingering status + * and interrupts. + */ + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + COMMAND(port, TxReset, 0); + COMMAND(port, RxReset, 0); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + if(ether->port == 0 || ether->port == port){ + ether->irq = irq; + return port; + } + + ap = malloc(sizeof(Adapter)); + ap->port = port; + ap->irq = irq; + ap->next = adapter; + adapter = ap; + } + + return 0; +} + +static int +tcm5XXeisa(Ether* ether) +{ + static int slot = 1; + ushort x; + int irq, port; + Adapter *ap; + + /* + * First time through, check if this is an EISA machine. + * If not, nothing to do. + */ + if(slot == 1 && strncmp((char*)(KZERO|0xFFFD9), "EISA", 4)) + return 0; + + /* + * Continue through the EISA slots looking for a match on both + * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product. + * If we find an adapter, select window 0, enable it and clear + * out any lingering status and interrupts. + */ + while(slot < MaxEISA){ + port = slot++*0x1000; + if(ins(port+0xC80+ManufacturerID) != 0x6D50) + continue; + x = ins(port+0xC80+ProductID); + if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900) + continue; + + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + COMMAND(port, TxReset, 0); + COMMAND(port, RxReset, 0); + COMMAND(port, AcknowledgeInterrupt, 0xFF); + + irq = (ins(port+ResourceConfig)>>12) & 0x0F; + if(ether->port == 0 || ether->port == port){ + ether->irq = irq; + return port; + } + + ap = malloc(sizeof(Adapter)); + ap->port = port; + ap->irq = irq; + ap->next = adapter; + adapter = ap; + } + + return 0; +} + +static int +tcm59Xpci(Ether* ether) +{ + PCIcfg pcicfg; + static int devno = 0; + int irq, port; + Adapter *ap; + + for(;;){ + pcicfg.vid = 0x10B7; + pcicfg.did = 0; + if((devno = pcimatch(0, devno, &pcicfg)) == -1) + break; + port = pcicfg.baseaddr[0] & ~0x01; + COMMAND(port, GlobalReset, 0); + while(STATUS(port) & commandInProgress) + ; + irq = pcicfg.irq; + if(ether->port == 0 || ether->port == port){ + ether->irq = irq; + return port; + } + + ap = malloc(sizeof(Adapter)); + ap->port = port; + ap->irq = irq; + ap->next = adapter; + adapter = ap; + } + + return 0; +} + +static int +tcm5XXpcmcia(Ether* ether) +{ + if(cistrcmp(ether->type, "3C589") == 0) + return ether->port; + + return 0; +} + +int +etherelnk3reset(Ether* ether) +{ + int busmaster, i, port, rxstatus9, x, xcvr; + 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 an adapter that fits the bill has + * already been found. If not, scan for adapter on PCI, EISA and finally + * using the little ISA configuration dance. The EISA and ISA scan + * routines leave Wsetup mapped. + * If an adapter is found save the IRQ and transceiver type. + */ + port = 0; + rxstatus9 = 0; + xcvr = 0; + for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){ + if(ether->port == 0 || ether->port == ap->port){ + port = ap->port; + ether->irq = ap->irq; + *app = ap->next; + free(ap); + break; + } + } + if(port == 0 && (port = tcm5XXpcmcia(ether))){ + xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20; + rxstatus9 = 1; + } + else if(port == 0 && (port = tcm59Xpci(ether))){ + COMMAND(port, SelectRegisterWindow, Wfifo); + xcvr = inl(port+InternalConfig) & xcvrMask; + } + else if(port == 0 && (port = tcm5XXeisa(ether))){ + x = ins(port+ProductID); + if((x & 0xFF00) == 0x5900){ + COMMAND(port, SelectRegisterWindow, Wfifo); + xcvr = inl(port+InternalConfig) & xcvrMask; + } + else{ + xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20; + rxstatus9 = 1; + } + } + else if(port == 0 && (port = tcm509isa(ether))){ + xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20; + rxstatus9 = 1; + } + + if(port == 0) + return -1; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to loading the + * station address in Wstation. The EEPROM returns 16-bits at a time. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + COMMAND(port, SelectRegisterWindow, Wsetup); + while(EEPROMBUSY(port)) + ; + for(i = 0; i < Eaddrlen/2; i++){ + EEPROMCMD(port, EepromReadRegister, i); + while(EEPROMBUSY(port)) + ; + x = EEPROMDATA(port); + ether->ea[2*i] = (x>>8) & 0xFF; + ether->ea[2*i+1] = x & 0xFF; + } + } + + COMMAND(port, SelectRegisterWindow, Wstation); + for(i = 0; i < Eaddrlen; i++) + outb(port+i, ether->ea[i]); + + /* + * Enable the transceiver if necessary and determine whether + * busmastering can be used. Due to bugs in the first revision + * of the 3C59[05], don't use busmastering at 10Mbps. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); + outs(port+MediaStatus, x); + if(x & dataRate100) + busmaster = 1; + else + busmaster = 0; + switch(xcvr){ + + case xcvr100BaseTX: + case xcvr100BaseFX: + ether->mbps = 100; + /*FALLTHROUGH*/ + case xcvr10BaseT: + /* + * Enable Link Beat and Jabber to start the + * transceiver. + */ + x |= linkBeatEnable|jabberGuardEnable; + outs(port+MediaStatus, x); + break; + + case xcvr10Base2: + /* + * Start the DC-DC converter. + * Wait > 800 microseconds. + */ + COMMAND(port, EnableDcConverter, 0); + delay(1); + break; + } + + /* + * Wop is the normal operating register set. + * The 3C59[0257] adapters allow access to more than one register window + * at a time, but there are situations where switching still needs to be + * done, so just do it. + * Clear out any lingering Tx status. + */ + COMMAND(port, SelectRegisterWindow, Wop); + while(inb(port+TxStatus)) + outb(port+TxStatus, 0); + + /* + * Allocate a controller structure, clear out the + * adapter statistics, clear the statistics logged into ctlr + * and enable statistics collection. Xcvr is needed in order + * to collect the BadSSD statistics. + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + ctlr->xcvr = xcvr; + statistics(ether); + memset(ctlr, 0, sizeof(Ctlr)); + + ctlr->busmaster = busmaster; + ctlr->xcvr = xcvr; + ctlr->rxstatus9 = rxstatus9; + + COMMAND(port, StatisticsEnable, 0); + + /* + * Allocate the receive buffers. + */ + ctlr->rbpix = 0; + ctlr->rbp[0] = allocrbp(); + if(ctlr->busmaster) + ctlr->rbp[1] = allocrbp(); + + /* + * Set a base TxStartThresh which will be incremented + * if any txUnderrun errors occur and ensure no RxEarly + * interrupts happen. + */ + ctlr->txthreshold = ETHERMINTU; + COMMAND(port, SetTxStartThresh, ETHERMINTU); + COMMAND(port, SetRxEarlyThresh, ETHERMAXTU); + + iunlock(&ctlr->wlock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->port = port; + ether->attach = attach; + ether->write = write; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->promiscuous = promiscuous; + ether->arg = ether; + + return 0; +} + +void +etherelnk3link(void) +{ + addethercard("elnk3", etherelnk3reset); + addethercard("3C509", etherelnk3reset); +} diff --git a/pc/etherif.h b/pc/etherif.h index f58d0151f6aa77f307287de11e890cddc338bd91..39aaafacd6f73416e3917fcad458607f13e522bf 100644 --- a/pc/etherif.h +++ b/pc/etherif.h @@ -1,5 +1,5 @@ enum { - MaxEther = 8, + MaxEther = 24, Ntypes = 8, }; @@ -12,6 +12,7 @@ struct Ether { void (*attach)(Ether*); /* filled in by reset routine */ long (*write)(Ether*, void*, long); void (*interrupt)(Ureg*, void*); + long (*ifstat)(Ether*, void*, long, ulong); void *ctlr; Etherpkt tpkt; /* transmit buffer */ diff --git a/pc/fns.h b/pc/fns.h index df22057800e5e03a6b2f12d11241c423251b6d63..4816e6f483f420e027a747e713ed1dab608a6f5b 100644 --- a/pc/fns.h +++ b/pc/fns.h @@ -129,7 +129,3 @@ ulong ifunroute(ulong); void parseip(char*, char*); #define dcflush(a, b) - -int scsibtissue(int, int, uchar*, int, uchar*, int); -Scsiio scsibt24(int, int, ISAConf*); -Scsiio scsibt32(int, int, ISAConf*); diff --git a/pc/scsi.c b/pc/scsi.c index 564e40d9d9490d4088a5de01d5cc50258dbd1947..036606dcf6ebc0bf6998a848299ff995dd640e55 100644 --- a/pc/scsi.c +++ b/pc/scsi.c @@ -78,7 +78,7 @@ scsireset(void) if((ctlr->io = (*lp->reset)(ctlrno, ctlr)) == 0) break; - print("scsi%d: %s: port 0x%luX irq %d", + print("scsi#%d: %s: port 0x%luX irq %d", ctlrno, ctlr->type, ctlr->port, ctlr->irq, ctlr->mem, ctlr->size); if(ctlr->mem) @@ -106,7 +106,29 @@ scsireset(void) int scsiexec(Target *t, int rw, uchar *cmd, int cbytes, void *data, int *dbytes) { - return (*scsi[t->ctlrno]->io)(t, rw, cmd, cbytes, data, dbytes); + int s; + + /* + * Call the device-specific I/O routine. + * There should be no calls to 'error()' below this + * which percolate back up. + */ + switch(s = (*scsi[t->ctlrno]->io)(t, rw, cmd, cbytes, data, dbytes)){ + + default: + /* + * It's more complicated than this. There are conditions which + * are 'ok' but for which the returned status code is not 'STok'. + * Also, not all conditions require a reqsense, there may be a + * need to do a reqsense here when necessary and making it + * available to the caller somehow. + * + * Later. + */ + break; + } + + return s; } Target* @@ -143,7 +165,7 @@ scsiprobe(Ctlr *ctlr) nbytes = Nscratch; s = scsireqsense(t, 0, t->scratch, &nbytes, 0); if(s != STok){ - print("scsi%d: unit %d unavailable, status %d\n", t->ctlrno, i, s); + print("scsi#%d: unit %d unavailable, status %d\n", t->ctlrno, i, s); continue; } @@ -154,11 +176,11 @@ scsiprobe(Ctlr *ctlr) memset(t->inq, 0, Ninq); nbytes = Ninq; s = scsiinquiry(t, 0, t->inq, &nbytes); - if(s < 0) { - print("scsi%d: unit %d inquire failed, status %d\n", t->ctlrno, i, s); + if(s != STok) { + print("scsi#%d: unit %d inquire failed, status %d\n", t->ctlrno, i, s); continue; } - print("scsi%d: unit %d: %s\n", t->ctlrno, i, t->inq+8); + print("scsi#%d: unit %d: %s\n", t->ctlrno, i, t->inq+8); t->ok = 1; } } @@ -264,7 +286,7 @@ scsicap(Target *t, char lun, ulong *size, ulong *bsize) nbytes = 8; d = scsialloc(nbytes); if(d == 0) - error(Enomem); + return -1; s = scsiexec(t, SCSIread, cmd, sizeof(cmd), d, &nbytes); if(s == STok) { @@ -302,10 +324,10 @@ scsibio(Target *t, char lun, int dir, void *b, long n, long bsize, long bno) } nbytes = n*bsize; s = scsiexec(t, dir, cmd, cdbsiz, b, &nbytes); - if(s < 0) { + if(s != STok) { nbytes = Nscratch; scsireqsense(t, lun, t->scratch, &nbytes, 0); - return -1; + return scsierrstr(s); } return nbytes; } @@ -391,7 +413,7 @@ scsireqsense(Target *t, char lun, void *data, int *nbytes, int quiet) buggery: if(quiet == 0){ s = key[sense[2]&0x0F]; - print("scsi%d: unit %d reqsense: '%s' code #%2.2ux #%2.2ux\n", + print("scsi#%d: unit %d reqsense: '%s' code #%2.2ux #%2.2ux\n", t->ctlrno, t->target, s, sense[12], sense[13]); } free(sense); @@ -414,7 +436,7 @@ scsidiskinfo(Target *t, char lun, int track, uchar *data) d = scsialloc(nbytes); if(d == 0) - error(Enomem); + return scsierrstr(STnomem); memset(d, 0, nbytes); s = scsiexec(t, SCSIread, cmd, sizeof(cmd), d, &nbytes); @@ -439,7 +461,7 @@ scsitrackinfo(Target *t, char lun, int track, uchar *data) d = scsialloc(nbytes); if(d == 0) - error(Enomem); + return scsierrstr(STnomem); memset(d, 0, nbytes); s = scsiexec(t, SCSIread, cmd, sizeof(cmd), d, &nbytes); @@ -465,7 +487,7 @@ scsibufsize(Target *t, char lun, int size) d = scsialloc(nbytes); if(d == 0) - error(Enomem); + return scsierrstr(STnomem); memset(d, 0, nbytes); d[3] = 8; @@ -491,10 +513,65 @@ scsireadcdda(Target *t, char lun, int, void *b, long n, long bsize, long bno) nbytes = n*bsize; s = scsiexec(t, SCSIread, cmd, sizeof(cmd), b, &nbytes); - if(s < 0) { + if(s != STok) { nbytes = Nscratch; scsireqsense(t, lun, t->scratch, &nbytes, 0); - return -1; + return scsierrstr(s); } return nbytes; } + +int +scsierrstr(int errno) +{ + char *p; + + switch(errno){ + case STnomem: + p = Enomem; + break; + case STtimeout: + p = "bus timeout"; + break; + case STownid: + p = "playing with myself"; + break; + case STharderr: + p = Eio; + break; + case STok: + p = Enoerror; + break; + case STcheck: + p = "check condition"; + break; + case STcondmet: + p = "condition met/good"; + break; + case STbusy: + p = "busy"; + break; + case STintok: + p = "intermediate/good"; + break; + case STintcondmet: + p = "intermediate/condition met/good"; + break; + case STresconf: + p = "reservation conflict"; + break; + case STterminated: + p = "command terminated"; + break; + case STqfull: + p = "queue full"; + break; + + default: + p = "unknown SCSI error"; + break; + } + strncpy(up->error, p, NAMELEN); + + return -1; +} diff --git a/port/devsd.c b/port/devsd.c index 85d266c69c7f27b4733b0bb2d408483bda716112..9aa663300b022135b814deea5b127d947b9bc229 100644 --- a/port/devsd.c +++ b/port/devsd.c @@ -47,6 +47,8 @@ struct Disk char vol[NAMELEN]; uchar* inquire; + int partok; + ulong version; ulong size; ulong bsize; @@ -57,7 +59,7 @@ struct Disk int ndisk; Disk disk[Ndisk]; -static void sdrdpart(Disk*); +static int sdrdpart(Disk*); static long sdio(Chan*, int, char*, ulong, ulong); static int types[] = @@ -137,9 +139,6 @@ sdinit(void) * their act together after a reset. * If 'not ready' comes back, try starting a TypeDA and punt * the get capacity until the drive is attached. - * It might be possible to be smarter here, and look at the - * response from a test-unit-ready which would show if the - * target was in the process of becoming ready. */ if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok) { nbytes = sizeof(scratch); @@ -187,8 +186,12 @@ sdattach(char *spec) { int i; - for(i = 0; i < ndisk; i++) - sdrdpart(&disk[i]); + for(i = 0; i < ndisk; i++){ + qlock(&disk[i]); + if(disk[i].partok == 0) + sdrdpart(&disk[i]); + qunlock(&disk[i]); + } return devattach('w', spec); } @@ -246,8 +249,12 @@ sdclose(Chan *c) d = &disk[DRIVE(c->qid)]; p = &d->table[PART(c->qid)]; - if((c->mode&3) != OREAD && strcmp(p->name, "partition") == 0) + if((c->mode&3) != OREAD && strcmp(p->name, "partition") == 0){ + qlock(d); + d->partok = 0; sdrdpart(d); + qunlock(d); + } } long @@ -268,6 +275,12 @@ sdbread(Chan *c, long n, ulong offset) long sdwrite(Chan *c, char *a, long n, ulong offset) { + Disk *d; + + d = &disk[DRIVE(c->qid)]; + + if((d->inquire[0] & 0x1F) == TypeCD) + error(Eperm); return sdio(c, 1, a, n, offset); } @@ -277,7 +290,7 @@ sdbwrite(Chan *c, Block *bp, ulong offset) return devbwrite(c, bp, offset); } -static void +static int sdrdpart(Disk *d) { Part *p; @@ -285,30 +298,36 @@ sdrdpart(Disk *d) char *b, *line[Npart+2], *field[3]; static char MAGIC[] = "plan9 partitions"; + if(d->partok) + return STok; + + p = d->table; + strcpy(p->name, "disk"); + p->beg = 0; + p->end = 0; + d->npart = 1; + /* - * If the drive wasn't ready when we tried to do a - * read-capacity earlier (in sdinit()), try again. - * It might be possible to be smarter here, and look at the - * response from a test-unit-ready which would show if the - * target was in the process of becoming ready. + * If the drive wasn't ready when a read-capacity was tried + * earlier (in sdinit()), try again. */ if(d->size == 0 || d->bsize == 0){ - if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok){ + n = scsicap(d->t, d->lun, &d->size, &d->bsize); + if(n != STok){ d->size = d->bsize = 0; - error(Eio); + return scsierrstr(n); } } + p->end = d->size + 1; + + if((d->inquire[0] & 0x1F) == TypeCD) + return STok; + b = scsialloc(d->bsize); if(b == 0) - error(Enomem); - qlock(d); - - p = d->table; + return scsierrstr(STnomem); - strcpy(p->name, "disk"); - p->beg = 0; - p->end = d->size + 1; p++; strcpy(p->name, "partition"); p->beg = d->table[0].end - 1; @@ -316,12 +335,6 @@ sdrdpart(Disk *d) p++; d->npart = 2; - if((d->inquire[0] & 0x1F) == TypeCD){ - scsifree(b); - qunlock(d); - return; - } - scsibio(d->t, d->lun, SCSIread, b, 1, d->bsize, d->table[0].end-1); b[d->bsize-1] = '\0'; @@ -331,26 +344,24 @@ sdrdpart(Disk *d) n = parsefields(b, line, Npart+2, "\n"); if(n > 0 && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0) { for(i = 1; i < n; i++) { - switch(parsefields(line[i], field, 3, " ")) { - case 2: - if(strcmp(field[0], "unit") == 0) - strncpy(d->vol, field[1], NAMELEN); - break; - case 3: - if(p >= &d->table[Npart]) - break; - strncpy(p->name, field[0], NAMELEN); - p->beg = strtoul(field[1], 0, 0); - p->end = strtoul(field[2], 0, 0); - if(p->beg > p->end || p->beg >= d->table[0].end) - break; - p++; - } + if(parsefields(line[i], field, 3, " ") != 3) + break; + if(p >= &d->table[Npart]) + break; + strncpy(p->name, field[0], NAMELEN); + p->beg = strtoul(field[1], 0, 0); + p->end = strtoul(field[2], 0, 0); + if(p->beg > p->end || p->beg >= d->table[0].end) + break; + p++; } } d->npart = p - d->table; scsifree(b); - qunlock(d); + + d->partok = 1; + + return STok; } static long @@ -364,9 +375,6 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset) d = &disk[DRIVE(c->qid)]; p = &d->table[PART(c->qid)]; - if(write && (d->inquire[0] & 0x1F) == TypeCD) - error(Eperm); - block = (offset / d->bsize) + p->beg; n = (offset + len + d->bsize - 1) / d->bsize + p->beg - block; max = SCSImaxxfer / d->bsize; @@ -379,15 +387,16 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset) b = scsialloc(n*d->bsize); if(b == 0) - error(Enomem); - if(waserror()) { - scsifree(b); - nexterror(); - } + return scsierrstr(STnomem); + offset %= d->bsize; if(write) { if(offset || len % d->bsize) { x = scsibio(d->t, d->lun, SCSIread, b, n, d->bsize, block); + if(x < 0){ + len = -1; + goto buggery; + } if(x < n * d->bsize) { n = x / d->bsize; x = n * d->bsize - offset; @@ -397,6 +406,10 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset) } memmove(b + offset, a, len); x = scsibio(d->t, d->lun, SCSIwrite, b, n, d->bsize, block); + if(x < 0){ + len = -1; + goto buggery; + } if(x < offset) len = 0; else @@ -405,6 +418,10 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset) } else { x = scsibio(d->t, d->lun, SCSIread, b, n, d->bsize, block); + if(x < 0){ + len = -1; + goto buggery; + } if(x < offset) len = 0; else @@ -412,7 +429,8 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset) len = x - offset; memmove(a, b+offset, len); } - poperror(); + +buggery: scsifree(b); return len; } diff --git a/port/portdat.h b/port/portdat.h index 5d4251aa80999ee0c10e9ac48c33e9a31ee1405f..a0172938cc8905c7a6d8c86aec803d20c1efa5e1 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -670,19 +670,19 @@ enum /* Things scsi */ enum { - STtimeout =-3, - STownid =-2, - STharderr =-1, /* Command status error returns */ - STok = 0, - STcheck = 2, - STcondmet = 4, - STbusy = 8, - STintok = 16, - STconflict = 18, - STintcondmet = 20, - STterminated = 22, - STresconf = 24, - STqfull = 28, + STnomem =-4, /* buffer allocation failed */ + STtimeout =-3, /* bus timeout */ + STownid =-2, /* playing with myself */ + STharderr =-1, /* controller error of some kind */ + STok = 0, /* good */ + STcheck = 0x02, /* check condition */ + STcondmet = 0x04, /* condition met/good */ + STbusy = 0x08, /* busy */ + STintok = 0x10, /* intermediate/good */ + STintcondmet = 0x14, /* intermediate/condition met/good */ + STresconf = 0x18, /* reservation conflict */ + STterminated = 0x22, /* command terminated */ + STqfull = 0x28, /* queue full */ SCSIread = 0, SCSIwrite, diff --git a/port/portfns.h b/port/portfns.h index ba54139ca5fcd2109368fba77e1cc8e32e2ed737..20dddfdc9b0b072340f79b298ec656603aa50475 100644 --- a/port/portfns.h +++ b/port/portfns.h @@ -247,6 +247,7 @@ int screenbits(void); #define scsialloc(n) mallocz((n)+512, 0) int scsibio(Target*, char, int, void*, long, long, long); int scsicap(Target*, char, ulong*, ulong*); +int scsierrstr(int); int scsiexec(Target*, int, uchar*, int, void*, int*); #define scsifree(p) free(p) int scsiinquiry(Target*, char, void*, int*);