📄 if_eidve.c
字号:
* other value for this parameter is interpreted by this routine as the address* of the shared memory region to be used.** If the caller provides the shared memory region, then the driver assumes* that this region does not require cache coherency operations, nor does it* require conversions between virtual and physical addresses.** If the caller indicates that this routine must allocate the shared memory* region, then this routine will use cacheDmaMalloc() to obtain* some non-cacheable memory. The attributes of this memory will be checked,* and if the memory is not both read and write coherent, this routine will* abort and return ERROR.** RETURNS: OK or ERROR.** SEE ALSO: ifLib,* .I "Intel 82596 User's Manual"*/STATUS eidveattach ( int unit, /* unit number */ int ivec, /* interrupt vector number */ UINT8 sysbus, /* sysbus field of SCP */ char * memBase, /* address of memory pool or NONE */ int nTfds, /* no. of transmit frames (0 = default) */ int nRfds /* no. of receive frames (0 = default) */ ) { DRV_CTRL *pDrvCtrl; UINT size; /* temporary size holder */ UINT sizeScp; UINT sizeIscp; UINT sizeScb; UINT sizeCfd; RFD * pRfd; /* pointer to RFD's */ int ix; static char *errMsg1 = "\neidveattach: could not obtain memory\n"; static char *errMsg2 = "\neidveattach: shared memory not cache coherent\n"; sizeScp = MEM_ROUND_UP (sizeof (SCP)); sizeIscp = MEM_ROUND_UP (sizeof (ISCP)); sizeScb = MEM_ROUND_UP (sizeof (SCB)); sizeCfd = MEM_ROUND_UP (sizeof (CFD)); /* Sanity check the unit number */ if (unit < 0 || unit >= MAX_UNITS) return (ERROR); /* Ensure single invocation per system life */ pDrvCtrl = & drvCtrl [unit]; if (pDrvCtrl->attached) return (OK); /* Determine number of Tx and Rx descriptors to use */ pDrvCtrl->nTFDs = nTfds ? nTfds : DEF_NUM_TFDS; pDrvCtrl->nRFDs = nRfds ? nRfds : DEF_NUM_RFDS; /* calculate the total size of 82596 memory pool */ size = 24 + /* allow for alignment */ sizeScp + sizeIscp + sizeScb + sizeCfd + /* synch'ed command frame */ (sizeof (RFD) * pDrvCtrl->nRFDs) + /* pool of receive frames */ (sizeof (TFD) * pDrvCtrl->nTFDs); /* pool of transmit frames */ /* Establish the memory area that we will share with the device. If * the caller has provided an area, then we assume it is non-cacheable * and will not require the use of the special cache routines. * If the caller did not provide an area, then we must obtain it from * the system, using the cache savvy allocation routine. */ switch ((int) memBase) { case NONE : /* we must obtain it */ /* this driver can't handle incoherent caches */ if (!CACHE_DMA_IS_WRITE_COHERENT () || !CACHE_DMA_IS_READ_COHERENT ()) { printf (errMsg2); goto error; } pDrvCtrl->memBase = cacheDmaMalloc (size); if (pDrvCtrl->memBase == NULL) /* no memory available */ { printf (errMsg1); goto error; }#ifndef DMA_MEMORY_IS_DUALPORTED pDrvCtrl->cacheFuncs = cacheDmaFuncs;#endif /* DMA_MEMORY_IS_DUALPORTED */ break; default : /* the user provided an area */ pDrvCtrl->memBase = memBase; /* use the provided address */#ifndef DMA_MEMORY_IS_DUALPORTED pDrvCtrl->cacheFuncs = cacheNullFuncs;#else printf ("\neidveattach: allocate 0x%x bytes from address 0x%x... ", size, (UINT)(pDrvCtrl->memBase));#endif /* DMA_MEMORY_IS_DUALPORTED */ break; }#ifdef DMA_MEMORY_IS_DUALPORTED pDrvCtrl->cacheFuncs.flushRtn = NULL; pDrvCtrl->cacheFuncs.invalidateRtn = NULL; pDrvCtrl->cacheFuncs.virtToPhysRtn = (FUNCPTR)sysDmaVirtToPhys; pDrvCtrl->cacheFuncs.physToVirtRtn = (FUNCPTR)sysDmaPhysToVirt;#endif /* DMA_MEMORY_IS_DUALPORTED */ /* Save other passed-in parameters */ pDrvCtrl->sysbus = sysbus; /* remember sysbus value */ pDrvCtrl->ivec = ivec; /* interrupt vector */ /* Carve up the shared memory region into the specific sections */ bzero ((char *) pDrvCtrl->memBase, size); pDrvCtrl->pScp = (SCP *) /* align to first 16 byte page */ ( ((u_long) pDrvCtrl->memBase + 0xf) & ~0xf ); pDrvCtrl->pIscp = (ISCP *)((int)pDrvCtrl->pScp + sizeScp); pDrvCtrl->pScb = (SCB *) ((int)pDrvCtrl->pIscp + sizeIscp); pDrvCtrl->pCfd = (CFD *) ((int)pDrvCtrl->pScb + sizeScb); /* * Align the RFD pool on a 2-byte boundary. This causes the IP * header to be aligned on a 4-byte boundary and enables the protocol * layer (ip_input) to access long fields without getting a bus error. */ pDrvCtrl->rfdPool = (RFD *) (MEM_ROUND_UP (((int)pDrvCtrl->pCfd + sizeCfd)) + 2); pDrvCtrl->tfdPool = (TFD *) /* TFD is OK on a 4-byte boundary */ MEM_ROUND_UP ((int)pDrvCtrl->rfdPool+ (sizeof (RFD) * pDrvCtrl->nRFDs)); for (ix = 0, pRfd = pDrvCtrl->rfdPool; ix < DEF_NUM_RFDS; ix++, pRfd++) { pRfd->refCnt = 0; /* initialize ref cnt */ } /* Init the watchdog that will patrol for device lockups */ pDrvCtrl->transLocks = 0; pDrvCtrl->recvLocks = 0; pDrvCtrl->wid = wdCreate (); /* create watchdog */ if (pDrvCtrl->wid == NULL) /* no resource */ goto error; pDrvCtrl->wdInterval = sysClkRateGet() >> 1; /* get our enet addr */ if (sysEnetAddrGet (unit, (char *)pDrvCtrl->idr.ac_enaddr) == ERROR) { errnoSet (S_iosLib_INVALID_ETHERNET_ADDRESS); goto error; } /* Publish the interface record */ ether_attach ( (IFNET *) & pDrvCtrl->idr, unit, "ei", (FUNCPTR) NULL, (FUNCPTR) eiIoctl, (FUNCPTR) ether_output, (FUNCPTR) eiReset ); pDrvCtrl->idr.ac_if.if_start = (FUNCPTR) eiTxStartup; /* initialize the unit */ if (eiInit (unit) == ERROR) goto error; /* 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 system memory */ if (((int) memBase == NONE) && ((int) pDrvCtrl->memBase != NULL)) cacheDmaFree (pDrvCtrl->memBase); /* Release watchdog */ if (pDrvCtrl->wid != NULL) wdDelete (pDrvCtrl->wid); return (ERROR); } }/********************************************************************************* eiInit - Initialize the interface.** Initialization of interface; clear recorded pending operations.* Called at boot time and by eiWatchDog() if a reset is required.** RETURNS: OK or ERROR*/static STATUS eiInit ( int unit /* unit number */ ) { DRV_CTRL *pDrvCtrl = & drvCtrl [unit]; pDrvCtrl->rcvHandling = FALSE; /* netTask not running our receive job */ pDrvCtrl->txCleaning = FALSE; /* netTask not running our clean job */ pDrvCtrl->txIdle = TRUE; /* transmitter idle */ eiQInit ((EI_LIST *)&pDrvCtrl->rxQueue); /* to be received queue */ eiQInit ((EI_LIST *)&pDrvCtrl->txQueue); /* to be sent queue */ eiQInit ((EI_LIST *)&pDrvCtrl->tfdQueue); /* free tfds to add to send q */ eiQInit ((EI_LIST *)&pDrvCtrl->cblQueue); /* actively sending queue */ eiQInit ((EI_LIST *)&pDrvCtrl->cleanQueue); /* queue of TFDs to clean */ pDrvCtrl->wdTxTimeout = 0; pDrvCtrl->wdRxTimeout = 0; { /***** Perform device initialization *****/ /* Block local variables */ int ix; sys596IntDisable (unit); /* disable device interrupts */ (void) sys596Init (unit); /* do board specific init */ /* Connect the interrupt handler */ if (intConnect ((VOIDFUNCPTR *)INUM_TO_IVEC (pDrvCtrl->ivec), eiInt, (int)pDrvCtrl) == ERROR) return (ERROR); /* Start the device */ if ( eiDeviceStart (unit) == ERROR ) return (ERROR); eiDiag (unit); /* run diagnostics */ eiConfig (unit); /* configure chip */ eiIASetup (unit); /* setup address */ for (ix = 0; ix < pDrvCtrl->nTFDs; ix ++) /* tank up the free tfd queue */ { eiQPut ( unit, (EI_LIST *) & pDrvCtrl->tfdQueue, (EI_NODE *) & pDrvCtrl->tfdPool [ix] ); } pDrvCtrl->pFreeRfd = NULL; /* first free RFD */ pDrvCtrl->nLoanRfds = MAX_RFDS_LOANED; /* number of loanable RFD's */ for (ix = 0; ix < pDrvCtrl->nRFDs; ix ++) /* tank up the receive queue */ { if (pDrvCtrl->rfdPool[ix].refCnt == 0) eiRxQPut (pDrvCtrl, & pDrvCtrl->rfdPool [ix]); } sys596IntAck (unit); /* clear any pended dev ints */ sys596IntEnable (unit); /* enable interrupts at system */ } /* End block */ wdCancel(pDrvCtrl->wid); /* in case re-initializing */ wdStart(pDrvCtrl->wid, (int) pDrvCtrl->wdInterval, (FUNCPTR) eiWatchDog, pDrvCtrl->idr.ac_if.if_unit ); 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 ( IFNET *pIfNet, 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 *)pIfNet)->ac_ipaddr = IA_SIN (data)->sin_addr; arpwhohas (pIfNet, &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); }/********************************************************************************* 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. This will cause eiOutput() 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.*/void eiTxStartup ( DRV_CTRL * pDrvCtrl ) { 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) { IF_DEQUEUE (&pDrvCtrl->idr.ac_if.if_snd, pMbuf); /* dequeue a packet */ /* get free tfd */ pTfd = (TFD *) eiQGet ((EI_LIST *)&pDrvCtrl->tfdQueue); if (pTfd == NULL) { m_freem (pMbuf); break; /* out of TFD's */ } 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); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -