📄 templatenetif.c
字号:
* templateOutput - 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 (see ether_output)** SEE ALSO:* ether_output*/LOCAL int templateOutput ( IDR * pIDR, MBUF * pMbuf, SOCK * pDestAddr ) { return (ether_output (&pIDR->ac_if, pMbuf, pDestAddr, (FUNCPTR) templateTxStart, pIDR)); }/********************************************************************************* templateTxStart - 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. This will cause templateOutput() 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.** 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.** RETURNS: N/A.*/LOCAL void templateTxStart ( int unit ) { MBUF * pMbuf; int length; TFD * pTfd = NULL; DRV_CTRL * pDrvCtrl; pDrvCtrl = & drvCtrl [unit]; /* * 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) { /* TODO - get a free Tx packet buffer */ if (pTfd == NULL) break; /* out of TFD's */ IF_DEQUEUE (&pDrvCtrl->idr.ac_if.if_snd, pMbuf); /* dequeue a packet */ copy_from_mbufs (pTfd->pEnetHdr, pMbuf, length); length = max (ETHERSMALL, length); length = min (0x3fff, length); if ((etherOutputHookRtn != NULL) && (* etherOutputHookRtn) (&pDrvCtrl->idr, pTfd->pEnetHdr, length)) { /* TODO - release the TFD */ continue; } /* TODO - update TFD data fields: length, etc */ pTfd->count = length; templateTfdSend (pDrvCtrl, pTfd); /* txmit the TFD */ } }/********************************************************************************* templateInt - entry point for handling interrupts** 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.** RETURNS: N/A.*/void templateInt ( int unit ) { UINT16 devStatus; DRV_CTRL *pDrvCtrl; pDrvCtrl = & drvCtrl [unit]; /* TODO - read device status */ TEMPLATE_NETIF_READ (pDrvCtrl, pCsr, devStatus); /* Quick exit if device did not generate interrupt */ if (devStatus == 0) return; /* Acknowledge interrupt to reset it, either here or at end of routine. */ TEMPLATE_INT_ACK (pDrvCtrl); /* Handle transmitter interrupt */ if (devStatus & TEMPLATE_CSR_TX_PEND) { /* TODO - reclaim tx buffers */ if (1) /* TODO - if tx queue is not empty */ templateTfdStart (pDrvCtrl); else pDrvCtrl->txIdle = TRUE; /* transmitter idle */ } /* Handle receiver interrupt */ if (devStatus & TEMPLATE_CSR_RX_PEND) { if (!(pDrvCtrl->rcvHandling)) { pDrvCtrl->rcvHandling = TRUE; netJobAdd ((FUNCPTR) templateHandleRecv, (int) pDrvCtrl,0, 0, 0, 0); } } }/********************************************************************************* templateHandleRecv - 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.** RETURNS: N/A.*/LOCAL void templateHandleRecv ( DRV_CTRL *pDrvCtrl ) { RFD *pRfd; do { pDrvCtrl->rcvHandling = TRUE; /* interlock with templateInt() */ while ((pRfd = templateRfdGet (pDrvCtrl)) != NULL) { if (templateReceive (pDrvCtrl, pRfd) == OK) templateRfdReturn (pDrvCtrl, pRfd); } pDrvCtrl->rcvHandling = FALSE; /* interlock with templateInt() */ templateRxStart (pDrvCtrl); /* restart Rcvr */ } while (templateRfdReady (pDrvCtrl)); /* make sure rx q still empty */ }/********************************************************************************* templateReceive - 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* is therefore 'ok' to return the RFD to the Rx queue. A return value of ERROR* means that buffer loaning was employed, and so the RFD is still in use and* should not be returned to the Rx queue. In this latter case, the RFD will* eventually be returned by the protocol, via a call to our templateLoanFree().** RETURNS: OK if RFD was not loaned, ERROR if it was loaned.*/LOCAL STATUS templateReceive ( DRV_CTRL *pDrvCtrl, RFD *pRfd ) { ETH_HDR *pEh; u_char *pData; int len; UINT16 etherType; MBUF *m = NULL; BOOL rfdLoaned = FALSE; if (0) /* TODO - check RFD for errors */ { ++pDrvCtrl->idr.ac_if.if_ierrors; /* bump error counter */ templateRfdReturn (pDrvCtrl, pRfd); /* free the RFD */ return (ERROR); } /* Bump input packet counter. */ ++pDrvCtrl->idr.ac_if.if_ipackets; len = pRfd->actualCnt; /* get frame length */ pEh = pRfd->pEnetHdr; /* get ptr to ethernet header */ /* Service input hook */ if (etherInputHookRtn != NULL) { if ((* etherInputHookRtn) (&pDrvCtrl->idr, (char *)pEh, len)) { templateRfdReturn (pDrvCtrl, pRfd); /* free the RFD */ return (OK); } } len -= EH_SIZE; pData = (u_char *) pRfd->enetData; etherType = ntohs (pEh->ether_type); /* * we can loan out receive frames from receive queue if: * * 1) the threshold of loanable frames has not been exceeded * 2) size of the input ethernet frame is large enough to be used with * clustering. */ if ((pDrvCtrl->nLoanRfds > 0) && (USE_CLUSTER (len)) && ((m = build_cluster (pData, len, &pDrvCtrl->idr, MC_EI, &pRfd->refCnt, templateLoanFree, (int) pDrvCtrl, (int) pRfd, NULL)) != NULL)) { pDrvCtrl->nLoanRfds--; /* one less to loan */ rfdLoaned = TRUE; /* we loaned a frame */ } else { m = copy_to_mbufs (pData, len, 0, &pDrvCtrl->idr); } if (m != NULL) do_protocol_with_type (etherType, m, &pDrvCtrl->idr, len); return ((rfdLoaned) ? ERROR : OK); }/********************************************************************************* templateLoanFree - return a loaned receive frame descriptor** This routine is called by the protocol code when it has completed use of* an RFD that we loaned to it.** RETURNS: N/A.*/LOCAL void templateLoanFree ( DRV_CTRL *pDrvCtrl, RFD *pRfd ) { templateRfdReturn (pDrvCtrl, pRfd); pDrvCtrl->nLoanRfds ++; }/********************************************************************************* templateDeviceStart - reset and start the device** This routine assumes interrupts from the device have been disabled, and* that the driver control structure has been initialized.** RETURNS: OK upon success, or ERROR for a hardware failure.*/LOCAL STATUS templateDeviceStart ( int unit /* physical unit number */ ) { DRV_CTRL *pDrvCtrl; /* Get pointers */ pDrvCtrl = & drvCtrl [unit]; /* Issue the reset operation to the device */ TEMPLATE_NETIF_WRITE(pDrvCtrl, pCsr, TEMPLATE_CMD_RESET); /* TODO - initialize the device */ /* TODO - generate error message if needed */ templateRxStart (pDrvCtrl); return (OK); }/********************************************************************************* templateTfdSend - place a transmit frame on the transmit queue** The TFD has been filled in with the network pertinent data. This* routine will enqueue the TFD for transmission.** RETURNS: N/A.*/LOCAL void templateTfdSend ( DRV_CTRL *pDrvCtrl, TFD *pTfd ) { int unit; unit = pDrvCtrl->idr.ac_if.if_unit; /* TODO - setup the TFD data fields */ pTfd->status = 0; /* fill in TFD fields */ pTfd->reserved = 0; /* must be zero */ TEMPLATE_INT_DISABLE (pDrvCtrl); /* TODO - queue the TFD */ if (pDrvCtrl->txIdle) /* transmitter idle */ templateTfdStart (pDrvCtrl); /* flush txQueue */ TEMPLATE_INT_ENABLE (pDrvCtrl); }/********************************************************************************* templateTfdStart - start/restart the xmit list** This routine is the device level xmit routine. The packet is given to the* device for transmission.** RETURNS: N/A.*/LOCAL void templateTfdStart ( DRV_CTRL *pDrvCtrl /* device pointer */ ) { /* TODO - setup tx queues */ pDrvCtrl->txIdle = FALSE; /* transmitter busy */ /* TODO - send start command */ TEMPLATE_NETIF_WRITE (pDrvCtrl, pCsr, TEMPLATE_CMD_TX_START); }/********************************************************************************* templateRxStart - start up the Receive Unit** Starts up the Receive Unit. Assumes that the receive structures are set up.** RETURNS: N/A.*/LOCAL void templateRxStart ( DRV_CTRL *pDrvCtrl ) { /* TODO - send start command */ TEMPLATE_NETIF_WRITE(pDrvCtrl, pCsr, TEMPLATE_CMD_RX_START); }/********************************************************************************* templateRfdReturn - return a RFD to the receive queue for use by the device** This routine is used by the protocol layer to return a loaned receive buffer* back to the driver.** RETURNS: N/A.*/LOCAL void templateRfdReturn ( DRV_CTRL* pDrvCtrl, /* device pointer */ RFD* pRfd /* buffer being returned */ ) { int unit; unit = pDrvCtrl->idr.ac_if.if_unit; /* TODO - update RFD data */ pRfd->status = 0; /* clear status */ pRfd->actualCnt = 0; /* clear actual count */ pRfd->bufSize = ETHERMTU + EH_SIZE; /* fill in size */ pRfd->refCnt = 0; /* initialize ref cnt */ /* TODO - link RFD back into queue */ }/********************************************************************************* templateRfdGet - get a received frame from the receive queue** This routine returns a received frame descriptor (incoming packet) to* the caller. If no RFD is available, the NULL pointer is returned.** RETURNS:* A pointer to a valid RFD, or NULL if none available*/LOCAL RFD *templateRfdGet ( DRV_CTRL *pDrvCtrl /* device pointer */ ) { RFD *pRfd = NULL; if (templateRfdReady (pDrvCtrl)) { /* TODO - get RFD from device queue */ } return (pRfd); }/********************************************************************************* templateRfdReady - determine if a RFD is ready for processing** This routine is used to determine if a received frame descriptor (incoming* packet) is ready for processing.** RETURNS:* TRUE if complete received frame is available, FALSE otherwise.*/LOCAL BOOL templateRfdReady ( DRV_CTRL *pDrvCtrl ) { UCHAR devStatus; TEMPLATE_NETIF_READ(pDrvCtrl, pCsr, devStatus); if (devStatus & TEMPLATE_CSR_RFD_READY) return TRUE; return FALSE; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -