?? etherelnk3.c
字號:
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("#l%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 voidpromiscuous(void* arg, int on){ int filter, port; Ether *ether; ether = (Ether*)arg; port = ether->port; filter = receiveBroadcast|receiveIndividual; if(ether->nmaddr) filter |= receiveMulticast; 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);}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); /* * 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 voidinterrupt(Ureg*, void* arg){ Ether *ether; int port, status, s, w, x; Ctlr *ctlr; ether = arg; port = ether->port; ctlr = ether->ctlr; ilock(&ctlr->wlock); w = (STATUS(port)>>13) & 0x07; COMMAND(port, SelectRegisterWindow, Wop); ctlr->interrupts++; ctlr->timer += inb(port+Timer) & 0xFF; while((status = STATUS(port)) & (interruptMask|interruptLatch)){ 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)){
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -