## diffname pc/etherelnk3.c 1996/0607 ## diff -e /dev/null /n/fornaxdump/1996/0607/sys/src/brazil/pc/etherelnk3.c 0a /* * 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); } . ## diffname pc/etherelnk3.c 1996/0608 ## diff -e /n/fornaxdump/1996/0607/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0608/sys/src/brazil/pc/etherelnk3.c 591d 567d ## diffname pc/etherelnk3.c 1996/0612 ## diff -e /n/fornaxdump/1996/0608/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0612/sys/src/brazil/pc/etherelnk3.c 411a x &= ~rxEarly; . ## diffname pc/etherelnk3.c 1996/0613 ## diff -e /n/fornaxdump/1996/0612/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0613/sys/src/brazil/pc/etherelnk3.c 1210c COMMAND(port, SetRxEarlyThresh, rxearly); . 1087a rxearly = 8188; . 1081a rxearly = 8188; . 1064a rxearly = 2044; . 1051c int busmaster, i, port, rxearly, rxstatus9, x, xcvr; . 1042c if(cistrcmp(ether->type, "3C589") == 0 || cistrcmp(ether->type, "3C562") == 0) . 747a * Currently, this shouldn't happen. */ if(status & rxEarly){ COMMAND(port, AcknowledgeInterrupt, rxEarly); status &= ~rxEarly; } /* . 412d 4a * RxEarly and busmaster; . ## diffname pc/etherelnk3.c 1996/0925 ## diff -e /n/fornaxdump/1996/0613/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0925/sys/src/brazil/pc/etherelnk3.c 1220,1221c COMMAND(port, SetTxStartThresh, ETHERMINTU>>ctlr->ts); COMMAND(port, SetRxEarlyThresh, rxearly>>ctlr->ts); . 1202a if(rxearly >= 2048) ctlr->ts = 2; . 727c COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); . 504c COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); . 330a int ts; /* threshold shift */ . ## diffname pc/etherelnk3.c 1996/0928 ## diff -e /n/fornaxdump/1996/0925/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0928/sys/src/brazil/pc/etherelnk3.c 1222,1223c ctlr->txthreshold = ETHERMINTU*2; COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); . 725c COMMAND(port, TxReset, dmaReset); . ## diffname pc/etherelnk3.c 1996/0929 ## diff -e /n/fornaxdump/1996/0928/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0929/sys/src/brazil/pc/etherelnk3.c 1143a if(xcvr & autoSelect) xcvr = autoselect(port, rxstatus9); . 1130,1131c ether->ea[2*i] = x>>8; ether->ea[2*i+1] = x; . 1108c x = ins(port+AddressConfig); xcvr = ((x & xcvrMask9)>>14)<<20; if(x & autoSelect9) xcvr |= autoSelect; . 1103c x = ins(port+AddressConfig); xcvr = ((x & xcvrMask9)>>14)<<20; if(x & autoSelect9) xcvr |= autoSelect; . 1100c xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); . 1093c xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); . 1056a static int autoselect(int port, int rxstatus9) { int media, x; /* * Pathetic attempt at automatic media selection. * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX * cards operational. */ media = auiAvailable|coaxAvailable|base10TAvailable; if(rxstatus9 == 0){ COMMAND(port, SelectRegisterWindow, Wfifo); media = ins(port+ResetOptions); } COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~(dcConverterEnabled|linkBeatEnable|jabberGuardEnable); outs(port+MediaStatus, x); if(media & baseTXAvailable){ /* * Must have InternalConfig register. */ COMMAND(port, SelectRegisterWindow, Wfifo); x = inl(port+InternalConfig) & ~xcvrMask; x |= xcvr100BaseTX; outl(port+InternalConfig, x); COMMAND(port, TxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, RxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus); outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); delay(1); if(ins(port+MediaStatus) & linkBeatDetect) return xcvr100BaseTX; outs(port+MediaStatus, x); } if(media & base10TAvailable){ if(rxstatus9 == 0){ COMMAND(port, SelectRegisterWindow, Wfifo); x = inl(port+InternalConfig) & ~xcvrMask; x |= xcvr10BaseT; outl(port+InternalConfig, x); } else{ COMMAND(port, SelectRegisterWindow, Wsetup); x = ins(port+AddressConfig) & ~xcvrMask9; x |= (xcvr10BaseT>>20)<<14; outs(port+AddressConfig, x); } COMMAND(port, TxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, RxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus); outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); delay(1); if(ins(port+MediaStatus) & linkBeatDetect) return xcvr10BaseT; outs(port+MediaStatus, x); } /* * Botch. */ return autoSelect; } . 981c * If an adapter is found, select window 0, enable it and clear . 877c * A delay seems necessary between reading the bits. . 849,850c * If a global reset is done here on all adapters it will confuse * any ISA cards configured for EISA mode. . 725c if(ctlr->busmaster == 0) COMMAND(port, TxReset, 0); else COMMAND(port, TxReset, dmaReset); . 705c * the transmitter, taking care not to reset the dma logic * as a busmaster receive may be in progress. . 676c if(ctlr->busmaster == 0) COMMAND(port, TxReset, 0); else COMMAND(port, TxReset, dmaReset); . 138a autoSelect9 = 0x0080, . ## diffname pc/etherelnk3.c 1996/1025 ## diff -e /n/fornaxdump/1996/0929/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/1025/sys/src/brazil/pc/etherelnk3.c 1059c if(!cistrcmp(ether->type, "3C589") || !cistrcmp(ether->type, "3C562")) . ## diffname pc/etherelnk3.c 1997/0327 ## diff -e /n/fornaxdump/1996/1025/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0327/sys/src/brazil/pc/etherelnk3.c 1330c ether->transmit = transmit; . 1319c ctlr->txthreshold = ETHERMAXTU/2; . 1309,1312c ctlr->rbp = allocrbp(allocb); . 1307c * Allocate the receive buffer. . 1300a ctlr->rxearly = rxearly; . 1245a x = inl(port+InternalConfig) & ~ramPartitionMask; outl(port+InternalConfig, x|ramPartition1to1); } . 1244c if(x & dataRate100){ . 1179a COMMAND(port, GlobalReset, 0); while(STATUS(port) & commandInProgress) ; . 1173a bpp = &bp->next; . 1170,1171c ether->tbdf = ap->tbdf; *bpp = bp->next; freeb(bp); . 1166c bpp = &adapter; for(bp = *bpp; bp; bp = bp->next){ ap = (Adapter*)bp->rp; . 1150c Block *bp, **bpp; Adapter *ap; . 1046,1050c tcmadapter(port, irq, p->tbdf); . 1042a ether->tbdf = p->tbdf; . 1031,1040c while(p = pcimatch(p, 0x10B7, 0)){ port = p->bar[0] & ~0x01; irq = p->intl; . 1029d 1026,1027c static Pcidev *p; . 1013,1017c tcmadapter(port, irq, BUSUNKNOWN); . 977d 961,965c tcmadapter(port, irq, BUSUNKNOWN); . 917d 816a static void tcmadapter(int port, int irq, int tbdf) { Block *bp; Adapter *ap; bp = allocb(sizeof(Adapter)); ap = (Adapter*)bp->rp; ap->port = port; ap->irq = irq; ap->tbdf = tbdf; bp->next = adapter; adapter = bp; } . 809,815c typedef struct Adapter { int port; int irq; int tbdf; } Adapter; static Block* adapter; . 769a COMMAND(port, AcknowledgeInterrupt, interruptLatch); . 768c panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status); . 747c txstart(ether); . 729c if(s & (txJabber|txUnderrun)){ . 720c if(s & txUnderrun){ . 717c s |= x; . 713c s = 0; . 692a COMMAND(port, SetRxFilter, s); COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); . 689a COMMAND(port, SelectRegisterWindow, Wstate); s = (port+RxFilter) & 0x000F; COMMAND(port, SelectRegisterWindow, Wop); . 687a * Reset the receiver and restore the filter and RxEarly * threshold before re-enabling. . 682d 673c print("#l%d: status 0x%uX, diag 0x%uX\n", . 651,663c for(status = STATUS(port); status & interruptMask; status = STATUS(port)){ . 638c int port, status, s, w, x; . 629,630c etheriq(ether, ctlr->rbp, 1); ctlr->rbp = bp; . 624,627c if(ctlr->busmaster) ctlr->rbp->wp = startdma(ether, PADDR(bp->rp)); . 620,622c COMMAND(port, RxDiscard, 0); while(STATUS(port) & commandInProgress) ; . 614,618c /* * A valid receive packet awaits: * if using PIO, read it into the buffer; * discard the packet from the FIFO; * if using busmastering, start a new transfer for * the next packet and as a side-effect get the * end-pointer of the one just received; * pass the packet on to whoever wants it. */ if(ctlr->busmaster == 0){ len = (rxstatus & rxBytes9); ctlr->rbp->wp = ctlr->rbp->rp + len; insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4)); } . 610,612d 608c startdma(ether, PADDR(ctlr->rbp->rp)); continue; . 602a /* * If there was an error or a new receive buffer can't be * allocated, discard the packet and go on to the next. */ if((rxstatus & rxError) || (bp = allocrbp(iallocb)) == 0){ . 601a } . 545,546d 536,542c txstart(ether); . 522,532d 516d 512,513c static void transmit(Ether* ether) . 504,507c else{ ctlr->txbp = bp; if(ctlr->txbusy == 0){ ctlr->txbusy = 1; COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); } break; . 499d 493c for(;;){ if(ctlr->txbp){ bp = ctlr->txbp; ctlr->txbp = 0; } else{ bp = qget(ether->oq); if(bp == nil) break; } . 475c txstart(Ether* ether) . 427c startdma(ether, PADDR(ctlr->rbp->rp)); . 425d 412c x = interruptMask; . 367c print("#l%d: BM status 0x%uX\n", ether->ctlrno, status); . 345,348c if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){ addr = (ulong)bp->base; addr = ROUNDUP(addr, 32); bp->rp = (uchar*)addr; } . 336c allocrbp(Block* (*f)(int)) . 331a int rxearly; /* RxEarlyThreshold */ . 309,310c Block* txbp; /* */ . 306,307c Block* rbp; /* receive buffer */ . 259c TxAvailableThresh = 0x0002, . 202a ramPartition5to3 = 0x00000000, ramPartition3to1 = 0x00010000, ramPartition1to1 = 0x00020000, ramPartitionMask = 0x00030000, . 125,126c #define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) #define STATUS(port) ins((port)+IntStatusR) . 50,51c CommandR = 0x000E, IntStatusR = 0x000E, . 8c * errata list; * 3C90x. . 4c * check robustness in the face of errors (e.g. busmaster & rxUnderrun); . ## diffname pc/etherelnk3.c 1997/0328 ## diff -e /n/emeliedump/1997/0327/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0328/sys/src/brazil/pc/etherelnk3.c 1259,1260d 1256a case xcvrMii: break; . 1249c ether->mbps = 100; . 1225,1228c x = eepromdata(port, i); . 1221,1223d 1211,1213d 1208a break; . 1207a rxearly = 2044; . 1187,1203c rxearly = 8188; rxstatus9 = 0; break; buggery: default: busmaster = 0; COMMAND(port, SelectRegisterWindow, Wsetup); . 1185d 1176,1183c if(port == 0) port = tcm5XXpcmcia(ether); if(port == 0) port = tcm59Xpci(ether); if(port == 0) port = tcm5XXeisa(ether); if(port == 0) port = tcm509isa(ether); if(port == 0) return -1; /* * Read the DeviceID from the EEPROM, it's at offset 0x03, * and do something depending on capabilities. */ switch(did = eepromdata(port, 0x03)){ case 0x9000: case 0x9001: case 0x9050: case 0x9051: if(BUSTYPE(ether->tbdf) != BusPCI) goto buggery; busmaster = 2; goto vortex; case 0x5900: case 0x5920: case 0x5950: case 0x5951: case 0x5952: case 0x5970: case 0x5971: case 0x5972: busmaster = 1; vortex: . 1160,1162d 1145c int busmaster, did, i, port, rxearly, rxstatus9, x, xcvr; . 1141a static int eepromdata(int port, int offset) { COMMAND(port, SelectRegisterWindow, Wsetup); while(EEPROMBUSY(port)) ; EEPROMCMD(port, EepromReadRegister, offset); while(EEPROMBUSY(port)) ; return EEPROMDATA(port); } . 1076a if(media & miiConnector) return xcvrMii; . 1039a COMMAND(port, GlobalReset, 0); while(STATUS(port) & commandInProgress) ; . 796,807c len += sprint(buf+len, "carrierlost: %ld\n", ctlr->stats[CarrierLost]); len += sprint(buf+len, "sqeerrors: %ld\n", ctlr->stats[SqeErrors]); len += sprint(buf+len, "multiplecolls: %ld\n", ctlr->stats[MultipleColls]); len += sprint(buf+len, "singlecollframes: %ld\n", ctlr->stats[SingleCollFrames]); len += sprint(buf+len, "latecollisions: %ld\n", ctlr->stats[LateCollisions]); len += sprint(buf+len, "rxoverruns: %ld\n", ctlr->stats[RxOverruns]); len += sprint(buf+len, "framesxmittedok: %ld\n", ctlr->stats[FramesXmittedOk]); len += sprint(buf+len, "framesrcvdok: %ld\n", ctlr->stats[FramesRcvdOk]); len += sprint(buf+len, "framesdeferred: %ld\n", ctlr->stats[FramesDeferred]); len += sprint(buf+len, "bytesrcvdok: %ld\n", ctlr->stats[BytesRcvdOk]); len += sprint(buf+len, "bytesxmittedok: %ld\n", ctlr->stats[BytesRcvdOk+1]); sprint(buf+len, "badssd: %ld\n", ctlr->stats[BytesRcvdOk+2]); . 772d 770a COMMAND(port, AcknowledgeInterrupt, interruptLatch); . 735c COMMAND(port, TxReset, (updnReset|dmaReset)); . 659c while((status = STATUS(port)) & (interruptMask|interruptLatch)){ . 472c ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD); . 463,467c ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4; ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8; ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF; ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF; . 454,461c for(i = 0; i < UpperFramesOk; i++) ctlr->stats[i] += inb(port+i) & 0xFF; . 440c int port, i, u, w; . 388c . 319,331c long stats[BytesRcvdOk+2]; . 305a enum { /* 3C90x extended register set */ PktStatus = 0x0020, /* 32-bits */ DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */ FragAddr = 0x0028, /* 32-bits */ FragLen = 0x002C, /* 16-bits */ ListOffset = 0x002E, /* 8-bits */ TxFreeThresh = 0x002F, /* 8-bits */ UpPktStatus = 0x0030, /* 32-bits */ FreeTimer = 0x0034, /* 16-bits */ UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */ /* PktStatus bits */ fragLast = 0x00000001, dnCmplReq = 0x00000002, dnStalled = 0x00000004, upCompleteX = 0x00000008, dnCompleteX = 0x00000010, upRxEarlyEnable = 0x00000020, armCountdown = 0x00000040, dnInProg = 0x00000080, counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */ countdownMode = 0x00000020, /* UpPktStatus bits */ upPktLenMask = 0x00001FFF, upStalled = 0x00002000, upError = 0x00004000, upPktComplete = 0x00008000, upOverrrun = 0x00010000, /* RxError<<16 */ upRuntFrame = 0x00020000, upAlignmentError = 0x00040000, upCRCError = 0x00080000, upOversizedFrame = 0x00100000, upDribbleBits = 0x00800000, upOverflow = 0x01000000, }; /* * Up/Dn Packet Descriptor. * The hardware info (np, control, addr, len) must be 8-byte aligned. */ typedef struct Pd Pd; typedef struct Pd { Pd* next; Block* bp; ulong np; /* next pointer */ ulong control; /* FSH or UpPktStatus */ ulong addr; ulong len; }; . 304c }; . 257c auiDisable = 0x8000, /* 10BaseT transceiver selected */ . 239a UpperBytesOk = 0x000D, . 228c miiConnector = 0x0040, . 206a ramPartition3to5 = 0x00030000, . 203a disableBadSsdDetect = 0x00000100, ramLocation = 0x00000200, /* 0 external, 1 internal */ . 123c interruptMask = 0x07FE, . 119a dnComplete = 0x0200, upComplete = 0x0400, . 97a enum { /* Stall command bits */ UpStall = 0x0000, UpUnStall = 0x0001, DnStall = 0x0002, DnUnStall = 0x0003, }; . 95c resetMask = 0x01FF, . 93a updnReset = 0x0100, /* upload/download (Rx/TX) logic */ . 62c Stall = 0x0006, /* 3C90x */ TxDone = 0x0007, . 57c SelectRegisterWindow = 0x0001, . 29a * 9000 PCI 3C900-TPO Etherlink III XL PCI 10BASE-T * 9001 PCI 3C900-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI * 9050 PCI 3C905-TX Fast Etherlink XL Shared 10BASE-T/100BASE-TX * 9051 PCI 3C905-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4 * . 9c * 3C90x full busmastering; * rewrite all initialisation. . ## diffname pc/etherelnk3.c 1997/0403 ## diff -e /n/emeliedump/1997/0328/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0403/sys/src/brazil/pc/etherelnk3.c 451a COMMAND(port, SetRxFilter, filter); } static void multicast(void* arg, char *addr, int on) { int filter, port; Ether *ether; ether = (Ether*)arg; port = ether->port; filter = receiveBroadcast|receiveIndividual; if(ether->nmaddr) filter |= receiveMulticast; . 449a if(ether->nmaddr) filter |= receiveMulticast; . 447c ether = (Ether*)arg; port = ether->port; . 445a Ether *ether; . ## diffname pc/etherelnk3.c 1997/0404 ## diff -e /n/emeliedump/1997/0403/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0404/sys/src/brazil/pc/etherelnk3.c 1438a ether->multicast = multicast; . 1316a USED(did); . 467a print("mutlicast nmaddr == %d\n", ether->nmaddr); . 464a USED(addr, on); . 460c multicast(void* arg, uchar *addr, int on) . ## diffname pc/etherelnk3.c 1997/0415 ## diff -e /n/emeliedump/1997/0404/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0415/sys/src/brazil/pc/etherelnk3.c 473a if(ether->prom) filter |= receiveAllFrames; . 470c print("multicast nmaddr == %d\n", ether->nmaddr); . ## diffname pc/etherelnk3.c 1997/0417 ## diff -e /n/emeliedump/1997/0415/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0417/sys/src/brazil/pc/etherelnk3.c 887c n = readstr(offset, a, n, p); free(p); return n; . 872,885c p = malloc(READSTR); len = snprint(p, READSTR, "interrupts: %ld\n", ctlr->interrupts); len += snprint(p+len, READSTR-len, "timer: %ld\n", ctlr->timer); len += snprint(p+len, READSTR-len, "carrierlost: %ld\n", ctlr->stats[CarrierLost]); len += snprint(p+len, READSTR-len, "sqeerrors: %ld\n", ctlr->stats[SqeErrors]); len += snprint(p+len, READSTR-len, "multiplecolls: %ld\n", ctlr->stats[MultipleColls]); len += snprint(p+len, READSTR-len, "singlecollframes: %ld\n", ctlr->stats[SingleCollFrames]); len += snprint(p+len, READSTR-len, "latecollisions: %ld\n", ctlr->stats[LateCollisions]); len += snprint(p+len, READSTR-len, "rxoverruns: %ld\n", ctlr->stats[RxOverruns]); len += snprint(p+len, READSTR-len, "framesxmittedok: %ld\n", ctlr->stats[FramesXmittedOk]); len += snprint(p+len, READSTR-len, "framesrcvdok: %ld\n", ctlr->stats[FramesRcvdOk]); len += snprint(p+len, READSTR-len, "framesdeferred: %ld\n", ctlr->stats[FramesDeferred]); len += snprint(p+len, READSTR-len, "bytesrcvdok: %ld\n", ctlr->stats[BytesRcvdOk]); len += snprint(p+len, READSTR-len, "bytesxmittedok: %ld\n", ctlr->stats[BytesRcvdOk+1]); snprint(p+len, READSTR-len, "badssd: %ld\n", ctlr->stats[BytesRcvdOk+2]); . 861a Ctlr *ctlr; . 859,860c char *p; . ## diffname pc/etherelnk3.c 1997/0418 ## diff -e /n/emeliedump/1997/0417/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0418/sys/src/brazil/pc/etherelnk3.c 470d ## diffname pc/etherelnk3.c 1997/0614 ## diff -e /n/emeliedump/1997/0418/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0614/sys/src/brazil/pc/etherelnk3.c 1411c memset(ctlr->stats, 0, sizeof(ctlr->stats)); . ## diffname pc/etherelnk3.c 1997/0712 ## diff -e /n/emeliedump/1997/0614/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0712/sys/src/brazil/pc/etherelnk3.c 1425c switch(ctlr->busmaster){ case 2: ctlr->dnenabled = 1; /* * Too severe, can use receive busmastering at 100Mbps OK, * but how to tell which rate is actually being used - the * 3c905 always seems to have dataRate100 set? */ x = eepromdata(port, 0x0F); print("software info 2: %uX\n", x); if(x & 0x01) ctlr->upenabled = 1; if(ctlr->upenabled) init905(ctlr); else ctlr->rbp = rbpalloc(allocb); outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); break; default: ctlr->rbp = rbpalloc(allocb); break; } . 1423c * Allocate any receive buffers. . 1378a COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); outs(port+MediaStatus, x); . 1375a if((did & 0xFF00) == 0x5900) busmaster = 0; . 1373a COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~dcConverterEnabled; . 1368a COMMAND(port, SelectRegisterWindow, Wfifo); x = inl(port+InternalConfig) & ~ramPartitionMask; outl(port+InternalConfig, x|ramPartition1to1); COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); x |= linkBeatEnable; outs(port+MediaStatus, x); if(x & dataRate100) ether->mbps = 100; break; . 1364a /* * Bug? the 3c905 always seems to have dataRate100 set. */ COMMAND(port, SelectRegisterWindow, Wdiagnostic); if(ins(port+MediaStatus) & dataRate100) ether->mbps = 100; . 1351,1361c xcvr = autoselect(port, xcvr, rxstatus9); . 1349a //print("reset: xcvr %uX\n", xcvr); . 1325d 1281a . 1272,1280c if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0) . 1257a if(scandone == 0){ tcm59Xpci(); tcm5XXeisa(); tcm509isa(); scandone = 1; } /* * Any adapter matches if no ether->port is supplied, * otherwise the ports must match. */ . 1251,1256c * Scan for adapter on PCI, EISA and finally * using the little ISA configuration dance. . 1248a static int scandone; . 1216c delay(100); . 1214c x = ins(port+MediaStatus) & ~dcConverterEnabled; . 1194,1211c setxcvr(port, xcvr10BaseT, is9); . 1187a { int i, v; for(i = 0; i < 2000; i++){ v = ins(port+MediaStatus); if(v & linkBeatDetect){ print("count %d v %uX\n", i, v); return xcvr100BaseTX; } delay(1); } //print("count %d v %uX\n", i, ins(port+MediaStatus)); } . 1184,1186c x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); outs(port+MediaStatus, linkBeatEnable|x); delay(10); . 1172,1181c setxcvr(port, xcvr100BaseTX, is9); . 1164,1166c //COMMAND(port, SelectRegisterWindow, Wdiagnostic); //print("autoselect: media status %uX\n", ins(port+MediaStatus)); . 1159a //print("autoselect: media %uX\n", media); . 1155,1156c if(is9){ COMMAND(port, SelectRegisterWindow, Wsetup); x = ins(port+ConfigControl); media = 0; if(x & base10TAvailable9) media |= base10TAvailable; if(x & coaxAvailable9) media |= coaxAvailable; if(x & auiAvailable9) media |= auiAvailable; } else{ . 1153a * It's a bonus if it works for anything else. . 1146c autoselect(int port, int xcvr, int is9) . 1144a static void setxcvr(int port, int xcvr, int is9) { int x; if(is9){ COMMAND(port, SelectRegisterWindow, Wsetup); x = ins(port+AddressConfig) & ~xcvrMask9; x |= (xcvr>>20)<<14; outs(port+AddressConfig, x); } else{ COMMAND(port, SelectRegisterWindow, Wfifo); x = inl(port+InternalConfig) & ~xcvrMask; x |= xcvr; outl(port+InternalConfig, x); } COMMAND(port, TxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, RxReset, 0); while(STATUS(port) & commandInProgress) ; } #ifdef notdef static struct xxx { int available; int next; } xxx[8] = { { base10TAvailable, 1, }, /* xcvr10BaseT -> xcvrAui */ { auiAvailable, 3, }, /* xcvrAui -> xcvr10Base2 */ { 0, -1, }, { coaxAvailable, -1, }, /* xcvr10Base2 -> nowhere */ { baseTXAvailable, 5, }, /* xcvr100BaseTX-> xcvr100BaseFX */ { baseFXAvailable, -1, }, /* xcvr100BaseFX-> nowhere */ { miiConnector, -1, }, /* xcvrMii -> nowhere */ { 0, -1, }, }; #endif /* notdef */ . 1132,1133d 1124,1128d 1117a p = nil; . 1115c Pcidev *p; . 1112,1113c static void tcm59Xpci(void) . 1108,1109d 1101,1105d 1085,1086c for(slot = 1; slot < MaxEISA; slot++){ port = slot*0x1000; . 1076,1077c if(strncmp((char*)(KZERO|0xFFFD9), "EISA", 4)) return; . 1073c * Check if this is an EISA machine. . 1070c int irq, port, slot; . 1068d 1065,1066c static void tcm5XXeisa(void) . 1061,1062d 1054,1058d 1018,1020c * Attempt to activate all adapters. If adapter is set for * EISA mode (0x3F0), tag it and ignore. Otherwise, activate * it fully. . 1012,1013c static void tcm509isa(void) . 872,885c len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); len += snprint(p+len, READSTR-len, "timer: %lud\n", ctlr->timer); len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->stats[CarrierLost]); len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n", ctlr->stats[SqeErrors]); len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n", ctlr->stats[MultipleColls]); len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n", ctlr->stats[SingleCollFrames]); len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->stats[LateCollisions]); len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->stats[RxOverruns]); len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n", ctlr->stats[FramesXmittedOk]); len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n", ctlr->stats[FramesRcvdOk]); len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->stats[FramesDeferred]); len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n", ctlr->stats[BytesRcvdOk]); len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", ctlr->stats[BytesRcvdOk+1]); snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]); . 828a if(status & dnComplete){ COMMAND(port, AcknowledgeInterrupt, dnComplete); start905(ether, 0); status &= ~dnComplete; } . 814a if(ctlr->busmaster == 2) outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); . 798a if(ctlr->dnenabled){ while(inl(port+PktStatus) & dnInProg) ; } . 781a if(status & (upComplete)){ COMMAND(port, AcknowledgeInterrupt, upComplete); receive905(ether); status &= ~upComplete; } . 710c if(ctlr->busmaster == 1) . 700c if(ctlr->busmaster == 0 || ctlr->busmaster == 2){ . 685c if(ctlr->busmaster == 1) . 680c if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){ . 632c if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress)) . 621a receive905(Ether* ether) { Ctlr *ctlr; int port; Block *bp, *xbp; Upd *upd; ctlr = ether->ctlr; port = ether->port; //ilock(&ctlr->upqlock); COMMAND(port, Stall, upStall); while(STATUS(port) & commandInProgress) ; bp = ctlr->upqhead; upd = (Upd*)bp->rp; while(upd->control & upPktComplete){ if(upd->control & upError){ if(upd->control & upOverrun) ether->overflows++; if(upd->control & (upOversizedFrame|upRuntFrame)) ether->buffs++; if(upd->control & upAlignmentError) ether->frames++; if(upd->control & upCRCError) ether->crcs++; upd->control = 0; } else if(xbp = updalloc(upd->np)){ bp->rp += sizeof(Upd)-sizeof(Etherpkt); bp->wp = bp->rp + (upd->control & rxBytes); xbp->next = bp->next; bp->next = 0; etheriq(ether, bp, 1); bp = xbp; } upd = (Upd*)ctlr->upqtail->rp; upd->np = PADDR(bp->rp); ctlr->upqtail->next = bp; ctlr->upqtail = bp; ctlr->upqhead = bp->next; bp = ctlr->upqhead; upd = (Upd*)bp->rp; } COMMAND(port, Stall, upUnStall); //iunlock(&ctlr->upqlock); } static void . 612a if(ctlr->dnenabled){ transmit905(ether); return; } . 604a start905(Ether* ether, Dpd* add) { Ctlr *ctlr; int dnlistptr, port; Dpd *dpd; ctlr = ether->ctlr; port = ether->port; ilock(&ctlr->dnqlock); COMMAND(port, Stall, dnStall); while(STATUS(port) & commandInProgress) ; /* * Free any completed packets. */ dnlistptr = inl(port+DnListPtr); while(dpd = ctlr->dnqhead){ if(PADDR(dpd) == dnlistptr) break; ctlr->dnqhead = dpd->next; if(dpd->bp) freeb(dpd->bp); dpdfree(ctlr, dpd); } /* * Add any new packets to the queue. */ if(add){ if(ctlr->dnqhead){ dpd = ctlr->dnqtail; dpd->next = add; dpd->np = PADDR(&add->np); dpd->control &= ~dnIndicate; } else ctlr->dnqhead = add; ctlr->dnqtail = add; } /* * If the adapter is not currently processing anything * and there is something on the queue, start it processing. */ if(dnlistptr == 0 && ctlr->dnqhead) outl(port+DnListPtr, PADDR(ctlr->dnqhead)); COMMAND(port, Stall, dnUnStall); iunlock(&ctlr->dnqlock); } static void transmit905(Ether* ether) { Ctlr *ctlr; Block *bp; Dpd* dpd; bp = qget(ether->oq); if(bp == nil) return; ctlr = ether->ctlr; dpd = dpdalloc(ctlr); dpd->next = 0; dpd->bp = bp; dpd->np = 0; dpd->control = dnIndicate|BLEN(bp); dpd->addr = PADDR(bp->rp); dpd->len = updnLastFrag|BLEN(bp); start905(ether, dpd); } static void . 548a break; . 545c switch(ctlr->xcvr){ case xcvrMii: case xcvr100BaseTX: case xcvr100BaseFX: . 514a else{ if(ctlr->upenabled) outl(port+UpListPtr, PADDR(ctlr->upqhead->rp)); } . 513c if(ctlr->busmaster == 1) . 502a else{ if(ctlr->dnenabled) x &= ~transferInt; if(ctlr->upenabled) x &= ~(rxEarly|rxComplete); } . 501c if(ctlr->busmaster == 1) . 402a Upd *upd; /* * The hardware info (np, control, addr, len) * must be 8-byte aligned. */ if(bp = iallocb(sizeof(Upd)+8)){ bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 8); bp->wp = bp->rp; upd = (Upd*)bp->rp; upd->np = np; upd->control = 0; upd->addr = PADDR(upd->data); upd->len = updnLastFrag|sizeof(Etherpkt); } return bp; } static void init905(Ctlr* ctlr) { int i; ulong np; Block *bp; Upd *upd; np = 0; for(i = 0; i < 16; i++){ bp = updalloc(np); if(ctlr->upqhead == 0) ctlr->upqtail = bp; bp->next = ctlr->upqhead; ctlr->upqhead = bp; np = PADDR(bp->rp); } ctlr->upqtail->next = ctlr->upqhead; upd = (Upd*)ctlr->upqtail->rp; upd->np = PADDR(ctlr->upqhead->rp); } static Dpd* dpdalloc(Ctlr* ctlr) { Dpd *dpd; void *base; ilock(&ctlr->dpdlock); if(dpd = ctlr->dpdpool){ ctlr->dpdpool = dpd->next; iunlock(&ctlr->dpdlock); dpd->next = 0; dpd->bp = 0; } else{ iunlock(&ctlr->dpdlock); base = smalloc(sizeof(Dpd)+8); dpd = (Dpd*)ROUNDUP((ulong)base, 8); dpd->base = base; } return dpd; } static void dpdfree(Ctlr* ctlr, Dpd* dpd) { ilock(&ctlr->dpdlock); dpd->next = ctlr->dpdpool; ctlr->dpdpool = dpd; iunlock(&ctlr->dpdlock); } static Block* rbpalloc(Block* (*f)(int)) { Block *bp; . 400c updalloc(ulong np) . 396a int upenabled; int dnenabled; . 391c long stats[BytesRcvdOk+3]; . 388a //Lock upqlock; /* full-busmaster -based reception */ Block* upqhead; Block* upqtail; int upalloc; int nupd; Lock dpdlock; /* pool of free Dpd's */ Dpd* dpdpool; Lock dnqlock; /* full-busmaster -based transmission */ Dpd* dnqhead; Dpd* dnqtail; . 385c Block* txbp; /* FIFO -based transmission */ . 377a uchar data[sizeof(Etherpkt)]; } Upd; . 376d 371a typedef struct Upd { . 370a void* base; /* base of this allocation */ } Dpd; . 367,369c typedef struct Dpd Dpd; typedef struct Dpd { ulong np; /* next pointer */ ulong control; /* FSH or UpPktStatus */ ulong addr; ulong len; Dpd* next; . 360a dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */ updnLastFrag = 0x80000000, /* (dpd->len) */ . 354c upOverrun = 0x00010000, /* RxError<<16 */ . 349c /* UpPktStatus bits (dpd->control) */ . 160a base10TAvailable9 = 0x0200, coaxAvailable9 = 0x1000, auiAvailable9 = 0x2000, . 107,110c upStall = 0x0000, upUnStall = 0x0001, dnStall = 0x0002, dnUnStall = 0x0003, . ## diffname pc/etherelnk3.c 1997/0715 ## diff -e /n/emeliedump/1997/0712/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0715/sys/src/brazil/pc/etherelnk3.c 1755,1757c //print("software info 2: %uX\n", x); if(!(x & 0x01)) outl(port+PktStatus, upRxEarlyEnable); . 1753a ctlr->upenabled = 1; . 1750,1752c * 10MUpldBug. * Disabling is too severe, can use receive busmastering at * 100Mbps OK, but how to tell which rate is actually being used - * the 3c905 always seems to have dataRate100 set? * Believe the bug doesn't apply if upRxEarlyEnable is set * and the threshold is set such that uploads won't start * until the whole packet has been received. . 463c for(i = 0; i < 64; i++){ . ## diffname pc/etherelnk3.c 1997/0806 ## diff -e /n/emeliedump/1997/0715/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0806/sys/src/brazil/pc/etherelnk3.c 9c * limit 3C90x transmit queue; . ## diffname pc/etherelnk3.c 1997/0919 ## diff -e /n/emeliedump/1997/0806/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0919/sys/src/brazil/pc/etherelnk3.c 1765a } . 1764c if(ctlr->upenabled || ctlr->dnenabled){ ctlr->nup = Nup; ctlr->ndn = Ndn; . 1166a len += snprint(p+len, READSTR-len, "up: q %lud s %lud\n", ctlr->upqmax, ctlr->upstalls); ctlr->upqmax = 0; len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d\n", ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax); ctlr->dnqmax = 0; . 1108a ctlr->dninterrupts++; . 1107d 1105a txstart905(ether); . 1048a COMMAND(port, AcknowledgeInterrupt, upComplete); . 1047d 881,882c if(q > ctlr->upqmax) ctlr->upqmax = q; . 879a ctlr->uphead = pd; . 877,878c q++; . 871,875c pd->control = 0; COMMAND(port, Stall, upUnStall); . 860,868c else if(bp = iallocb(sizeof(Etherpkt)+4)){ len = pd->control & rxBytes; pd->bp->wp = pd->bp->rp+len; etheriq(ether, pd->bp, 1); pd->bp = bp; pd->addr = PADDR(bp->rp); . 857,858d 855c if(pd->control & upCRCError) . 853c if(pd->control & upAlignmentError) . 851c if(pd->control & (upOversizedFrame|upRuntFrame)) . 840,849c if(inl(port+UpPktStatus) & upStalled) ctlr->upstalls++; q = 0; for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){ if(pd->control & upError){ if(pd->control & upOverrun) . 833,835c int len, port, q; Pd *pd; Block *bp; . 817c txstart905(ether); . 783,807d 779c iunlock(&ctlr->dnlock); . 775,776c if(dnlistptr == 0 && ctlr->dnq) outl(port+DnListPtr, PADDR(&ctlr->dnhead->np)); . 770a if(ctlr->dnq > ctlr->dnqmax) ctlr->dnqmax = ctlr->dnq; . 756,768c while(ctlr->dnq < (ctlr->ndn-1)){ bp = qget(ether->oq); if(bp == nil) break; pd = ctlr->dnhead->next; pd->np = 0; pd->control = dnIndicate|BLEN(bp); pd->addr = PADDR(bp->rp); pd->len = updnLastFrag|BLEN(bp); pd->bp = bp; if(ctlr->dnq == 0) ctlr->dntail = pd; ctlr->dnhead->np = PADDR(&pd->np); ctlr->dnhead->control &= ~dnIndicate; ctlr->dnhead = pd; ctlr->dnq++; ctlr->dnqueued++; . 754a ctlr->dntail = pd; . 750,753c if(pd->bp){ freeb(pd->bp); pd->bp = nil; } ctlr->dnq--; pd = pd->next; . 747,748c pd = ctlr->dntail; while(ctlr->dnq){ if(PADDR(&pd->np) == dnlistptr) . 738c ilock(&ctlr->dnlock); . 733c Block *bp; Pd *pd; . 729c txstart905(Ether* ether) . 632c outl(port+UpListPtr, PADDR(&ctlr->uphead->np)); . 499,507d 489,496c ctlr->dnhead = ctlr->dnr; ctlr->dntail = ctlr->dnr; ctlr->dnq = 0; iunlock(&ctlr->dnlock); . 482,487c prev = ctlr->dnr; for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){ pd->next = prev; prev = pd; . 476,480c ilock(&ctlr->dnlock); ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd)); ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8); . 471,474c ctlr->uphead = ctlr->upr; . 454,469c pd->next = prev; prev = pd; pd->bp = bp; . 451,452c prev = ctlr->upr; for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){ pd->np = PADDR(&prev->np); pd->control = 0; bp = allocb(sizeof(Etherpkt)); pd->addr = PADDR(bp->rp); pd->len = updnLastFrag|sizeof(Etherpkt); . 441,449c ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd)); ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8); . 438,439c * Create rings for the receive and transmit sides. * Take care with alignment: * make sure ring base is 8-byte aligned; * make sure each entry is 8-byte aligned. . 435c Pd *pd, *prev; . 431,432c static void init905(Ctlr* ctlr) . 422a int upqmax; int upstalls; int dnqmax; long dninterrupts; long dnqueued; . 415,418d 412,413c Lock dnlock; /* full-busmaster -based transmission */ int ndn; void* dnbase; Pd* dnr; Pd* dnhead; Pd* dntail; int dnq; . 406,410c int nup; /* full-busmaster -based reception */ void* upbase; Pd* upr; Pd* uphead; . 386,394d 383,384c } Pd; . 381c Pd* next; . 374,375c typedef struct Pd Pd; typedef struct Pd { . 371,372c * Up/Dn Packet Descriptors. * The hardware info (np, control, addr, len) must be 8-byte aligned * and this structure size must be a multiple of 8. . 367a Nup = 32, Ndn = 16, . 9d ## diffname pc/etherelnk3.c 1997/1011 ## diff -e /n/emeliedump/1997/0919/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1011/sys/src/brazil/pc/etherelnk3.c 1338c port = p->mem[0].bar & ~0x01; . ## diffname pc/etherelnk3.c 1997/1025 ## diff -e /n/emeliedump/1997/1011/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1025/sys/src/brazil/pc/etherelnk3.c 1301c if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4)) . 776,781d 774c else{ w = (STATUS(port)>>13) & 0x07; COMMAND(port, SelectRegisterWindow, Wop); txstart(ether); COMMAND(port, SelectRegisterWindow, w); . 772c ilock(&ctlr->wlock); if(ctlr->dnenabled) . 760d 705d 471d 459d 404,405c int ndn; /* full-busmaster -based transmission */ . ## diffname pc/etherelnk3.c 1997/1203 ## diff -e /n/emeliedump/1997/1025/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1203/sys/src/brazil/pc/etherelnk3.c 1731a COMMAND(port, AcknowledgeInterrupt, interruptLatch); . 1106,1111c if(ctlr->upenabled){ len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d s %lud\n", ctlr->upqueued, ctlr->upinterrupts, ctlr->dnqmax, ctlr->upstalls); ctlr->upqmax = 0; } if(ctlr->dnenabled){ len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d\n", ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax); ctlr->dnqmax = 0; } . 1045a txstart905(ether); . 1044d 987a ctlr->upinterrupts++; . 986a receive905(ether); . 985d 818a ctlr->upqueued += q; . 415a long upinterrupts; long upqueued; . ## diffname pc/etherelnk3.c 1997/1207 ## diff -e /n/emeliedump/1997/1203/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1207/sys/src/brazil/pc/etherelnk3.c 1113c ctlr->upqueued, ctlr->upinterrupts, ctlr->upqmax, ctlr->upstalls); . 369c Ndn = 32, . ## diffname pc/etherelnk3.c 1997/1212 ## diff -e /n/emeliedump/1997/1207/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1212/sys/src/brazil/pc/etherelnk3.c 1742,1743d 1033a print("#l%d: txstatus 0x%uX, threshold %d\n", ether->ctlrno, s, ctlr->txthreshold); . 1032a if(ctlr->dnenabled) status |= dnComplete; . 756,757c if(stalled) COMMAND(port, Stall, dnUnStall); . 754c if(inl(port+DnListPtr) == 0 && ctlr->dnq) . 741a if(ctlr->dnq == 0) ctlr->dntail = pd; . 737,738c if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){ COMMAND(port, Stall, dnStall); for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--) ; if(timeo == 0) print("#l%d: dnstall %d\n", ether->ctlrno, timeo); stalled = 1; } . 724a stalled = 0; . 714c if(PADDR(&pd->np) == inl(port+DnListPtr)) . 711d 704,707d 697c int port, stalled, timeo; . 369c Ndn = 64, . ## diffname pc/etherelnk3.c 1998/0319 ## diff -e /n/emeliedump/1997/1212/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0319/sys/src/brazil/pc/etherelnk3.c 1414c autoselect(int port, int , int is9) . ## diffname pc/etherelnk3.c 1998/0320 ## diff -e /n/emeliedump/1998/0319/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0320/sys/src/brazil/pc/etherelnk3.c 962c COMMAND(port, TxReset, (updnReset|dmaReset)); . ## diffname pc/etherelnk3.c 1998/0321 ## diff -e /n/emeliedump/1998/0320/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0321/sys/src/brazil/pc/etherelnk3.c 1028c /* * According to the manual, maxCollisions does not require * a TxReset, merely a TxEnable. However, evidence points to * it being necessary on the 3C905. The jury is still out. */ if(s & (txJabber|txUnderrun|maxCollisions)){ . ## diffname pc/etherelnk3.c 1998/0325 ## diff -e /n/emeliedump/1998/0321/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0325/sys/src/brazil/pc/etherelnk3.c 1631a if(an & 0x0140) setfullduplex(port); . 1629,1630c phyaddr = 24; an = miir(ether, phyaddr, 0x04); an &= miir(ether, phyaddr, 0x05) & 0x03E0; XCVRDEBUG("mii an: %uX\n", an); if(an & 0x380) . 1627c * Quick hack. . 1622a XCVRDEBUG("autoselect returns: xcvr %uX\n", xcvr); . 1620c XCVRDEBUG("reset: xcvr %uX\n", xcvr); . 1514c int an, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr; . 1487a XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus)); . 1472c XCVRDEBUG("count %d v %uX\n", i, ins(port+MediaStatus)); . 1449,1450c COMMAND(port, SelectRegisterWindow, Wdiagnostic); XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus)); . 1444c XCVRDEBUG("autoselect: media %uX\n", media); . 1422a USED(xcvr); . 1419c autoselect(int port, int xcvr, int is9) . 1401a static void setfullduplex(int port) { int x; COMMAND(port, SelectRegisterWindow, Wfifo); x = ins(port+MacControl); outs(port+MacControl, fullDuplexEnable|x); COMMAND(port, TxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, RxReset, 0); while(STATUS(port) & commandInProgress) ; } static int miir(Ether* ether, int phyad, int regad) { int data, i, port, w, x; port = ether->port+PhysicalMgmt; w = (STATUS(port)>>13) & 0x07; COMMAND(ether->port, SelectRegisterWindow, Wdiagnostic); /* * Taken from the Cyclone manual appendix describing * how to programme the MII Management Interface. */ /* * Preamble */ for(i = 0; i < 32; i++){ outs(port, mgmtDir|mgmtData); microdelay(1); outs(port, mgmtDir|mgmtData|mgmtClk); microdelay(1); } /* * ST+OP+PHYAD+REGAD */ x = 0x1800|(phyad<<5)|regad; for(i = 14-1; i >= 0; i--){ if(x & (1<= 0; i--){ outs(port, 0); microdelay(1); outs(port, mgmtClk); microdelay(1); x = ins(port); if(x & mgmtData) data |= (1<port, SelectRegisterWindow, w); if(data & 0x10000) return -1; print("%d/%d: data=%uX\n", phyad, regad, data); return data & 0xFFFF; } static void scanphy(Ether* ether) { int i, x; for(i = 0; i < 32; i++){ if((x = miir(ether, i, 2)) == -1) continue; x <<= 6; x |= miir(ether, i, 3)>>10; print("phy%d: oui %uX reg1 %uX\n", i, x, miir(ether, i, 1)); } } . 1132,1133c if(ctlr->dnqmax > ctlr->dnqmaxhw) ctlr->dnqmaxhw = ctlr->dnqmax; len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n", ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw); . 1127,1128c if(ctlr->upqmax > ctlr->upqmaxhw) ctlr->upqmaxhw = ctlr->upqmax; len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n", ctlr->upqueued, ctlr->upinterrupts, ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls); . 419a int dnqmaxhw; . 415a int upqmaxhw; . 266a /* PhysicalMgmt bits */ mgmtClk = 0x0001, mgmtData = 0x0002, mgmtDir = 0x0004, cat5LinkTestDefeat = 0x8000, . 242a extendAfterCollision = 0x0080, /* 3C90xB */ flowControlEnable = 0x0100, /* 3C90xB */ vltEnable = 0x0200, /* 3C90xB */ . 50a #define XCVRDEBUG if(1)print . 9c * rewrite all initialisation; * handle the cyclone adapter. . 2c * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters. . ## diffname pc/etherelnk3.c 1998/0326 ## diff -e /n/emeliedump/1998/0325/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0326/sys/src/brazil/pc/etherelnk3.c 52c #define XCVRDEBUG if(0)print . ## diffname pc/etherelnk3.c 1998/0327 ## diff -e /n/emeliedump/1998/0326/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0327/sys/src/brazil/pc/etherelnk3.c 1512c XCVRDEBUG("phy%d/%d: data=%uX\n", phyad, regad, data); . ## diffname pc/etherelnk3.c 1998/0331 ## diff -e /n/emeliedump/1998/0327/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0331/sys/src/brazil/pc/etherelnk3.c 1766c //if(an & 0x380) . 1527c XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(ether, i, 1)); . 1512c print("%d/%d: data=%uX\n", phyad, regad, data); . 1105c iunlock(&ctlr->wlock); . 952c ilock(&ctlr->wlock); . ## diffname pc/etherelnk3.c 1998/0401 ## diff -e /n/emeliedump/1998/0331/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0401/sys/src/brazil/pc/etherelnk3.c 1527a USED(x); . ## diffname pc/etherelnk3.c 1998/0505 ## diff -e /n/emeliedump/1998/0401/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0505/sys/src/brazil/pc/etherelnk3.c 1767c for(i = 0; i < ether->nopt; i++){ if(cistrcmp(ether->opt[i], "fullduplex") == 0) an |= 0x0100; else if(cistrcmp(ether->opt[i], "force100") == 0) an |= 0x0080; } XCVRDEBUG("mii an: %uX\n", an); if(an & 0x380) . 1761a scanphy(ether); . 1753a /* * forgive me, but i am weak */ if(did == 0x9055) xcvr = xcvrMii; else . 1695a case 0x9055: . 1595,1606d 1429,1434c txrxreset(port); . 1412,1417c txrxreset(port); . 1358,1359c txrxreset(port); . 1319,1320c txrxreset(port); . 1162a static void txrxreset(int port) { COMMAND(port, TxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, RxReset, 0); while(STATUS(port) & commandInProgress) ; } . 34a * 9055 PCI 3C905B-TX Fast Etherlink Shared 10BASE-T/100BASE-TX . ## diffname pc/etherelnk3.c 1998/0605 ## diff -e /n/emeliedump/1998/0505/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0605/sys/src/brazil/pc/etherelnk3.c 1872d 1760,1761c an = miir(port, phyaddr, 0x04); an &= miir(port, phyaddr, 0x05) & 0x03E0; . 1757c scanphy(port); . 1751c XCVRDEBUG("autoselect returns: xcvr %uX, did 0x%uX\n", xcvr, did); . 1526,1527c x |= miir(port, i, 3)>>10; XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1)); . 1523c if((x = miir(port, i, 2)) == -1 || x == 0) . 1518c scanphy(int port) . 1512d 1508c port -= PhysicalMgmt; COMMAND(port, SelectRegisterWindow, w); . 1446a port += PhysicalMgmt; . 1445c COMMAND(port, SelectRegisterWindow, Wdiagnostic); . 1442,1443d 1440c int data, i, w, x; . 1438c miir(int port, int phyad, int regad) . ## diffname pc/etherelnk3.c 1998/0606 ## diff -e /n/emeliedump/1998/0605/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0606/sys/src/brazil/pc/etherelnk3.c 1765a else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) an |= 0x0100; . ## diffname pc/etherelnk3.c 1998/0725 ## diff -e /n/emeliedump/1998/0606/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0725/sys/src/brazil/pc/etherelnk3.c 1775a } else if(anar & 0x0200) /* 100BASE-T4 */ ; else if(anar & 0x0080) /* 100BASE-TX */ ether->mbps = 100; else if(anar & 0x0040) /* 10BASE-TFD */ setfullduplex(port); else /* 10BASE-T */ ; . 1774d 1771,1772c XCVRDEBUG("mii anar: %uX\n", anar); if(anar & 0x0100){ /* 100BASE-TXFD */ . 1769c anar |= 0x0080; . 1767c anar |= 0x0100; . 1765c anar |= 0x0100; . 1760,1762c anar = miir(port, phyaddr, 0x04); anlpar = miir(port, phyaddr, 0x05) & 0x03E0; anar &= anlpar; miir(port, phyaddr, 0x00); XCVRDEBUG("mii an: %uX r0:%uX r1:%uX\n", anar, anlpar, miir(port, phyaddr, 0x00), miir(port, phyaddr, 0x01)); . 1635c int anar, anlpar, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr; . ## diffname pc/etherelnk3.c 1998/0811 ## diff -e /n/emeliedump/1998/0725/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0811/sys/src/brazil/pc/etherelnk3.c 1764c XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n", . ## diffname pc/etherelnk3.c 1998/0825 ## diff -e /n/emeliedump/1998/0811/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0825/sys/src/brazil/pc/etherelnk3.c 434,435c ulong dninterrupts; ulong dnqueued; . 429,431c ulong upinterrupts; ulong upqueued; ulong upstalls; . ## diffname pc/etherelnk3.c 1998/1024 ## diff -e /n/emeliedump/1998/0825/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/1024/sys/src/brazil/pc/etherelnk3.c 830a coherence(); . 755a coherence(); . ## diffname pc/etherelnk3.c 1998/1027 ## diff -e /n/emeliedump/1998/1024/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/1027/sys/src/brazil/pc/etherelnk3.c 1063,1064c if(s & ~(txStatusComplete|maxCollisions)) print("#l%d: txstatus 0x%uX, threshold %d\n", ether->ctlrno, s, ctlr->txthreshold); . 1047a * On busy or badly configured networks maxCollisions can * happen frequently enough for messages to be annoying so * keep quiet about them by popular request. . ## diffname pc/etherelnk3.c 1999/0130 ## diff -e /n/emeliedump/1998/1027/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0130/sys/src/brazil/pc/etherelnk3.c 1687a case 0x9005: . 32a * 9005 PCI 3C900B-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI . ## diffname pc/etherelnk3.c 1999/0219 ## diff -e /n/emeliedump/1999/0130/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0219/sys/src/brazil/pc/etherelnk3.c 1405,1406c int i; for(i = 0; tcmpcmcia[i] != nil; i++){ if(!cistrcmp(ether->type, tcmpcmcia[i])) return ether->port; } . 1401a static char* tcmpcmcia[] = { "3C589", /* 3COM 589[ABCD] */ "3C562", /* 3COM 562 */ "589E", /* 3COM Megahertz 589E */ nil, }; . ## diffname pc/etherelnk3.c 1999/0314 ## diff -e /n/emeliedump/1999/0219/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0314/sys/src/brazil/pc/etherelnk3.c 1398a pcisetbme(p); . ## diffname pc/etherelnk3.c 1999/0318 ## diff -e /n/emeliedump/1999/0314/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0318/sys/src/brazil/pc/etherelnk3.c 1785c anar, anlpar, miir(port, phyaddr, 0x00), miir(port, phyaddr, 0x01)); . 1779a for(i = 0; i < 7; i++) XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i)); XCVRDEBUG("\n"); { int phystat, timeo; for(timeo = 0; timeo < 30; timeo++){ phystat = miir(port, phyaddr, 0x01); if(phystat & 0x20) break; XCVRDEBUG(" %2.2uX", phystat); delay(100); } XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01)); XCVRDEBUG("\n"); } . 1767a XCVRDEBUG("9055 reset ops 0x%uX\n", ins(port+ResetOp905B)); } . 1766c if(did == 0x9055){ . 1523,1525c miimdo(port, 0xFFFFFFFF, 32); miimdo(port, 0x1800|(phyad<<5)|regad, 14); data = miimdi(port, 18); . 1521c * Preamble; * ST+OP+PHYAD+REGAD; * TA + 16 data bits. . 1519a static int miir(int port, int phyad, int regad) { int data, w; w = (STATUS(port)>>13) & 0x07; COMMAND(port, SelectRegisterWindow, Wdiagnostic); port += PhysicalMgmt; . 1518a } . 1514,1516c outs(port, mdo); . 1512c outs(port, mdo|mgmtClk); . 1509,1510c for(i = n-1; i >= 0; i--){ if(bits & (1<= 0; i--){ if(ins(port) & mgmtData) data |= (1<type, tcmpcmcia[i])){ if(ioalloc(ether->port, 0x20, 0, "tcm5XXpcmcia") < 0) return 0; . 1395a if(ioalloc(port, p->mem[0].size, 0, "tcm59Xpci") < 0){ print("tcm59Xpci: port %d in use\n", port); continue; } . 1374a } . 1373c if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){ iofree(port); . 1371a } if(ins(port+0xC80+ManufacturerID) != 0x6D50){ iofree(port); continue; } . 1370c if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){ print("tcm5XXeisa: port %d in use\n", port); . 1320a } . 1319c if(port == 0x3F0){ iofree(port); . 1314a if(ioalloc(port, 0x20, 0, "tcm509isa") < 0){ print("tcm509isa:port %d in use\n", port); continue; } . ## diffname pc/etherelnk3.c 1999/0716 ## diff -e /n/emeliedump/1999/0714/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0716/sys/src/brazil/pc/etherelnk3.c 1440a */ . 1439c /* * No need for an ioalloc here, the 589 reset * code deals with it. if(ioalloc(ether->port, 0x10, 0, "tcm5XXpcmcia") < 0) . 1315c if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){ . ## diffname pc/etherelnk3.c 1999/0916 ## diff -e /n/emeliedump/1999/0716/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0916/sys/src/brazil/pc/etherelnk3.c 1782a /* * Allow user to specify desired media in plan9.ini */ for(i = 0; i < ether->nopt; i++){ if(cistrncmp(ether->opt[i], "media=", 6) != 0) continue; p = ether->opt[i]+6; for(j = 0; j < nelem(media); j++) if(cistrcmp(p, media[j].name) == 0) xcvr = media[j].xcvr; } . 1679a char *p; . 1674c int anar, anlpar, busmaster, did, i, j, phyaddr, port, rxearly, rxstatus9, x, xcvr; . 1591d 1586a static struct { char *name; int avail; int xcvr; } media[] = { "10BaseT", base10TAvailable, xcvr10BaseT, "10Base2", coaxAvailable, xcvr10Base2, "100BaseTX", baseTXAvailable, xcvr100BaseTX, "100BaseFX", baseFXAvailable, xcvr100BaseFX, "aui", auiAvailable, xcvrAui, "mii", miiConnector, xcvrMii }; . ## diffname pc/etherelnk3.c 2000/0612 ## diff -e /n/emeliedump/1999/0916/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/2000/0612/sys/src/9/pc/etherelnk3.c 1812c if(did == 0x9055 || did ==0x9200){ . 1737a case 0x9200: . 36a * 9200 PCI 3C905C-TX Fast Etherlink Shared 10BASE-T/100BASE-TX . ## diffname pc/etherelnk3.c 2000/0619 ## diff -e /n/emeliedump/2000/0612/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0619/sys/src/9/pc/etherelnk3.c 1413c print("tcm59Xpci: port 0x%uX in use\n", port); . 1379c print("tcm5XXeisa: port 0x%uX in use\n", port); . 1317c print("tcm509isa: port 0x%uX in use\n", port); . ## diffname pc/etherelnk3.c 2000/0907 ## diff -e /n/emeliedump/2000/0619/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0907/sys/src/9/pc/etherelnk3.c 1816c XCVRDEBUG("905[BC] reset ops 0x%uX\n", . 1814c if(did == 0x9055 || did == 0x9200){ . ## diffname pc/etherelnk3.c 2000/0921 ## diff -e /n/emeliedump/2000/0907/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0921/sys/src/9/pc/etherelnk3.c 1928,1929c if(busmaster == 2) x = port+TxStatus905; else x = port+TxStatus; while(inb(x)) outb(x, 0); . 1138,1149c len += snprint(p+len, READSTR-len, "timer: %lud %lud\n", ctlr->timer[0], ctlr->timer[1]); len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->stats[CarrierLost]); len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n", ctlr->stats[SqeErrors]); len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n", ctlr->stats[MultipleColls]); len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n", ctlr->stats[SingleCollFrames]); len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->stats[LateCollisions]); len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->stats[RxOverruns]); len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n", ctlr->stats[FramesXmittedOk]); len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n", ctlr->stats[FramesRcvdOk]); len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->stats[FramesDeferred]); len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n", ctlr->stats[BytesRcvdOk]); len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", ctlr->stats[BytesRcvdOk+1]); . 1115a if(ctlr->busmaster == 2) ctlr->timer[1] += inb(port+Timer905) & 0xFF; else ctlr->timer[1] += inb(port+Timer) & 0xFF; . 1114c }while((status = STATUS(port)) & (interruptMask|interruptLatch)); . 1031,1032c if(x = inb(txstatus)) outb(txstatus, 0); . 1028a if(ctlr->busmaster == 2) txstatus = port+TxStatus905; else txstatus = port+TxStatus; . 965,966c if(ctlr->busmaster == 2) ctlr->timer[0] += inb(port+Timer905) & 0xFF; else ctlr->timer[0] += inb(port+Timer) & 0xFF; do{ . 961c status = STATUS(port); if(!(status & (interruptMask|interruptLatch))){ iunlock(&ctlr->wlock); return; } w = (status>>13) & 0x07; . 953c int port, status, s, txstatus, w, x; . 429c long timer[2]; . 346a Timer905 = 0x001A, /* 8-bits */ TxStatus905 = 0x001B, /* 8-bits */ . ## diffname pc/etherelnk3.c 2001/0330 ## diff -e /n/emeliedump/2000/0921/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0330/sys/src/9/pc/etherelnk3.c 1867,1877c for(timeo = 0; timeo < 30; timeo++){ phystat = miir(port, phyaddr, 0x01); if(phystat & 0x20) break; XCVRDEBUG(" %2.2uX", phystat); delay(100); } XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01)); XCVRDEBUG("\n"); . 1863,1865c for(i = 0; i < 7; i++) XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i)); XCVRDEBUG("\n"); . 1854a . 1843,1852c /* * forgive me, but i am weak */ if(did == 0x9055 || did == 0x9200){ xcvr = xcvrMii; txrxreset(port); XCVRDEBUG("905[BC] reset ops 0x%uX\n", ins(port+ResetOp905B)); } else if(xcvr & autoSelect) . 1808,1809c * 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. . 1719c int anar, anlpar, phyaddr, phystat, timeo, xcvr; int busmaster, did, i, j, port, rxearly, rxstatus9, x; . 9,10c * rewrite all initialisation. . ## diffname pc/etherelnk3.c 2001/0504 ## diff -e /n/emeliedump/2001/0330/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0504/sys/src/9/pc/etherelnk3.c 2022c ctlr->rbp = rbpalloc(iallocb); if(ctlr->rbp == nil) panic("can't reset ethernet: out of memory"); . 2016,2017c else { ctlr->rbp = rbpalloc(iallocb); if(ctlr->rbp == nil) panic("can't reset ethernet: out of memory"); } . 1230c bp = iallocb(sizeof(Adapter)); if(bp == nil) return; . 470c bp = iallocb(sizeof(Etherpkt)); if(bp == nil) panic("can't allocate ethernet receive ring"); . ## diffname pc/etherelnk3.c 2001/0510 ## diff -e /n/emeliedump/2001/0504/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0510/sys/src/9/pc/etherelnk3.c 1445a /* * Not prepared to deal with memory-mapped * devices yet. */ if(!(p->mem[0].bar & 0x01)) continue; . ## diffname pc/etherelnk3.c 2001/0518 ## diff -e /n/emeliedump/2001/0510/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0518/sys/src/9/pc/etherelnk3.c 1914,1915c else{ /* 10BASE-T */ /* nothing to do */ } . 1908,1909c else if(anar & 0x0200){ /* 100BASE-T4 */ /* nothing to do */ } . ## diffname pc/etherelnk3.c 2001/0622 ## diff -e /n/emeliedump/2001/0518/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0622/sys/src/9/pc/etherelnk3.c 2074a addethercard("3C575", etherelnk3reset); . 2019c x = eepromdata(did, port, 0x0F); . 1873c phyaddr = (did == 0x5157)? 0: 24; . 1860a if (did == 0x5157) { ushort reset_opts; COMMAND(port, SelectRegisterWindow, Wstation); reset_opts = ins(port + ResetOp905B); reset_opts |= 0x0010; /* Invert LED */ outs(port + ResetOp905B, reset_opts); } . 1857c if(did == 0x9055 || did == 0x9200 || did == 0x5157){ . 1825c x = eepromdata(did, port, i); . 1781a case 0x5157: /* 3C575 Cyclone */ . 1773c switch(did = eepromdata(did, port, 0x03)){ . 1759a ether->mem = ap->cbfns; . 1757a did = ap->did; . 1752a did = 0; . 1719c EEPROMCMD(port, (did == 0x5157)? EepromRead8bRegister: EepromReadRegister, offset); . 1714c eepromdata(int did, int port, int offset) . 1462c bar = 0; if (p->did == 0x5157) { /* Map the CardBus functions */ bar = pcicfgr32(p, PciBAR2); print("tcmp59Xpci: CardBus functions at %.8uX\n", bar & ~KZERO); } tcmadapter(port, irq, p->tbdf, p->did, bar); . 1445a ulong bar; . 1434c tcmadapter(port, irq, BUSUNKNOWN, 0, 0); . 1388c tcmadapter(port, irq, BUSUNKNOWN, 0, 0); . 1238a ap->did = did; ap->cbfns = cbfns; . 1227c tcmadapter(int port, int irq, int tbdf, int did, ulong cbfns) . 1222a int did; ulong cbfns; . 1129a if (ether->mem) intrack3c575((ulong *)KADDR(ether->mem)); . 610a if (ether->mem) /* This must be a cardbus card. Acknowledge the interrupt */ intrack3c575(KADDR(ether->mem)); . 607d 574a intrack3c575(ulong *cbfns) { cbfns[1] = 0x8000; } static void . 573a /* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */ . 169a EepromRead8bRegister = 0x0230, . ## diffname pc/etherelnk3.c 2001/0623 ## diff -e /n/emeliedump/2001/0622/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0623/sys/src/9/pc/etherelnk3.c 2035a ctlr->did = did; ctlr->cbfns = cbfns; . 1788c cbfns = ap->cbfns; . 1779a cbfns = 0; . 1761a ulong cbfns; . 1485c print("tcmp59Xpci: CardBus functions at %.8ulX\n", bar & ~KZERO); . 1141c if (ctlr->cbfns) intrack3c575((ulong *)KADDR(ctlr->cbfns)); . 998a if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) { print("#l%d: Card ejected?\n", ether->ctlrno); iunlock(&ctlr->wlock); return; } . 962a static int ejectable(int did) { switch (did) { case 0x5157: return 1; default: return 0; } } . 620c intrack3c575(KADDR(ctlr->cbfns)); . 618c if (ctlr->cbfns) . 443a int did; /* Controller's device ID */ ulong cbfns; /* CardBus functions */ . ## diffname pc/etherelnk3.c 2001/0720 ## diff -e /n/emeliedump/2001/0623/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0720/sys/src/9/pc/etherelnk3.c 1813,1814c // *bpp = bp->next; // HIRO. // freeb(bp); . ## diffname pc/etherelnk3.c 2001/0920 ## diff -e /n/emeliedump/2001/0720/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0920/sys/src/9/pc/etherelnk3.c 1493a print("tcm59Xpci: Allocating port %X\n", port); . ## diffname pc/etherelnk3.c 2001/0925 ## diff -e /n/emeliedump/2001/0920/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0925/sys/src/9/pc/etherelnk3.c 1494,1495c if((port = ioalloc((port == 0)? -1: port, p->mem[0].size, 0, "tcm59Xpci")) < 0){ . ## diffname pc/etherelnk3.c 2002/0315 ## diff -e /n/emeliedump/2001/0925/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0315/sys/src/9/pc/etherelnk3.c 1912c if(did == 0x9055 || did == 0x7646 || did == 0x9200 || did == 0x5157){ . 1835a case 0x7646: /* 3CSOHO100-TX */ . ## diffname pc/etherelnk3.c 2002/0403 ## diff -e /n/emeliedump/2002/0315/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0403/sys/src/9/pc/etherelnk3.c 1815a ap->active = 1; . 1807a if(ap->active) continue; . 1529c if(ether->type==nil || !cistrcmp(ether->type, tcmpcmcia[i])){ . 1259a int active; . ## diffname pc/etherelnk3.c 2002/0405 ## diff -e /n/emeliedump/2002/0403/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0405/sys/src/9/pc/etherelnk3.c 1170c ctlr->timer[1] += inb(port+Timerx) & 0xFF; . 1002c ctlr->timer[0] += inb(port+Timerx) & 0xFF; . 184c Timerx = 0x000A, . ## diffname pc/etherelnk3.c 2002/0411 ## diff -e /n/emeliedump/2002/0405/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0411/sys/src/9/pc/etherelnk3.c 1499c . 1496,1497c 0, "tcm59Xpci")) < 0) . 1456c . 1453,1454c if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0) . 1394d 1391,1392c if(ioalloc(port, 0x10, 0, "tcm509isa") < 0) . ## diffname pc/etherelnk3.c 2002/0414 ## diff -e /n/emeliedump/2002/0411/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0414/sys/src/9/pc/etherelnk3.c 852a coherence(); . 779a coherence(); . ## diffname pc/etherelnk3.c 2002/0502 ## diff -e /n/emeliedump/2002/0414/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0502/sys/src/9/pc/etherelnk3.c 28a * 7646 PCI 3CSOHO100-TX Fast Etherlink Small Office Connect (9050 downsized clone) . ## diffname pc/etherelnk3.c 2002/0510 ## diff -e /n/emeliedump/2002/0502/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0510/sys/src/9/pc/etherelnk3.c 1917c if(ismii){ . 1843a } . 1838,1842c if(BUSTYPE(ether->tbdf) != BusPCI){ ismii = 0; . 1832a case 0x9055: case 0x9200: case 0x7646: /* 3CSOHO100-TX */ case 0x5157: /* 3C575 Cyclone */ case 0x6056: ismii = 1; /*FALLTHROUGH*/ . 1830a ismii = 0; . 1779c int busmaster, did, i, ismii, j, port, rxearly, rxstatus9, x; . ## diffname pc/etherelnk3.c 2002/0511 ## diff -e /n/emeliedump/2002/0510/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0511/sys/src/9/pc/etherelnk3.c 2150,2151c addethercard("elnk3", etherelnk3reset); addethercard("3C509", etherelnk3reset); . 2134d 2127c COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); . 2096c x = eepromdata(ctlr, 0x0F); . 2066,2075d 2062d 2058,2060d 2055,2056c * and enable statistics collection. . 2053c * Clear out the . 2045c if(ctlr->busmaster == 2) . 2022d 2019,2020c if((ctlr->did & 0xFF00) == 0x5900) ctlr->busmaster = 0; . 2008d 1993d 1947c if(ctlr->did == 0x5157) phyaddr = 0; else phyaddr = 24; . 1940,1941c switch(ctlr->xcvr){ . 1936,1938c XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did); . 1934a break; . 1927c if(ctlr->did == 0x5157) { . 1923,1924c switch(ctlr->did){ default: if(ctlr->xcvr & autoSelect) ctlr->xcvr = autoselect(ctlr); break; case 0x6056: case 0x5157: case 0x7646: case 0x9055: case 0x9200: ctlr->xcvr = xcvrMii; . 1917c ctlr->xcvr = media[j].xcvr; . 1906c XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr); . 1891c x = eepromdata(ctlr, i); . 1880a if(ctlr->rxearly >= 2048) ctlr->ts = 2; . 1876,1878c ctlr->xcvr |= autoSelect; ctlr->rxearly = 2044; ctlr->rxstatus9 = 1; . 1874c ctlr->xcvr = ((x & xcvrMask9)>>14)<<20; . 1871c ctlr->busmaster = 0; . 1868d 1864,1866c ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); ctlr->rxearly = 8188; ctlr->rxstatus9 = 0; . 1852,1861c case 0x5900: /* 3C590-[TP|COMBO|TPO] */ case 0x5920: /* 3C592-[TP|COMBO|TPO] */ case 0x5950: /* 3C595-TX */ case 0x5951: /* 3C595-T4 */ case 0x5952: /* 3C595-MII */ case 0x5970: /* 3C597-TX */ case 0x5971: /* 3C597-T4 */ case 0x5972: /* 3C597-MII */ ctlr->busmaster = 1; . 1849,1850c ctlr->busmaster = 2; . 1841,1847c case 0x9000: /* 3C900-TPO */ case 0x9001: /* 3C900-COMBO */ case 0x9005: /* 3C900B-COMBO */ case 0x9050: /* 3C905-TX */ case 0x9051: /* 3C905-T4 */ if(BUSTYPE(ether->tbdf) != BusPCI) . 1839c case 0x7646: /* 3CSOHO100-TX */ case 0x9055: /* 3C905B-TX */ case 0x9200: /* 3C905C-TX */ . 1837a ctlr->cbfns = KADDR(pcicfgr32(ctlr->pcidev, PciBAR2)); /*FALLTHROUGH*/ . 1831,1836c switch(ctlr->did = eepromdata(ctlr, 0x03)){ . 1826a ether->ctlr = ctlr; port = ctlr->port; ether->port = port; ether->irq = ctlr->irq; if(ctlr->pcidev != nil) ether->tbdf = ctlr->pcidev->tbdf; else ether->tbdf = BUSUNKNOWN; . 1824c if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0) . 1822d 1811,1819c if(ether->port == 0 || ether->port == ctlr->port){ ctlr->active = 1; . 1803,1809c for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ if(ctlr->active) . 1785,1786c int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x; . 1783a uchar ea[Eaddrlen]; . 1778,1782c char *p; . 1769c if(ctlr->pcidev && ctlr->pcidev->did == 0x5157) EEPROMCMD(port, EepromRead8bRegister, offset); else EEPROMCMD(port, EepromReadRegister, offset); . 1765a int port; port = ctlr->port; . 1764c eepromdata(Ctlr* ctlr, int offset) . 1744c setxcvr(ctlr, xcvr10BaseT); . 1731c setxcvr(ctlr, xcvr100BaseTX); . 1704c port = ctlr->port; if(ctlr->rxstatus9){ . 1695,1696c int media, port, x; . 1693c autoselect(Ctlr* ctlr) . 1663,1678d 1549c port = ctlr->port; if(ctlr->rxstatus9){ . 1547c int port, x; . 1545c setxcvr(Ctlr* ctlr, int xcvr) . 1540,1541c return nil; . 1530,1538c if(cistrcmp(ether->type, tcmpcmcia[i])) continue; return tcmadapter(ether->port, ether->irq, nil); . 1528a if(ether->type == nil) return nil; . 1524c static Ctlr* . 1505,1512c tcmadapter(port, irq, p); . 1499c } . 1497c 0, "tcm59Xpci")) < 0){ print("tcm59Xpci: port 0x%uX in use\n", port); . 1487,1488d 1475c tcmadapter(port, irq, nil); . 1457c } . 1455c if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){ print("tcm5XXeisa: port 0x%uX in use\n", port); . 1430c tcmadapter(port, irq, nil); . 1396a } . 1395c if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){ print("tcm509isa: port 0x%uX in use\n", port); . 1284,1285c if(ctlrhead != nil) ctlrtail->next = ctlr; else ctlrhead = ctlr; ctlrtail = ctlr; return ctlr; . 1274,1282c ctlr = malloc(sizeof(Ctlr)); ctlr->port = port; ctlr->irq = irq; if(pcidev != nil){ ctlr->pcidev = pcidev; } . 1271,1272c Ctlr *ctlr; . 1258,1269c static Ctlr* tcmadapter(int port, int irq, Pcidev* pcidev) . 1197a len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts); . 1174c ctlr->timer[1] += inb(port+TIMER) & 0xFF; . 1172c ctlr->timer[1] += inb(port+TIMER905) & 0xFF; . 1167c if(ctlr->cbfns != nil) intrack3c575(ctlr->cbfns); . 1006c ctlr->timer[0] += inb(port+TIMER) & 0xFF; . 1004c ctlr->timer[0] += inb(port+TIMER905) & 0xFF; . 995a ctlr->bogusinterrupts++; . 856d 782d 623,625c /* * If this is a CardBus card, acknowledge any interrupts. */ if(ctlr->cbfns != nil) intrack3c575(ctlr->cbfns); . 456a static Ctlr* ctlrhead; static Ctlr* ctlrtail; . 454a ulong* cbfns; /* CardBus functions */ . 446,452c int xcvr; /* transceiver type */ int rxstatus9; /* old-style RxStatus register */ int rxearly; /* RxEarlyThreshold */ int ts; /* threshold shift */ . 432c long interrupts; /* statistics */ long bogusinterrupts; . 425c int ndn; /* full-busmaster -based transmission */ . 420c int nup; /* full-busmaster -based reception */ . 416c Block* txbp; /* FIFO -based transmission */ . 414c Block* rbp; /* receive buffer */ . 411a Lock wlock; /* window access */ . 409,410c typedef struct Ctlr Ctlr; typedef struct Ctlr { int port; Pcidev* pcidev; int irq; Ctlr* next; int active; int did; . 400,401c ulong np; /* next pointer */ ulong control; /* FSH or UpPktStatus */ . 358c FreeTIMER = 0x0034, /* 16-bits */ . 349c TIMER905 = 0x001A, /* 8-bits */ . 251c deferTIMERSelect = 0x001E, /* mask */ . 186c TIMER = 0x000A, . 10,43d ## diffname pc/etherelnk3.c 2002/0524 ## diff -e /n/emeliedump/2002/0511/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0524/sys/src/9/pc/etherelnk3.c 1871a case 0x6056: . 1870c case 0x4500: . 1779a case 0x4500: /* 3C450 HomePNA Tornado */ . ## diffname pc/etherelnk3.c 2002/0530 ## diff -e /n/emeliedump/2002/0524/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0530/sys/src/9/pc/etherelnk3.c 1500c ctlr = tcmadapter(ether->port, ether->irq, nil); ctlr->active = 1; return ctlr; . 1492a Ctlr *ctlr; . 1245,1247c ctlr->pcidev = pcidev; .