etherelnk3.c
来自「这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易」· C语言 代码 · 共 2,135 行 · 第 1/4 页
C
2,135 行
if(on) filter |= receiveAllFrames; COMMAND(port, SetRxFilter, filter);}static voidmulticast(void* arg, uchar *addr, int on){ int filter, port; Ether *ether; USED(addr, on); ether = (Ether*)arg; port = ether->port; filter = receiveBroadcast|receiveIndividual; if(ether->nmaddr) filter |= receiveMulticast; if(ether->prom) filter |= receiveAllFrames; COMMAND(port, SetRxFilter, filter);}/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */static voidintrackcb(ulong *cbfn){ cbfn[1] = 0x8000;}static voidattach(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; if(ctlr->busmaster == 1) x &= ~(rxEarly|rxComplete); else{ if(ctlr->dnenabled) x &= ~transferInt; if(ctlr->upenabled) x &= ~(rxEarly|rxComplete); } COMMAND(port, SetIndicationEnable, x); COMMAND(port, SetInterruptEnable, x); COMMAND(port, RxEnable, 0); COMMAND(port, TxEnable, 0); /* * If this is a CardBus card, acknowledge any interrupts. */ if(ctlr->cbfn != nil) intrackcb(ctlr->cbfn); /* * Prime the busmaster channel for receiving directly into a * receive packet buffer if necessary. */ if(ctlr->busmaster == 1) startdma(ether, PADDR(ctlr->rbp->rp)); else{ if(ctlr->upenabled) outl(port+UpListPtr, PADDR(&ctlr->uphead->np)); } ctlr->attached = 1; iunlock(&ctlr->wlock);}static voidstatistics(Ether* ether){ int port, i, 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); for(i = 0; i < UpperFramesOk; i++) ctlr->stats[i] += inb(port+i) & 0xFF; u = inb(port+UpperFramesOk) & 0xFF; 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; switch(ctlr->xcvr){ case xcvrMii: case xcvr100BaseTX: case xcvr100BaseFX: 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; 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. */ for(;;){ if(ctlr->txbp){ bp = ctlr->txbp; ctlr->txbp = 0; } else{ bp = qget(ether->oq); if(bp == nil) break; } len = ROUNDUP(BLEN(bp), 4); if(len+4 <= ins(port+TxFree)){ outl(port+Fifo, BLEN(bp)); outsl(port+Fifo, bp->rp, len/4); freeb(bp); ether->outpackets++; } else{ ctlr->txbp = bp; if(ctlr->txbusy == 0){ ctlr->txbusy = 1; COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); } break; } }}static voidtxstart905(Ether* ether){ Ctlr *ctlr; int port, stalled, timeo; Block *bp; 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; if(pd->bp){ freeb(pd->bp); pd->bp = nil; } ctlr->dnq--; pd = pd->next; } ctlr->dntail = pd; stalled = 0; 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(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; Block *bp; 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){ if(pd->control & upOverrun) ether->overflows++; if(pd->control & (upOversizedFrame|upRuntFrame)) ether->buffs++; if(pd->control & upAlignmentError) ether->frames++; if(pd->control & upCRCError) ether->crcs++; } 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); coherence(); } 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, rxerror, rxstatus; Ctlr *ctlr; Block *bp; 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){ if(ctlr->rxstatus9){ 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); if(rxerror & rxOverrun) ether->overflows++; if(rxerror & (oversizedFrame|runtFrame)) ether->buffs++; if(rxerror & alignmentError) ether->frames++; if(rxerror & crcError) ether->crcs++; } } /* * 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 = rbpalloc(iallocb)) == 0){ COMMAND(port, RxDiscard, 0); while(STATUS(port) & commandInProgress) ; if(ctlr->busmaster == 1) startdma(ether, PADDR(ctlr->rbp->rp)); continue; } /* * 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 || ctlr->busmaster == 2){ len = (rxstatus & rxBytes9); ctlr->rbp->wp = ctlr->rbp->rp + len; insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4)); } COMMAND(port, RxDiscard, 0); while(STATUS(port) & commandInProgress) ; if(ctlr->busmaster == 1) ctlr->rbp->wp = startdma(ether, PADDR(bp->rp)); etheriq(ether, ctlr->rbp, 1); ctlr->rbp = bp; }}static intejectable(int did){ switch (did) { case 0x5157: return 1; default: return 0; }}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))){ ctlr->bogusinterrupts++; 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); if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) { print("#l%d: Card ejected?\n", ether->ctlrno); iunlock(&ctlr->wlock); return; } 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);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?