📄 etherelnk3x.c
字号:
/* * Etherlink III and Fast EtherLink adapters. * To do: * autoSelect; * busmaster channel; * PCMCIA; * 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 "lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.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 */ autoSelect9 = 0x0080, 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 */ disableBadSsdDetect = 0x00000100, ramLocation = 0x00000200, /* 0 external, 1 internal */ ramPartition5to3 = 0x00000000, ramPartition3to1 = 0x00010000, ramPartition1to1 = 0x00020000, ramPartition3to5 = 0x00030000, ramPartitionMask = 0x00030000, 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, miiConnector = 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 { int txthreshold;} Ctlr;static voidattach(Ether* ether){ int port, x; 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. */ x = receiveBroadcast|receiveIndividual; COMMAND(port, SetRxFilter, x); x = interruptMask|interruptLatch; COMMAND(port, SetIndicationEnable, x); COMMAND(port, SetInterruptEnable, x); COMMAND(port, RxEnable, 0); COMMAND(port, TxEnable, 0);}static voidtransmit(Ether* ether){ int port, len; RingBuf *tb; /* * 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. */ port = ether->port; for(tb = ðer->tb[ether->ti]; tb->owner == Interface; tb = ðer->tb[ether->ti]){ len = ROUNDUP(tb->len, 4); if(len+4 <= ins(port+TxFree)){ outl(port+Fifo, tb->len); outsl(port+Fifo, tb->pkt, len/4); tb->owner = Host; ether->ti = NEXT(ether->ti, ether->ntb); } else{ COMMAND(port, SetTxAvailableThresh, len); break; } }}static voidreceive(Ether* ether){ int len, port, rxstatus; RingBuf *rb; port = ether->port; while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ /* * If there was an error, throw it away and continue. * The 3C5[078]9 has the error info in the status register * and the 3C59[0257] implement a separate RxError register. */ if((rxstatus & rxError) == 0){ /* * Packet received. Read it into the next free * ring buffer, if any. Must read len bytes padded * to a doubleword, can be picked out 32-bits at * a time. The CRC is already stripped off. */ rb = ðer->rb[ether->ri]; if(rb->owner == Interface){ len = (rxstatus & rxBytes9); rb->len = len; insl(port+Fifo, rb->pkt, HOWMANY(len, 4)); rb->owner = Host; ether->ri = NEXT(ether->ri, ether->nrb); } } /* * All done, discard the packet. */ COMMAND(port, RxDiscard, 0); while(STATUS(port) & commandInProgress) ; }}static voidstatistics(Ether* ether){ int i, port, w; port = ether->port; w = (STATUS(port)>>13) & 0x07; COMMAND(port, SelectRegisterWindow, Wop); COMMAND(port, SelectRegisterWindow, Wstatistics); for(i = 0; i < 0x0A; i++) inb(port+i); ins(port+BytesRcvdOk); ins(port+BytesXmittedOk); COMMAND(port, SelectRegisterWindow, w);}static voidinterrupt(Ureg*, void* arg){ Ether *ether; int port, status, txstatus, w, x; Ctlr *ctlr; ether = arg; port = ether->port; ctlr = ether->ctlr; w = (STATUS(port)>>13) & 0x07; COMMAND(port, SelectRegisterWindow, Wop); 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); } 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); status &= ~txComplete; status |= txAvailable; } if(status & txAvailable){ COMMAND(port, AcknowledgeInterrupt, txAvailable); 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -