📄 if_fei.c
字号:
pDrvCtrl->pCFD->pPrev = pCFD; /* * N.B. * Use RFD_C_EL to tie the end of the RBD ring, and not RFD_C_SUSP * This is because we want the option of inserting a new RFD into * the ring on the fly (i.e., via an SCB_RUSTART command). Why would * we do this? Buffer loaning.... A suspend/resume reception model * will not allow us to do this, so we must use an idle/start model. */ /* first ready RFD pointer */ pDrvCtrl->pRFD = pRFD = (RFD *) ((UINT32) pCFD + sizeCFD); /*initialize the RFD ring,some of the fields will be done later in feiInit*/ for (ix = 0; ix < nRFD; ix++, pRFD++) { /* system bus address to link field */ LINK_WR (&pRFD->link, LOCAL_TO_SYS_ADDR(unit, ((UINT32) pRFD + sizeRFD)) ); /* previous RFD pointer */ pRFD->pPrev = (RFD *) ((UINT32) pRFD - sizeRFD); LINK_WR (&pRFD->pRBD, 0xffffffff); /* no RBDs used */ pRFD->bufSize = ETHERMTU + EH_SIZE; pRFD->refCnt = 0; pRFD->actualCnt = 0; } pRFD--; LINK_WR (&pRFD->link,LOCAL_TO_SYS_ADDR(unit,(UINT32)pDrvCtrl->pRFD) ) ; /* tie end of RFD ring, use system bus address */ pDrvCtrl->pRFD->pPrev = pRFD; /* initialize the RFD loanable buffers */ pDrvCtrl->pRFDL = NULL; pRFD++; for (ix = 0; ix < nRFDLoan; ix++, pRFD++) { pRFD->rfdStatus = 0; pRFD->rfdCommand = 0; LINK_WR (&pRFD->pRBD, 0xffffffff); /* no RBDs used */ pRFD->bufSize = ETHERMTU + EH_SIZE; pRFD->refCnt = 0; pRFD->actualCnt = 0; pRFD->pPrev = pDrvCtrl->pRFDL; /* link into head of list */ pDrvCtrl->pRFDL = pRFD; } if ((pDrvCtrl->txRestart = txWd = wdCreate ()) == NULL) goto error; feiReset (unit); /* reset the chip */ /* CU and RU should be idle following feiReset() */ if (feiSCBCommand (pDrvCtrl, SCB_C_CULDBASE, TRUE, 0x0) == ERROR) goto error; if (feiSCBCommand (pDrvCtrl, SCB_C_RULDBASE, TRUE, 0x0) == ERROR) goto error; /* connect the interrupt handler */ if (pDrvCtrl->board.vector) { if (SYS_INT_CONNECT(INUM_TO_IVEC (pDrvCtrl->board.vector), feiInt, pDrvCtrl) == ERROR) goto error; } /* get our ethernet hardware address */ bcopy ((char *)pDrvCtrl->board.enetAddr, (char *) pDrvCtrl->idr.ac_enaddr, sizeof (pDrvCtrl->board.enetAddr)); if (feiDiag (unit) == ERROR) /* run diagnostics */ goto error; if (feiIASetup (unit) == ERROR) /* setup address */ goto error; if (feiConfig (unit) == ERROR) /* configure chip */ goto error; pDrvCtrl->rxHandle = FALSE; pDrvCtrl->txStall = FALSE; pDrvCtrl->attached = TRUE; if (feiInit (unit) == ERROR) goto error; return (OK);error: /* error cleanup */ { if (memAlloc) cacheDmaFree (memBase); if (txWd) wdDelete (txWd); return (ERROR); } }/********************************************************************************* feiInit - initialize the 82557 device** This routine initializes the 82557 device and brings it up to an operational* state. The driver must have already been attached with the feiattach()* routine.** This routine is called at boot time and by feiWatchDog() if a reset is* required.** RETURNS: OK, or ERROR if the device could not be initialized.*/LOCAL STATUS feiInit ( int unit /* unit number */ ) { DRV_CTRL * pDrvCtrl = &drvCtrl [unit]; RFD * pRFD; UINT16 event; int ix; if (!pDrvCtrl->attached) /* must have been attached */ return (ERROR); /* wait for the receive handler to catch up to the [reset] 557 */ for (ix = (FEI_INIT_TMO * feiClkRate); --ix;) { if (!pDrvCtrl->rxHandle) break; taskDelay (1); } if (!ix) return (ERROR); /* initiailize RFD ring */ pRFD = pDrvCtrl->pRFD; do { pRFD->rfdStatus = 0; pRFD->rfdCommand = 0; pRFD = (RFD *) SYS_TO_LOCAL_ADDR(unit,(UINT32)LINK_RD (&pRFD->link)); } while (pRFD != pDrvCtrl->pRFD); pRFD->pPrev->rfdCommand = RFD_C_EL; if (feiNOP (unit) == ERROR) /* put CU into suspended state */ return (ERROR); event = pDrvCtrl->pCSR->scbStatus; /* save int events */ pDrvCtrl->pCSR->scbStatus = event; BOARD_INT_ACK(unit); /* acknowledge interrupts */ I82557_INT_ENABLE; BOARD_INT_ENABLE(unit); /* enable interrupts at system */ /* mark the interface as up */#ifdef BSD43_DRIVER pDrvCtrl->idr.ac_if.if_flags |= (IFF_UP | IFF_RUNNING | IFF_NOTRAILERS);#else pDrvCtrl->idr.ac_if.if_flags |= (IFF_MULTICAST| IFF_UP | IFF_RUNNING | IFF_NOTRAILERS);#endif /* startup the receiver */ if (feiSCBCommand (pDrvCtrl, SCB_C_RUSTART, TRUE, (UINT32 *)LOCAL_TO_SYS_ADDR(unit, (UINT32) pRFD) ) == ERROR) return (ERROR); return (OK); }/********************************************************************************* feiReset - reset the `fei' interface** This routine marks the interface as inactive and resets the chip. It brings* down the interface to a non-operational state. To bring the interface back* up, feiInit() must be called.** RETURNS: N/A*/LOCAL void feiReset ( int unit /* unit number of interface */ ) { DRV_CTRL * pDrvCtrl = &drvCtrl [unit]; BOARD_INT_DISABLE(unit); /* disable system interrupt */ I82557_INT_DISABLE; /* disable chip interrupt */ /* mark the interface as down */ pDrvCtrl->idr.ac_if.if_flags &= ~(IFF_UP | IFF_RUNNING); /* issue a selective reset to the 82557 chip */ PORT_WR (&pDrvCtrl->pCSR->port, FEI_PORT_SELRESET); taskDelay (2); /* wait for the reset... */ }/********************************************************************************* feiIoctl - interface control request routine** This routine processes the interface's ioctl() request.** RETURNS: OK, or EINVAL if <cmd> is not recognized.*/LOCAL int feiIoctl ( IFNET * pIfNet, /* interface to act upon */ int cmd, /* command to process */ caddr_t data /* command-specific data */ ) { DRV_CTRL * pDrvCtrl = & drvCtrl [pIfNet->if_unit]; int retVal = OK; int s = splimp (); /* not taken in some calling funcs */ switch (cmd) { case SIOCSIFADDR: /* set interface address */#ifdef BSD43_DRIVER retVal = set_if_addr (pIfNet, data, pDrvCtrl->idr.ac_enaddr);#else ((struct arpcom *)pIfNet)->ac_ipaddr = IA_SIN (data)->sin_addr; arpwhohas ((struct arpcom *) pIfNet, &IA_SIN (data)->sin_addr);#endif break; case SIOCSIFFLAGS: /* set interface flags */ break;#ifdef BSD44_DRIVER case SIOCADDMULTI: case SIOCDELMULTI: pDrvCtrl->idr.ac_if.if_flags |= IFF_PROMISC; retVal = OK; break;#endif default: retVal = EINVAL; } splx (s); return (retVal); }#ifdef BSD43_DRIVER/********************************************************************************* feiOutput - interface output routine** This routine simply calls ether_output(). ether_output() resolves* the hardware addresses and calls feiTxStartup() with the unit number* passed as an argument.** RETURNS: OK if successful, otherwise `errno'.*/LOCAL int feiOutput ( IDR * pIDR, MBUF * pMbuf, SOCK * pDestAddr ) { return (ether_output ((IFNET *) pIDR, pMbuf, pDestAddr, (FUNCPTR) feiTxStartup, pIDR)); }#endif/********************************************************************************* feiTxStartup - transmit frames on the device** Look for packets on the IP output queue; output frames if there is anything* there.** This routine is called from several possible threads. Each will be described* below.** The first (and most common thread) is when a user task requests the* transmission of data. Under BSD 4.3, this will cause feiOutput() to be * called, which will cause ether_output() to be called, which will cause this * routine to be called (usually). This routine will not be called if * ether_output() finds that our interface output queue is full. In this case, * the outgoing data will be thrown out. BSD 4.4 uses a slight different model* in which the generic ether_output() is called directly, followed by a call * to this routine.** The second, and most obscure thread, is when the reception of certain* packets causes an immediate (attempted) response. For example, ICMP echo* packets (ping), and ICMP "no listener on that port" notifications. All* functions in this driver that handle the reception side are executed in the* context of netTask(). Always. So, in the case being discussed, netTask()* will receive these certain packets, cause IP to be stimulated, and cause the* generation of a response to be sent. We then find ourselves following the* first thread explained above, with the important distinction that the* context is that of netTask().** The third thread occurs when this routine runs out of CFDs and returns. If* this occurs when our output queue is not empty, this routine would typically* not get called again until new output was requested. Even worse, if the* output queue was also full, this routine would never get called again and* we would have a lock state. It DOES happen. To guard against this,* we turn on a watchdog timer to scedule us again after an appropriate timeout.** Note that this function is ALWAYS called between an splnet() and an splx().* This is true because netTask(), and ether_output() take care of* this when calling this function. Therefore, no calls to these spl functions* are needed anywhere in this output thread. Thus, mutual exclusion is* guaranteed.** RETURNS: N/A*/LOCAL void feiTxStartup (#ifdef BSD43_DRIVER int unit#else DRV_CTRL * pDrvCtrl #endif ) {#ifdef BSD43_DRIVER DRV_CTRL * pDrvCtrl = &drvCtrl [unit];#else int unit = pDrvCtrl->idr.ac_if.if_unit;#endif CFD *pCFD = pDrvCtrl->pCFD; CFD *pCFDNext; MBUF * pMbuf; int length; int status = OK; /* loop until no more tx packets or we are out of tx resources */ while (pDrvCtrl->idr.ac_if.if_snd.ifq_head) { pCFDNext = (CFD *) SYS_TO_LOCAL_ADDR(unit,(UINT32)LINK_RD(&pCFD->link)); if (!(pCFDNext->cfdStatus & CFD_S_COMPLETE)) /* next CFD free ? */ { pDrvCtrl->txStall = TRUE; wdStart (pDrvCtrl->txRestart, FEI_TX_RESTART_TMO * feiClkRate, (FUNCPTR) feiTxRestart, unit); break; } if (pDrvCtrl->txStall) wdCancel (pDrvCtrl->txRestart); IF_DEQUEUE (&pDrvCtrl->idr.ac_if.if_snd, pMbuf); /* dequeue a packet */ copy_from_mbufs (pCFD->cfdTcb.enetHdr, pMbuf, length); length = max (ETHERSMALL, length); /* short frames are padded */ if ((etherOutputHookRtn != NULL) && (*etherOutputHookRtn) (&pDrvCtrl->idr, pCFD->cfdTcb.enetHdr, length)) continue; /* hook routine ate packet */ /* get frame ready for transmission */ pCFD->cfdTcb.count = (length & 0x3fff) | TCB_CNT_EOF; pCFD->cfdStatus = 0; pCFD->cfdCommand = CFD_C_XMIT | CFD_C_SUSP; pCFD->pPrev->cfdCommand &= ~CFD_C_SUSP; /* hook frame into CBL */ /* undates the first ready RFD pointer */ pDrvCtrl->pCFD = (CFD *)SYS_TO_LOCAL_ADDR(unit,(UINT32)LINK_RD(&pCFD->link) ); /* Bump the statistic counter. */ pDrvCtrl->idr.ac_if.if_opackets++; /* resume CU operation ? (check suspended, not sent yet, cmd return) */ if (((pDrvCtrl->pCSR->scbStatus & SCB_S_CUMASK) == SCB_S_CUSUSP) && (pCFD->cfdStatus == 0) ) { status =feiSCBCommand (pDrvCtrl, SCB_C_CURESUME, FALSE, 0); if (status == ERROR) break; } } /* make sure the last CFD is transmitted */ if (pCFD->cfdStatus == 0) feiSCBCommand (pDrvCtrl, SCB_C_CURESUME, FALSE, 0); }/********************************************************************************* feiTxRestart - reschedule transmit processing** Reschedule transmit processing because we previously had no available* CFDs.** RETURNS: N/A*/LOCAL void feiTxRestart ( int unit ) { DRV_CTRL * pDrvCtrl = &drvCtrl [unit]; pDrvCtrl->txStall = FALSE;#ifdef BSD43_DRIVER (void) netJobAdd ((FUNCPTR) feiTxStartup, unit, 0, 0, 0, 0);#else (void) netJobAdd ((FUNCPTR) feiTxStartup, (int)pDrvCtrl, 0, 0, 0, 0);#endif }/********************************************************************************* feiInt - entry point for handling interrupts from the 82557** The interrupting events are acknowledged to the device, so that the device* will de-assert its interrupt signal. The amount of work done here is kept* to a minimum; the bulk of the work is defered to the netTask.** RETURNS: N/A*/LOCAL void feiInt ( DRV_CTRL * pDrvCtrl ) { UINT16 event; int unit = pDrvCtrl->idr.ac_if.if_unit; event = pDrvCtrl->pCSR->scbStatus; /* save int events */ if ((event & SCB_S_STATMASK) == 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -