📄 if_eihk.c
字号:
); return (OK); }/********************************************************************************* eiReset - reset the ei interface** Mark interface as inactive and reset the chip.*/static void eiReset ( int unit ) { DRV_CTRL *pDrvCtrl; pDrvCtrl = & drvCtrl [unit]; pDrvCtrl->idr.ac_if.if_flags = 0; sys596IntDisable (unit); /* disable ints from the dev */ sys596Port (unit, PORT_RESET, NULL); /* reset the 596 chip */ }/********************************************************************************* eiIoctl - interface ioctl procedure** Process an interface ioctl request.*/static int eiIoctl ( IDR *pIDR, int cmd, caddr_t data ) { int error;#ifdef EI_DEBUG printf ("ei: ioctl: pIDR=%x cmd=%x data=%x\n", pIDR, cmd, data);#endif /* EI_DEBUG */ error = 0; switch (cmd) { case SIOCSIFADDR: ((struct arpcom *)pIDR)->ac_ipaddr = IA_SIN (data)->sin_addr; arpwhohas (pIDR, &IA_SIN (data)->sin_addr); break; case SIOCSIFFLAGS: /* Flags are set outside this module. No additional work to do. */ break; default: error = EINVAL; } return (error); }#ifdef BSD43_DRIVER/********************************************************************************* eiOutput - interface output routine.** This is the entry point for packets arriving from protocols above. This* routine merely calls our specific output routines that eventually lead* to a transmit to the network.** RETURNS: 0 or appropriate errno*/static int eiOutput ( IDR * pIDR, MBUF * pMbuf, SOCK * pDestAddr ) {#ifdef EI_DEBUG printf ("ei: output: pIDR=%x pMbuf=%x pDestAddr=%x\n", pIDR, pMbuf, pDestAddr);#endif /* EI_DEBUG */ return (ether_output ((IFNET *)pIDR, pMbuf, pDestAddr, (FUNCPTR) eiTxStartup, pIDR)); }#endif/********************************************************************************* eiTxStartup - start output on the chip** Looks for any action on the queue, and begins output 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 eiOutput() to be * called, which calls ether_output(), which will usually call this routine.* 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 slightly different model in which the generic ether_output()* routine 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* thread explained in the second example, with the important distinction that* the context is that of netTask().** The third thread occurs when this routine runs out of TFDs 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, the* transmit clean-up handler detects the out-of-TFDs state and calls this* function. The clean-up handler also runs from netTask.** 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.*/#ifdef BSD43_DRIVERstatic void eiTxStartup ( int unit ) { DRV_CTRL * pDrvCtrl = & drvCtrl [unit];#elsestatic void eiTxStartup ( DRV_CTRL * pDrvCtrl /* driver control structure */ ) {#endif MBUF * pMbuf; int length; TFD * pTfd; /* * Loop until there are no more packets ready to send or we * have insufficient resources left to send another one. */ while (pDrvCtrl->idr.ac_if.if_snd.ifq_head) { /* get free tfd */ pTfd = (TFD *) eiQGet ((EI_LIST *)&pDrvCtrl->tfdQueue); if (pTfd == NULL) break; /* out of TFD's */ IF_DEQUEUE (&pDrvCtrl->idr.ac_if.if_snd, pMbuf); /* dequeue a packet */ copy_from_mbufs ((ETH_HDR *)pTfd->enetHdr, pMbuf, length); length = max (ETHERSMALL, length); if ((etherOutputHookRtn != NULL) && (* etherOutputHookRtn) (&pDrvCtrl->idr, (ETH_HDR *)pTfd->enetHdr, length)) continue; pTfd->count = length & ~0xc000; /* fill in length */ eiTxQPut (pDrvCtrl, pTfd);#ifndef BSD43_DRIVER /* BSD 4.4 ether_output() doesn't bump statistic. */ pDrvCtrl->idr.ac_if.if_opackets++;#endif } }/********************************************************************************* eiInt - entry point for handling interrupts from the 82596** The interrupting events are acknowledged to the device, so that the device* will deassert its interrupt signal. The amount of work done here is kept* to a minimum; the bulk of the work is defered to the netTask. Several flags* are used here to synchronize with task level code and eliminate races.*/void eiInt ( DRV_CTRL *pDrvCtrl ) { UINT16 event; SCB *pScb; int unit; pScb = pDrvCtrl->pScb; unit = pDrvCtrl->idr.ac_if.if_unit; event = pScb->scbStatus & (SCB_S_CX | SCB_S_FR | SCB_S_CNA | SCB_S_RNR);#ifdef EI_DEBUG logMsg ("ei: interrupt: event=%x\n", event, 0, 0, 0, 0, 0);#endif /* EI_DEBUG */ eiCommand (pDrvCtrl, event); /* ack the events */ sys596IntAck (unit); /* ack 596 interrupt */ /* Handle transmitter interrupt */ if (event & SCB_S_CNA) /* reclaim tx tfds */ { pDrvCtrl->wdTxTimeout = 0; /* reset timeout count */ if (pDrvCtrl->cleanQueue.head == NULL) /* clean queue empty */ { pDrvCtrl->cleanQueue.head = pDrvCtrl->cblQueue.head; /* new head */ pDrvCtrl->cleanQueue.tail = pDrvCtrl->cblQueue.tail; /* new tail */ } else /* concatenate queues */ { pDrvCtrl->cleanQueue.tail->pNext = (EI_NODE*) pDrvCtrl->cblQueue.head; pDrvCtrl->cleanQueue.tail = pDrvCtrl->cblQueue.tail; } if (!pDrvCtrl->txCleaning) /* not cleaning? */ { pDrvCtrl->txCleaning = TRUE; /* set flag */ netJobAdd ((FUNCPTR) eiTxCleanQ, (int) pDrvCtrl, 0, 0, 0, 0); /* defer cleanup */ } if (pDrvCtrl->txQueue.head != NULL) /* anything to flush? */ eiTxQFlush (pDrvCtrl); /* flush the tx q */ else pDrvCtrl->txIdle = TRUE; /* transmitter idle */ } /* Handle receiver interrupt */ if (event & SCB_S_FR) { pDrvCtrl->wdRxTimeout = 0; /* reset timeout count */ if (!(pDrvCtrl->rcvHandling)) { pDrvCtrl->rcvHandling = TRUE; (void) netJobAdd ((FUNCPTR) eiHandleRecvInt, (int) pDrvCtrl,0, 0, 0, 0); /* netTask processes */ } } }/********************************************************************************* eiTxCleanQ - checks errors in completed TFDs and moves TFDs to free queue** This routine is executed by netTask. It "cleans" the TFDs on the clean-up* queue by checking each one for errors and then returning the TFD to the* "free TFD" queue. The startup routine is sometimes called here to eliminate* the lock-out case where the driver input queue is full but there are no* TFDs available.*/static void eiTxCleanQ ( DRV_CTRL *pDrvCtrl ) { TFD *pTfd; BOOL needTxStart; int unit; unit = pDrvCtrl->idr.ac_if.if_unit; do { pDrvCtrl->txCleaning = TRUE; if (pDrvCtrl->tfdQueue.head == NULL) /* tfd queue empty */ needTxStart = TRUE; /* set flag */ else needTxStart = FALSE; /* process transmitted frames */ while (1) { /* Get TFD. No ints allowed while manipulating this queue. */ sys596IntDisable (unit); pTfd = (TFD*) eiQGet ((EI_LIST *)&pDrvCtrl->cleanQueue); sys596IntEnable (unit); if (pTfd == NULL) break; pDrvCtrl->idr.ac_if.if_collisions += /* add any colls */ (pTfd->status & CFD_S_RETRY) ? 16 : /* excessive colls */ (pTfd->status & CFD_S_COLL_MASK); /* some colls */ if (!(pTfd->status & CFD_S_OK)) /* packet not sent */ { pDrvCtrl->idr.ac_if.if_oerrors++; /* incr err count */ pDrvCtrl->idr.ac_if.if_opackets--; /* decr sent count */ } /* return to tfdQ */ eiQPut (unit,(EI_LIST *)&pDrvCtrl->tfdQueue, (EI_NODE*)pTfd); } if (needTxStart) /* check flag */#ifdef BSD43_DRIVER eiTxStartup (unit);#else eiTxStartup (pDrvCtrl);#endif pDrvCtrl->txCleaning = FALSE; } while (pDrvCtrl->cleanQueue.head != NULL); /* check again */ }/********************************************************************************* eiHandleRecvInt - task level interrupt service for input packets** This routine is called at task level indirectly by the interrupt* service routine to do any message received processing.*/static void eiHandleRecvInt ( DRV_CTRL *pDrvCtrl ) { RFD *pRfd; do { pDrvCtrl->rcvHandling = TRUE; /* interlock with eiInt() */ while ((pRfd = eiRxQGet (pDrvCtrl)) != NULL) if (eiReceive (pDrvCtrl, pRfd) == OK) eiRxQPut (pDrvCtrl, pRfd); pDrvCtrl->rcvHandling = FALSE; /* interlock with eiInt() */ } while (eiRxQFull (pDrvCtrl)); /* make sure rx q still empty */ }/********************************************************************************* eiReceive - pass a received frame to the next layer up** Strips the Ethernet header and passes the packet to the appropriate* protocol. The return value indicates if buffer loaning was used to hold* the data. A return value of OK means that loaning was not done, and it
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -