📄 if_eex.c
字号:
pDrvCtrl->pCfd = &Cfd; /* Init the watchdog that will patrol for device lockups */ pDrvCtrl->transLocks = 0; pDrvCtrl->wid = wdCreate (); /* create watchdog */ if (pDrvCtrl->wid == NULL) /* no resource */ goto error; pDrvCtrl->rcvHandling = FALSE; /* netTask not running our receive job */ pDrvCtrl->txCleaning = FALSE; /* netTask not running our clean job */ pDrvCtrl->txIdle = TRUE; /* transmitter idle */ /* Perform device initialization */ if (eex586Init (unit) != OK) goto error; sysIntEnablePIC (ilevel); /* Set our flag */ pDrvCtrl->attached = TRUE; /* Set status flags in IDR */ pDrvCtrl->idr.ac_if.if_flags |= (IFF_UP | IFF_RUNNING | IFF_NOTRAILERS); /* Successful return */ return (OK); /***** Handle error cases *****/ error: { /* Release watchdog */ if (pDrvCtrl->wid != NULL) wdDelete (pDrvCtrl->wid); return (ERROR); } }/********************************************************************************* eexReset - reset the eex interface** Mark interface as inactive and reset the chip.*/static void eexReset ( int unit ) { DRV_CTRL *pDrvCtrl; pDrvCtrl = & drvCtrl [unit]; pDrvCtrl->idr.ac_if.if_flags = 0; eex586IntDisable (unit); /* disable ints from the dev */ eex586SetReset (unit); /* put the 586 chip in reset */ }/********************************************************************************* eexIoctl - interface ioctl procedure** Process an interface ioctl request.*/static int eexIoctl ( IDR *pIDR, int cmd, caddr_t data ) { int error;#ifdef EEX_DEBUG printf ("eex: ioctl: pIDR=%x cmd=%x data=%x\n", pIDR, cmd, data);#endif /* EEX_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/********************************************************************************* eexOutput - 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 eexOutput ( IDR * pIDR, MBUF * pMbuf, SOCK * pDestAddr ) {#ifdef EEX_DEBUG printf ("eex: output: pIDR=%x pMbuf=%x pDestAddr=%x\n", pIDR, pMbuf, pDestAddr);#endif /* EEX_DEBUG */ return (ether_output ( (IFNET *)pIDR, pMbuf, pDestAddr, (FUNCPTR) eexTxStartup, pIDR)); }#endif/********************************************************************************* eexTxStartup - 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 eexOutput() 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 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 eexTxStartup ( int unit ) { DRV_CTRL * pDrvCtrl; pDrvCtrl = & drvCtrl [unit];#elsestatic void eexTxStartup ( DRV_CTRL * pDrvCtrl ) {#endif MBUF * pMbuf; int length; EEX_SHORTLINK tfdOffset; /* * 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) { IF_DEQUEUE (&pDrvCtrl->idr.ac_if.if_snd, pMbuf); /* dequeue a packet */ copy_from_mbufs (buffer, pMbuf, length); length = max (ETHERSMALL, length); if ( (etherOutputHookRtn != NULL) && (* etherOutputHookRtn) (&pDrvCtrl->idr, buffer, length)) continue; /* get free tfd */ tfdOffset = eexQGet (pDrvCtrl, (EEX_LIST *)&pDrvCtrl->tfdQueue); if (tfdOffset == NULL) break; /* out of TFD's */ /* copy header and data to contiguous board memory areas */ sysOutWord (pDrvCtrl->port + WRPTR, tfdOffset + TF_BUFFER); sysOutWordString (pDrvCtrl->port + DXREG, (short *)buffer, (length + 1) / 2);#ifndef EEX_AL_LOC length -= EH_SIZE; /* compensate for header not in data */#endif /* EEX_AL_LOC */ sysOutWord (pDrvCtrl->port + WRPTR, tfdOffset + TF_ACT_COUNT); sysOutWord (pDrvCtrl->port + DXREG, (length & ACT_COUNT_MASK) | TBD_S_EOF);#ifndef EEX_AL_LOC /* Move length/ethertype up to where 82586 expects it following dest. */ sysOutWord (pDrvCtrl->port + RDPTR, tfdOffset + TF_OLDLENGTH); length = sysInWord (pDrvCtrl->port + DXREG); /* separate read and write because both pointers increment */ /* on any access to DXREG */ sysOutWord (pDrvCtrl->port + WRPTR, tfdOffset + TF_NEWLENGTH); sysOutWord (pDrvCtrl->port + DXREG, length);#endif /* EEX_AL_LOC */#ifndef BSD43_DRIVER /* BSD 4.4 ether_output() doesn't bump statistic. */ pDrvCtrl->idr.ac_if.if_opackets++;#endif eexTxQPut (pDrvCtrl, tfdOffset); } }/********************************************************************************* eexInt - entry point for handling interrupts from the 82586** 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.*/static void eexInt ( DRV_CTRL *pDrvCtrl ) { UINT16 event; int unit; unit = pDrvCtrl->idr.ac_if.if_unit; sysBusIntAck (pDrvCtrl->intLevel); event = sysInWord (pDrvCtrl->port + SCB_STATUS) & (SCB_S_CX | SCB_S_FR | SCB_S_CNA | SCB_S_RNR);#ifdef EEX_DEBUG logMsg ("eex: interrupt: event=%x\n", event, 0, 0, 0, 0, 0);#endif /* EEX_DEBUG */ eexCommand (pDrvCtrl, event); /* ack the events */ eex586IntAck (unit); /* ack 586 interrupt */ /* Handle transmitter interrupt */ if (event & SCB_S_CNA) /* reclaim tx tfds */ { wdCancel(pDrvCtrl->wid); /* canus delenda est */ 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 = pDrvCtrl->cblQueue.tail; } if (!pDrvCtrl->txCleaning) /* not cleaning? */ { pDrvCtrl->txCleaning = TRUE; /* set flag */ netJobAdd ( (FUNCPTR) eexTxCleanQ, (int) pDrvCtrl, 0, 0, 0, 0); /* defer cleanup */ } if (pDrvCtrl->txQueue.head != NULL) /* anything to flush? */ eexTxQFlush (pDrvCtrl); /* flush the tx q */ else pDrvCtrl->txIdle = TRUE; /* transmitter idle */ } /* Handle receiver interrupt */ if ( (event & SCB_S_FR) && !(pDrvCtrl->rcvHandling)) { pDrvCtrl->rcvHandling = TRUE; (void) netJobAdd ( (FUNCPTR) eexHandleRecvInt, (int) pDrvCtrl,0, 0, 0, 0); /* netTask processes */ } /* dhe 10/20/94 could lose some packets here */ if ( (event & SCB_S_RNR) && !(pDrvCtrl->rcvHandling)) eexRxStartup(pDrvCtrl); } /********************************************************************************* eexTxCleanQ - 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 eexTxCleanQ ( DRV_CTRL *pDrvCtrl ) { EEX_SHORTLINK tfdOffset; UINT16 status; 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. */ eex586IntDisable (unit); tfdOffset = eexQGet (pDrvCtrl, (EEX_LIST *)&pDrvCtrl->cleanQueue); eex586IntEnable (unit); if (tfdOffset == NULL) break; sysOutWord (pDrvCtrl->port + RDPTR, tfdOffset); status = sysInWord (pDrvCtrl->port + DXREG); pDrvCtrl->idr.ac_if.if_collisions += /* add any colls */ (status & CFD_S_RETRY) ? 16 : /* excessive colls */ (status & CFD_S_COLL_MASK); /* some colls */ if (!(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 */ eexQPut (pDrvCtrl, (EEX_LIST *)&pDrvCtrl->tfdQueue, tfdOffset); } if (needTxStart) /* check flag */#ifdef BSD43_DRIVER eexTxStartup (unit);#else eexTxStartup (pDrvCtrl);#endif pDrvCtrl->txCleaning = FALSE; } while (pDrvCtrl->cleanQueue.head != NULL); /* check again */ }/********************************************************************************* eexHandleRecvInt - task level interrupt service for input packets
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -