etherelnk3.c
来自「这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易」· C语言 代码 · 共 1,932 行 · 第 1/3 页
C
1,932 行
COMMAND(port, SelectRegisterWindow, Wdiagnostic); STATUS(port); ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD); break; } COMMAND(port, SelectRegisterWindow, w);}static voidtxstart(Ether* ether){ int port, len; Ctlr *ctlr; RingBuf *tb; 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. */ 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{ if(ctlr->txbusy == 0){ ctlr->txbusy = 1; COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); } break; } }}static voidtxstart905(Ether* ether){ Ctlr *ctlr; int port, stalled, timeo; RingBuf *tb; Pd *pd; ctlr = ether->ctlr; port = ether->port; /* * Free any completed packets. */ pd = ctlr->dntail; while(ctlr->dnq){ if(PADDR(&pd->np) == inl(port+DnListPtr)) break; ctlr->dnq--; pd = pd->next; } ctlr->dntail = pd; stalled = 0; while(ctlr->dnq < (ctlr->ndn-1)){ tb = ðer->tb[ether->ti]; if(tb->owner != Interface) break; pd = ctlr->dnhead->next; pd->np = 0; pd->control = dnIndicate|tb->len; memmove(pd->vaddr, tb->pkt, tb->len); pd->len = updnLastFrag|tb->len; tb->owner = Host; ether->ti = NEXT(ether->ti, ether->ntb); 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; } coherence(); ctlr->dnhead->np = PADDR(&pd->np); ctlr->dnhead->control &= ~dnIndicate; ctlr->dnhead = pd; if(ctlr->dnq == 0) ctlr->dntail = pd; ctlr->dnq++; ctlr->dnqueued++; } if(ctlr->dnq > ctlr->dnqmax) ctlr->dnqmax = ctlr->dnq; /* * If the adapter is not currently processing anything * and there is something on the queue, start it processing. */ if(inl(port+DnListPtr) == 0 && ctlr->dnq) outl(port+DnListPtr, PADDR(&ctlr->dnhead->np)); if(stalled) COMMAND(port, Stall, dnUnStall);}static voidtransmit(Ether* ether){ Ctlr *ctlr; int port, w; port = ether->port; ctlr = ether->ctlr; ilock(&ctlr->wlock); if(ctlr->dnenabled) txstart905(ether); else{ w = (STATUS(port)>>13) & 0x07; COMMAND(port, SelectRegisterWindow, Wop); txstart(ether); COMMAND(port, SelectRegisterWindow, w); } iunlock(&ctlr->wlock);}static voidreceive905(Ether* ether){ Ctlr *ctlr; int len, port, q; Pd *pd; RingBuf *rb; ctlr = ether->ctlr; port = ether->port; if(inl(port+UpPktStatus) & upStalled) ctlr->upstalls++; q = 0; for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){ if(!(pd->control & upError)){ rb = ðer->rb[ether->ri]; if (rb->owner == Interface) { len = pd->control & rxBytes; rb->len = len; memmove(rb->pkt, pd->vaddr, len); rb->owner = Host; ether->ri = NEXT(ether->ri, ether->nrb); } } pd->control = 0; COMMAND(port, Stall, upUnStall); q++; } ctlr->uphead = pd; ctlr->upqueued += q; if(q > ctlr->upqmax) ctlr->upqmax = q;}static voidreceive(Ether* ether){ int len, port, rxstatus; RingBuf *rb; Ctlr *ctlr; port = ether->port; ctlr = ether->ctlr; while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ if(ctlr->busmaster == 1 && (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) == 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); }elseif(debug) print("toss..."); }elseif(debug) print("error..."); /* * All done, discard the packet. */ COMMAND(port, RxDiscard, 0); while(STATUS(port) & commandInProgress) ; }}static voidinterrupt(Ureg*, void* arg){ Ether *ether; int port, status, s, txstatus, w, x; Ctlr *ctlr; ether = arg; port = ether->port; ctlr = ether->ctlr; ilock(&ctlr->wlock); status = STATUS(port); if(!(status & (interruptMask|interruptLatch))){ iunlock(&ctlr->wlock); return; } w = (status>>13) & 0x07; COMMAND(port, SelectRegisterWindow, Wop); ctlr->interrupts++; if(ctlr->busmaster == 2) ctlr->timer[0] += inb(port+Timer905) & 0xFF; else ctlr->timer[0] += inb(port+Timer) & 0xFF; do{ 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("#l%d: status 0x%uX, diag 0x%uX\n", ether->ctlrno, status, x); if(x & txOverrun){ if(ctlr->busmaster == 0) COMMAND(port, TxReset, 0); else COMMAND(port, TxReset, (updnReset|dmaReset)); COMMAND(port, TxEnable, 0); } if(x & rxUnderrun){ /* * This shouldn't happen... * Reset the receiver and restore the filter and RxEarly * threshold before re-enabling. * Need to restart any busmastering? */ COMMAND(port, SelectRegisterWindow, Wstate); s = (port+RxFilter) & 0x000F; COMMAND(port, SelectRegisterWindow, Wop); COMMAND(port, RxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, SetRxFilter, s); COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); COMMAND(port, RxEnable, 0); } status &= ~hostError; } if(status & (transferInt|rxComplete)){ receive(ether); status &= ~(transferInt|rxComplete); } if(status & (upComplete)){ COMMAND(port, AcknowledgeInterrupt, upComplete); receive905(ether); status &= ~upComplete; ctlr->upinterrupts++; } 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, taking care not to reset the dma logic * as a busmaster receive may be in progress. * For all conditions enable the transmitter. */ if(ctlr->busmaster == 2) txstatus = port+TxStatus905; else txstatus = port+TxStatus; s = 0; do{ if(x = inb(txstatus)) outb(txstatus, 0); s |= x; }while(STATUS(port) & txComplete); if(s & txUnderrun){ if(ctlr->dnenabled){ while(inl(port+PktStatus) & dnInProg) ; } COMMAND(port, SelectRegisterWindow, Wdiagnostic); while(ins(port+MediaStatus) & txInProg) ; COMMAND(port, SelectRegisterWindow, Wop); if(ctlr->txthreshold < ETHERMAXTU) ctlr->txthreshold += ETHERMINTU; } /* * 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. * On busy or badly configured networks maxCollisions can * happen frequently enough for messages to be annoying so * keep quiet about them by popular request. */ if(s & (txJabber|txUnderrun|maxCollisions)){ if(ctlr->busmaster == 0) COMMAND(port, TxReset, 0); else COMMAND(port, TxReset, (updnReset|dmaReset)); while(STATUS(port) & commandInProgress) ; COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); if(ctlr->busmaster == 2) outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); if(ctlr->dnenabled) status |= dnComplete; } if(s & ~(txStatusComplete|maxCollisions)) print("#l%d: txstatus 0x%uX, threshold %d\n", ether->ctlrno, s, ctlr->txthreshold); COMMAND(port, TxEnable, 0); status &= ~txComplete; status |= txAvailable; } if(status & txAvailable){ COMMAND(port, AcknowledgeInterrupt, txAvailable); ctlr->txbusy = 0; txstart(ether); status &= ~txAvailable; } if(status & dnComplete){ COMMAND(port, AcknowledgeInterrupt, dnComplete); txstart905(ether); status &= ~dnComplete; ctlr->dninterrupts++; } if(status & updateStats){ statistics(ether); status &= ~updateStats; } /* * Currently, this shouldn't happen. */ if(status & rxEarly){ COMMAND(port, AcknowledgeInterrupt, rxEarly); status &= ~rxEarly; } /* * Panic if there are any interrupts not dealt with. */ if(status & interruptMask) panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status); COMMAND(port, AcknowledgeInterrupt, interruptLatch); if(ctlr->cbfn != nil) intrackcb(ctlr->cbfn); }while((status = STATUS(port)) & (interruptMask|interruptLatch)); if(ctlr->busmaster == 2) ctlr->timer[1] += inb(port+Timer905) & 0xFF; else ctlr->timer[1] += inb(port+Timer) & 0xFF; COMMAND(port, SelectRegisterWindow, w); iunlock(&ctlr->wlock);}static voidtxrxreset(int port){ while(STATUS(port) & commandInProgress) ; COMMAND(port, RxReset, 0); while(STATUS(port) & commandInProgress) ;}static Ctlr*tcmadapter(int port, int irq, Pcidev* pcidev){ Ctlr *ctlr; ctlr = malloc(sizeof(Ctlr)); ctlr->port = port; ctlr->irq = irq; ctlr->pcidev = pcidev; ctlr->eepromcmd = EepromReadRegister; if(ctlrhead != nil) ctlrtail->next = ctlr; else ctlrhead = ctlr; ctlrtail = ctlr; return ctlr;}/* * 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 voididseq(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 a global reset is done here on all adapters it will confuse * any ISA cards configured for EISA mode. */ if(untag == 0){ outb(IDport, 0xD0); untag = 1; }}static ulongactivate(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. * A delay seems necessary 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 voidtcm509isa(void){ int irq, port; /* * Attempt to activate all adapters. 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); txrxreset(port); COMMAND(port, AcknowledgeInterrupt, 0xFF); irq = (ins(port+ResourceConfig)>>12) & 0x0F; tcmadapter(port, irq, nil); }}static voidtcm5XXeisa(void){ ushort x; int irq, port, slot; /* * Check if this is an EISA machine. * If not, nothing to do. */ if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4)) return; /* * Continue through the EISA slots looking for a match on both * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product. * If an adapter is found, select window 0, enable it and clear * out any lingering status and interrupts. */ for(slot = 1; slot < MaxEISA; slot++){ 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); txrxreset(port); COMMAND(port, AcknowledgeInterrupt, 0xFF); irq = (ins(port+ResourceConfig)>>12) & 0x0F; tcmadapter(port, irq, nil); }}static voidtcm59Xpci(void){ Pcidev *p; Ctlr *ctlr; int irq, port; p = nil; while(p = pcimatch(p, 0x10B7, 0)){ if(p->ccrb != 0x02 || p->ccru != 0) continue; /* * Not prepared to deal with memory-mapped * devices yet. */ if(!(p->mem[0].bar & 0x01)) continue; port = p->mem[0].bar & ~0x01; irq = p->intl;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?