ei82596End.c
资源名称:ixp425BSP.rar [点击查看]
上传用户:luoyougen
上传日期:2008-05-12
资源大小:23136k
文件大小:79k
源码类别:
VxWorks
开发平台:
C/C++
- /* ei82596End.c - END style Intel 82596 Ethernet network interface driver */
- /* Copyright 1989-2002 Wind River Systems, Inc. */
- #include "copyright_wrs.h"
- /*
- modification history
- --------------------
- 02c,14jan02,dat Removing warnings from Diab compiler
- 02b,12sep01,tor add interlock between ei82596DeviceRestart() and int (SPR 70154)
- update pFreeRfd in ei82596RxQPut() (SPR 70148)
- 02a,11jun00,ham removed reference to etherLib.
- 01z,27jan00,dat fixed use of NULL
- 01y,30nov99,stv freed mBlk chain before returning ERROR (SPR #28492).
- 01x,11jan99,mem added offset load parameter, changed pNext fields in RFD
- and TFD to type EI_LINK. Added test for netClusterGet()
- returning NULL. (SPR 24440)
- 01w,05nov98,dbt fixed error handling in ei82596Recv() routine (SPR #22557).
- 01v,22sep98,dat Lint cleanup.
- 01u,28aug98,n_s Changed watchdog to call ei82596DeviceRestart via netJobAdd.
- spr #22224
- 01t,17aug98,sut removed SYS_INT_ENABLE and sys596IntAck, which was before
- SYS_INT_CONNECT [SPR 20284]
- 01s,08dec97,gnn END code review fixes.
- 01r,19oct97,vin moved swapping of loaned buffer before END_RCV_RTN_CALL
- 01q,17oct97,vin removed extra free.
- 01p,09oct97,vin delete unwanted frees in PollSend routine
- 01o,07oct97,vin fixed multicasting, fixed mtu to ETHER_MTU
- 01m,25sep97,gnn SENS beta feedback fixes
- 01l,24sep97,vin added clBlk related calls
- 01k,01sep97,vin fixed the restart logic
- 01j,22aug97,gnn changes due to new buffering scheme
- 01i,12aug97,gnn changes necessitated by MUX/END update.
- 01h,30may97,map Doc updates.
- 01g,02may97,map Fixed bug in multicast address set.
- 01f,30apr97,jag Fixed man page errors.
- 01d,21apr97,map fixed strtok_r() invocation; added intr (dis)enable macros
- added sys596Init.
- 01c,17apr97,gnn fixed bug in Load/Start sequence.
- 01b,17mar97,map updates for new buffering scheme, and code cleanups.
- 01a,13sep96,map modified if_ei.c to END style.
- */
- /*
- This module implements an Intel 82596 Ethernet network interface driver.
- This driver is designed to be moderately generic. It operates unmodified
- across the range of architectures and targets supported by VxWorks.
- To achieve this, this driver requires some external support routines
- as well as several target-specific parameters. These parameters (and the
- mechanisms used to communicate them to the driver) are detailed below.
- This driver can run with the device configured in either
- big-endian or little-endian modes. Error recovery code has been added to
- deal with some of the known errata in the A0 version of the device. This
- driver supports up to four individual units per CPU.
- BOARD LAYOUT
- This device is on-board. No jumpering diagram is necessary.
- EXTERNAL INTERFACE
- The driver provides one standard external interface, ei82596EndLoad().
- As input, this routine takes a string of colon-separated parameters.
- The parameters should be specified in hexadecimal (optionally preceded by "0x"
- or a minus sign "-"). The parameter string is parsed using strtok_r(), and
- each parameter is converted from string to binary by a call to:
- .CS
- strtoul(parameter, NULL, 16).
- .CE
- TARGET-SPECIFIC PARAMETERS
- The format of the parameter string is:
- <unit>:<ivec>:<sysbus>:<memBase>:<nTfds>:<nRfds>:<offset>
- .IP <unit>
- A convenient holdover from the former model. It is only used in the
- string name for the driver.
- .IP <ivec>
- This is the interrupt vector number of the hardware interrupt generated by
- this ethernet device. The driver uses intConnect() to attach an interrupt
- handler to this interrupt.
- .IP <sysbus>
- This parameter tells the device about the system bus.
- To determine the correct value for a target, see
- .I "Intel 32-bit Local Area Network (LAN) Component User's Manual."
- .IP <memBase>
- This parameter specifies the base address of a DMA-able cache-free
- pre-allocated memory region for use as a memory pool for transmit/receive
- descriptors, buffers, and other device control structures.
- If there is no pre-allocated memory available for the driver, this parameter
- should be -1 (NONE). In which case, the driver calls cacheDmaAlloc() to
- allocate cache-safe memory.
- .IP <nTfds>
- This parameter specifies the number of transmit descriptor/buffers to be
- allocated. If this parameter is zero or -1 (NULL), a default of 32 is used.
- .IP <nRfds>
- This parameter specifies the number of receive descriptor/buffers to be
- allocated. If this parameter is zero or -1 (NULL), a default of 32 is used.
- .IP <offset>
- Specifies the memory alignment offset.
- .LP
- EXTERNAL SUPPORT REQUIREMENTS
- This driver requires seven external support functions:
- .IP sys596IntEnable()
- .CS
- void sys596IntEnable (int unit)
- .CE
- This routine provides a target-specific interface to enable Ethernet device
- interrupts for a given device unit.
- .IP sys596IntDisable()
- .CS
- void sys596IntDisable (int unit)
- .CE
- This routine provides a target-specific interface to disable Ethernet device
- interrupts for a given device unit.
- .IP sysEnetAddrGet()
- .CS
- STATUS sysEnetAddrGet (int unit, char *enetAdrs)
- .CE
- This routine provides a target-specific interface to access a device Ethernet
- address. This routine should provide a six-byte Ethernet address in
- the <enetAdrs> parameter and return OK or ERROR.
- .IP sys596Init()
- .CS
- STATUS sys596Init (int unit)
- .CE
- This routine performs any target-specific initialization required before the
- 82596 is initialized. Typically, it is empty. This routine must return OK
- or ERROR.
- .IP sys596Port()
- .CS
- void sys596Port (int unit, int cmd, UINT32 addr)
- .CE
- This routine provides access to the special port function of the 82596. It
- delivers the command and address arguments to the port of the specified unit.
- The driver calls this routine primarily during initialization and, under
- some conditions, during error recovery procedures.
- .IP sys596ChanAtn()
- .CS
- void sys596ChanAtn (int unit)
- .CE
- This routine provides the channel attention signal to the 82596 for the
- specified <unit>. The driver calls this routine frequently throughout all
- phases of operation.
- .IP sys596IntAck()
- .CS
- void sys596IntAck (int unit)
- .CE
- This routine must perform any required interrupt acknowledgment or clearing.
- Typically, this involves an operation to some interrupt control hardware.
- Note that the INT signal from the 82596 behaves in an "edge-triggered" mode.
- Therefore, this routine typically clears a latch within the control circuitry.
- The driver calls this routine from the interrupt handler.
- .LP
- SYSTEM RESOURCE USAGE
- The driver uses cacheDmaMalloc() to allocate memory to share with the 82596.
- The fixed-size pieces in this area total 160 bytes. The variable-size pieces
- in this area are affected by the configuration parameters specified in the
- eiattach() call. The size of one RFD (Receive Frame Descriptor) is 1536
- bytes. The size of one TFD (Transmit Frame Descriptor) is 1534 bytes. For
- more on RFDs and TFDs, see the
- .I "Intel 82596 User's Manual."
- The 82596 requires ether that this shared memory region is non-cacheable
- or that the hardware implements bus snooping. The driver cannot maintain
- cache coherency for the device. This is because fields within the command
- structures are asynchronously modified by both the driver and the device,
- and these fields might share the same cache line.
- TUNING HINTS
- The only adjustable parameters are the number of TFDs and RFDs that are
- created at run-time. These parameters are given to the driver when eiattach()
- is called. There is one TFD and one RFD associated with each transmitted
- frame and each received frame respectively. For memory-limited applications,
- decreasing the number of TFDs and RFDs might be a good idea. Increasing the
- number of TFDs provides no performance benefit after a certain point.
- Increasing the number of RFDs provides more buffering before packets are
- dropped. This can be useful if there are tasks running at a higher priority
- than the net task.
- SEE ALSO: ifLib,
- .I "Intel 82596 User's Manual,"
- .I "Intel 32-bit Local Area Network (LAN) Component User's Manual"
- */
- #include "vxWorks.h"
- #include "wdLib.h"
- #include "iv.h"
- #include "vme.h"
- #include "net/mbuf.h"
- #include "net/unixLib.h"
- #include "net/protosw.h"
- #include "sys/socket.h"
- #include "sys/ioctl.h"
- #include "errno.h"
- #include "memLib.h"
- #include "intLib.h"
- #include "net/route.h"
- #include "iosLib.h"
- #include "errnoLib.h"
- #include "cacheLib.h"
- #include "logLib.h"
- #include "netLib.h"
- #include "stdio.h"
- #include "stdlib.h"
- #include "sysLib.h"
- #include "net/systm.h"
- #include "sys/times.h"
- #include "net/if_subr.h"
- #include "drv/end/ei82596End.h"
- #undef ETHER_MAP_IP_MULTICAST
- #include "etherMultiLib.h"
- #include "end.h"
- #include "semLib.h"
- #define END_MACROS
- #include "endLib.h"
- #include "lstLib.h"
- /* stuff to change in endLib.h */
- #define END_OBJ_FREE(X) END_OBJECT_UNLOAD(X)
- /***** LOCAL DEFINITIONS *****/
- /* Driver debug control */
- #ifdef DRV_DEBUG
- #define DRV_DEBUG_OFF 0x0000
- #define DRV_DEBUG_RX 0x0001
- #define DRV_DEBUG_TX 0x0002
- #define DRV_DEBUG_POLL (DRV_DEBUG_POLL_RX | DRV_DEBUG_POLL_TX)
- #define DRV_DEBUG_POLL_RX 0x0004
- #define DRV_DEBUG_POLL_TX 0x0008
- #define DRV_DEBUG_LOAD 0x0010
- #define DRV_DEBUG_IOCTL 0x0020
- #define DRV_DEBUG_BUF 0x0040
- #define DRV_DEBUG_INT 0x0800
- #define DRV_DEBUG_POLL_REDIR 0x10000
- #define DRV_DEBUG_LOG_NVRAM 0x20000
- int eiDebug = 0xff;
- int nLoan=0;
- IMPORT int nvLogMsg();
- #define DRV_LOG(FLG, X0, X1, X2, X3, X4, X5, X6)
- {
- if ((eiDebug & FLG) && (eiDebug & DRV_DEBUG_LOG_NVRAM))
- logMsg(X0, X1, X2, X3, X4, X5, X6);
- else if (eiDebug & FLG)
- logMsg(X0, X1, X2, X3, X4, X5, X6);
- }
- #define DRV_PRINT(FLG,X)
- {
- if (eiDebug & FLG) printf X;
- }
- #else /*DRV_DEBUG*/
- #define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6)
- #define DRV_PRINT(DBG_SW,X)
- #endif /*DRV_DEBUG*/
- /* When a cluster is in a free cluster pool, the first four bytes are
- * used as a link pointer to the next cluster in the free list.
- */
- #define CLUSTER_POINTER_SIZE sizeof(void *)
- #define EI_CLUSTER_SIZE MEM_ROUND_UP((sizeof(RFD)+2))
- #define END_FLAGS_ISSET(pEnd, setBits)
- ((pEnd)->flags & (setBits))
- /* A shortcut for getting the hardware address from the MIB II stuff. */
- #define END_HADDR(pEnd)
- ((pEnd)->mib2Tbl.ifPhysAddress.phyAddress)
- #define END_HADDR_LEN(pEnd)
- ((pEnd)->mib2Tbl.ifPhysAddress.addrLength)
- #define EI_SPEED 10000000 /* bits per sec */
- #define EADDR_LEN 6 /* ethernet address length */
- #define DEF_NUM_TFDS 32 /* default number of TFDs */
- #define DEF_NUM_RFDS 32 /* default number of RFDs */
- #define EI_PKT_SIZE (ETHERMTU + SIZEOF_ETHERHEADER + EADDR_LEN)
- #define EI_DEV_NAME "ei"
- #define EI_DEV_NAME_LEN 3
- /* driver flags */
- #define EI_POLLING 0x01 /* polling mode */
- #define EI_PROMISC 0x02 /* promiscuous mode */
- #define EI_MCAST 0x04 /* multicast addressing mode */
- #define EI_MCAST_SOFT 0x08 /* software multicast */
- #define EI_MEMOWN 0x10 /* device mem allocated by driver */
- #define END_CTRL_SIZ sizeof(END_CTRL)
- #define EI_RX_TIMEOUT 3 /* # watchdog runs for receive timeout */
- #define EI_TX_TIMEOUT 2 /* # watchdog runs for transmit timeout */
- #define DRV_FLAGS_SET(setBits)
- (pDrvCtrl->flags |= (setBits))
- #define DRV_FLAGS_CLR(clrBits)
- (pDrvCtrl->flags &= ~(clrBits))
- #define DRV_FLAGS_GET()
- (pDrvCtrl->flags)
- #define DRV_FLAGS_ISSET(setBits)
- (pDrvCtrl->flags & (setBits))
- /*
- * Default macro definitions for BSP interface.
- * These macros can be redefined in a wrapper file, to generate
- * a new module with an optimized interface.
- */
- #ifndef SYS_INT_CONNECT
- #define SYS_INT_CONNECT(pDrvCtrl,rtn,arg,pResult)
- {
- IMPORT STATUS sysIntConnect();
- *pResult = intConnect ((VOIDFUNCPTR *)INUM_TO_IVEC (pDrvCtrl->ivec),
- rtn, (int)arg);
- }
- #endif /*SYS_INT_CONNECT*/
- #ifndef SYS_INT_DISCONNECT
- #define SYS_INT_DISCONNECT(pDrvCtrl,rtn,arg,pResult)
- {
- *pResult = OK; /* HELP: need a real routine */
- }
- #endif /*SYS_INT_DISCONNECT*/
- #ifndef SYS_INT_ENABLE
- #define SYS_INT_ENABLE(pDrvCtrl)
- {
- IMPORT STATUS sys596IntEnable(int);
- (void) sys596IntEnable (pDrvCtrl->unit);
- }
- #endif /*SYS_INT_ENABLE*/
- #ifndef SYS_INT_DISABLE
- #define SYS_INT_DISABLE(pDrvCtrl)
- {
- IMPORT void sys596IntDisable(int);
- sys596IntDisable(pDrvCtrl->unit);
- }
- #endif /* SYS_INT_DISABLE */
- #ifndef DRV_NAME
- #define DRV_NAME "ei"
- #define DRV_NAME_LEN 3
- #endif /* DRV_NAME */
- #define NET_BUF_FREE(pNetBuf)
- pNetBuf->freeRtn(pNetBuf->pSpare1, pNetBuf->pSpare2)
- /* The definition of our linked list management structure */
- typedef struct ei_list /* EI_LIST - 82596 queue head */
- {
- volatile EI_NODE * head; /* header of list */
- volatile EI_NODE * tail; /* tail of list */
- } EI_LIST;
- /* The definition of the driver control structure */
- typedef struct end_ctrl
- {
- END_OBJ endObj; /* base class */
- int unit; /* unit number */
- int ivec; /* interrupt vector */
- UINT8 sysbus; /* SCP sysbus value */
- char *memBase; /* 82596 memory pool base */
- int nTFDs; /* how many TFDs to create */
- int nRFDs; /* how many RFDs to create */
- INT8 flags; /* driver state */
- CACHE_FUNCS cacheFuncs; /* cache descriptor */
- SCP *pScp; /* SCP ptr */
- ISCP *pIscp; /* ISCP ptr */
- SCB *pScb; /* SCB ptr */
- CFD *pCfd; /* synchronous command frame */
- RFD *rfdPool; /* RFD pool */
- TFD *tfdPool; /* TFD pool */
- TFD *pPollTfd; /* TFD for polled transmits */
- RFD *pFreeRfd; /* first empty RFD in rxQueue */
- volatile EI_LIST rxQueue; /* receive queue */
- volatile EI_LIST txQueue; /* to be sent queue */
- volatile EI_LIST freeQueue; /* free frame descriptors */
- volatile EI_LIST cblQueue; /* actual chip transmit queue */
- volatile EI_LIST cleanQueue; /* queue of TFDs to cleanup */
- WDOG_ID wid; /* watchdog timer for transmit */
- int wdInterval; /* watchdog interval in ticks */
- long rxLockups; /* receive lockup count */
- long txLockups; /* transmit lockup count */
- volatile char wdTxTimeout; /* watchdog runs with tx lockup */
- volatile char wdRxTimeout; /* watchdog runs with rx lockup */
- int resetCount; /* number of chip resets */
- BOOL txIdle; /* no pending transmits */
- BOOL txCleaning; /* cleaning transmit queue */
- BOOL rxHandling; /* handling received packets */
- BOOL txBlocked; /* to implement flow control */
- CL_POOL_ID pClPoolId;
- int offset; /* memory offset */
- } END_CTRL;
- /***** GLOBALS *****/
- #ifdef DRV_DEBUG
- END_CTRL *pEndCtrl;
- #endif /*DRV_DEBUG*/
- /* Function declarations not in any header files */
- IMPORT STATUS sysEnetAddrGet (int unit, UCHAR addr[]);
- IMPORT void sys596Init (int unit);
- IMPORT STATUS sys596IntAck (int unit);
- IMPORT STATUS sys596IntEnable (int unit);
- IMPORT void sys596IntDisable (int unit);
- IMPORT void sys596Port (int unit, int cmd, UINT32 addr);
- IMPORT void sys596ChanAtn (int unit);
- /* forward function declarations */
- LOCAL STATUS ei82596InitParse (END_CTRL *pDrvCtrl, char *initString);
- LOCAL STATUS ei82596InitMem (END_CTRL *pDrvCtrl);
- LOCAL STATUS ei82596Diag (END_CTRL *pDrvCtrl);
- LOCAL void ei82596Config (END_CTRL *pDrvCtrl);
- LOCAL STATUS ei82596MCastConfig (END_CTRL *pDrvCtrl);
- LOCAL void ei82596IASetup (END_CTRL *pDrvCtrl);
- LOCAL void ei82596RxStartup (END_CTRL *pDrvCtrl);
- LOCAL void ei82596Action (END_CTRL *pDrvCtrl, UINT16 action);
- LOCAL STATUS ei82596Command (END_CTRL *pDrvCtrl, UINT16 cmd);
- LOCAL void ei82596TxQPut (END_CTRL *pDrvCtrl, TFD *pTfd);
- LOCAL void ei82596RxQPut (END_CTRL *pDrvCtrl, RFD *pRfd);
- LOCAL void ei82596TxQFlush (END_CTRL *pDrvCtrl);
- LOCAL RFD * ei82596RxQGet (END_CTRL *pDrvCtrl);
- LOCAL BOOL ei82596RxQFull (END_CTRL *pDrvCtrl);
- LOCAL void ei82596QInit (volatile EI_LIST *pHead);
- LOCAL EI_NODE * ei82596QGet (volatile EI_LIST *pQueue);
- LOCAL void ei82596QPut (END_CTRL *pDrvCtrl, EI_LIST *pQueue,
- EI_NODE *pNode);
- LOCAL void ei82596QCat (volatile EI_LIST *pQDest, volatile EI_LIST *pQSrc);
- LOCAL STATUS ei82596DeviceStart (END_CTRL *pDrvCtrl);
- LOCAL void ei82596WatchDog (END_CTRL *pDrvCtrl);
- LOCAL void ei82596Recv (END_CTRL *pDrvCtrl, RFD *pRfd);
- LOCAL void ei82596TxCleanQ (END_CTRL *pDrvCtrl);
- LOCAL void ei82596RxIntHandle (END_CTRL *pDrvCtrl);
- LOCAL void ei82596Int (END_CTRL *pDrvCtrl);
- LOCAL void ei82596DeviceRestart(END_CTRL *pDrvCtrl);
- /* END Specific interfaces. */
- END_OBJ * ei82596EndLoad (char *initString);
- LOCAL STATUS ei82596Unload (END_CTRL *pDrvCtrl);
- LOCAL STATUS ei82596Start(END_CTRL *pDrvCtrl);
- LOCAL STATUS ei82596Stop(END_CTRL *pDrvCtrl);
- LOCAL int ei82596Ioctl (END_CTRL *pDrvCtrl, int cmd, caddr_t data);
- LOCAL STATUS ei82596Send (END_CTRL *pDrvCtrl, M_BLK_ID pMblk);
- LOCAL STATUS ei82596MCastAdrsAdd (END_CTRL *pDrvCtrl, char* pAddress);
- LOCAL STATUS ei82596MCastAdrsDel (END_CTRL *pDrvCtrl, char* pAddress);
- LOCAL STATUS ei82596MCastAdrsGet (END_CTRL *pDrvCtrl,
- MULTI_TABLE *pTable);
- LOCAL STATUS ei82596PollSend (END_CTRL *pDrvCtrl, M_BLK_ID pMblk);
- LOCAL STATUS ei82596PollReceive (END_CTRL *pDrvCtrl, M_BLK_ID pMblk);
- LOCAL STATUS ei82596PollStart (END_CTRL *pDrvCtrl);
- LOCAL STATUS ei82596PollStop (END_CTRL *pDrvCtrl);
- /*
- * Define the device function table. This is static across all driver
- * instances.
- */
- LOCAL NET_FUNCS netFuncs =
- {
- (FUNCPTR)ei82596Start, /* start func. */
- (FUNCPTR)ei82596Stop, /* stop func. */
- (FUNCPTR)ei82596Unload, /* unload func. */
- (FUNCPTR)ei82596Ioctl, /* ioctl func. */
- (FUNCPTR)ei82596Send, /* send func. */
- (FUNCPTR)ei82596MCastAdrsAdd, /* multicast add func. */
- (FUNCPTR)ei82596MCastAdrsDel, /* multicast delete func. */
- (FUNCPTR)ei82596MCastAdrsGet, /* multicast get fun. */
- (FUNCPTR)ei82596PollSend, /* polling send func. */
- (FUNCPTR)ei82596PollReceive, /* polling receive func. */
- endEtherAddressForm, /* put address info into a NET_BUFFER. */
- endEtherPacketDataGet, /* get pointer to data in NET_BUFFER. */
- endEtherPacketAddrGet /* Get packet addresses. */
- };
- /* network buffers configuration */
- M_CL_CONFIG eiMclBlkConfig =
- {
- 0, 0, NULL, 0
- };
- CL_DESC eiClDescTbl [] =
- {
- /*
- clusterSize num memArea memSize
- ----------- ---- ------- -------
- */
- {EI_CLUSTER_SIZE, 0, NULL, 0}
- };
- int eiClDescTblNumEnt = (NELEMENTS(eiClDescTbl));
- /*******************************************************************************
- *
- * ei82596EndLoad - initialize the driver and device
- *
- * This routine initializes both driver and device to an operational state
- * using the device-specific values specified by <initString>.
- * The <initString> parameter expects an ordered list of colon-separated
- * values.
- *
- * The format of the <initString> is:
- *
- * <unit>:<ivec>:<sysbus>:<memBase>:<nTfds>:<nRfds>
- *
- * .IP <unit>
- * Specifies the unit number for this device.
- * .IP <ivec>
- * This is the interrupt vector number of the hardware interrupt generated by
- * this Ethernet device. The driver uses intConnect() to attach an interrupt
- * handler for this interrupt.
- * .IP <sysbus>
- * Passes in values as described in the Intel manual
- * for the 82596. A default number of transmit/receive frames of 32 can be
- * selected by passing zero in the parameters <nTfds> and <nRfds>.
- * In other cases, the number of frames selected should be greater than two.
- * .IP <memBase>
- * Informs the driver about the shared memory region. The 82596 shares a
- * region of memory with the driver. The caller of this routine can specify
- * the address of this memory region, or can specify that the driver must
- * obtain this memory region from the system resources. If this parameter
- * is set to the constant "NONE", this routine tries to allocate the shared
- * memory from the system. Any 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, 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, this routine uses cacheDmaMalloc() to obtain some non-cacheable
- * memory. The attributes of this memory are checked, and, if the memory is
- * not both read- and write-coherent, this routine aborts.
- * .LP
- *
- * RETURNS: An END object pointer or NULL.
- *
- * SEE ALSO: ifLib,
- * .I "Intel 82596 User's Manual"
- */
- END_OBJ *ei82596EndLoad
- (
- char * initString /* parameter string */
- )
- {
- END_CTRL *pDrvCtrl;
- UCHAR eAdrs[EADDR_LEN]; /* ethernet address */
- RFD *pRfd;
- int ix;
- if (initString == NULL)
- return (NULL);
- if (initString[0] == 0)
- {
- bcopy((char *)DRV_NAME, initString, DRV_NAME_LEN);
- return (0);
- }
- /* Allocate a control structure for this device */
- pDrvCtrl = calloc (sizeof(END_CTRL), 1);
- if (pDrvCtrl == NULL)
- return (NULL);
- /* Parse InitString */
- if (ei82596InitParse (pDrvCtrl, initString) == ERROR)
- return (NULL);
- #ifdef DRV_DEBUG
- pEndCtrl=pDrvCtrl;
- #endif /* DRV_DEBUG */
- /* endObj initializations */
- if (END_OBJ_INIT (&pDrvCtrl->endObj, (DEV_OBJ*) pDrvCtrl, DRV_NAME,
- pDrvCtrl->unit, &netFuncs,
- "Intel 82596 Ethernet Enhanced Network Driver") == ERROR)
- return (NULL);
- /* memory initialization */
- if (ei82596InitMem (pDrvCtrl) == ERROR)
- goto error;
- /* Initialize a watchdog for device lockups */
- pDrvCtrl->txLockups = 0;
- pDrvCtrl->rxLockups = 0;
- pDrvCtrl->wid = wdCreate (); /* create watchdog */
- if (pDrvCtrl->wid == NULL) /* no resource */
- goto error;
- pDrvCtrl->wdInterval = sysClkRateGet() >> 1;
- /* Get our enet addr */
- if (sysEnetAddrGet (pDrvCtrl->unit, eAdrs) == ERROR)
- {
- errnoSet (S_iosLib_INVALID_ETHERNET_ADDRESS);
- goto error;
- }
- /* Initialize MIB-II entries */
- if (END_MIB_INIT (&pDrvCtrl->endObj, M2_ifType_ethernet_csmacd,
- eAdrs, 6,
- ETHERMTU, EI_SPEED) == ERROR)
- goto error;
- pDrvCtrl->txIdle = TRUE;
- pDrvCtrl->rxHandling = FALSE;
- pDrvCtrl->txCleaning = FALSE;
- ei82596QInit (&pDrvCtrl->rxQueue); /* to be received queue */
- ei82596QInit (&pDrvCtrl->txQueue); /* to be sent queue */
- ei82596QInit (&pDrvCtrl->freeQueue); /* free tfds for the send q */
- ei82596QInit (&pDrvCtrl->cblQueue); /* actively sending queue */
- ei82596QInit (&pDrvCtrl->cleanQueue);/* queue of TFDs to clean */
- pDrvCtrl->wdTxTimeout = 0;
- pDrvCtrl->wdRxTimeout = 0;
- for (ix = 0; ix < pDrvCtrl->nTFDs; ix ++) /* tank up the free tfd queue */
- {
- ei82596QPut (pDrvCtrl,
- (EI_LIST *) & pDrvCtrl->freeQueue,
- (EI_NODE *) & pDrvCtrl->tfdPool [ix]);
- }
- for (ix = 0; ix < pDrvCtrl->nRFDs; ix ++) /* tank up the receive queue */
- {
- static BOOL first = TRUE;
- pRfd = (RFD *) netClusterGet (pDrvCtrl->endObj.pNetPool,
- pDrvCtrl->pClPoolId);
- if (pRfd == NULL)
- {
- DRV_LOG (DRV_DEBUG_LOAD, "Could not get an RFD buffern",
- 1, 2, 3, 4, 5, 6);
- goto error;
- }
- /* offset pRfd */
- pRfd = (RFD *) (((char *) pRfd) + pDrvCtrl->offset );
- pRfd->status = 0;
- pRfd->command = 0;
- if (first)
- {
- pDrvCtrl->pFreeRfd = pRfd;
- first = FALSE;
- }
- pRfd->bufSize = ETHERMTU + EH_SIZE; /* max packet size */
- LINK_WR (& pRfd->lBufDesc, 0xffffffff); /* no RDB */
- ei82596QPut (pDrvCtrl,
- (EI_LIST *) &pDrvCtrl->rxQueue,
- (EI_NODE *) pRfd);
- }
- pRfd = (RFD *) pDrvCtrl->rxQueue.tail;
- pRfd->command = CFD_C_EL; /* end of list */
- /* Mark the device ready */
- END_OBJ_READY (&pDrvCtrl->endObj,
- IFF_NOTRAILERS | IFF_MULTICAST | IFF_BROADCAST);
- /* Successful return */
- return (&pDrvCtrl->endObj);
- /***** Handle error cases *****/
- error:
- {
- ei82596Unload (pDrvCtrl);
- return (NULL);
- }
- }
- /*******************************************************************************
- *
- * ei82596Unload - unload a driver from the system
- *
- * This routine deallocates lists, and free allocated memory.
- *
- * RETURNS: OK, always.
- */
- LOCAL STATUS ei82596Unload
- (
- END_CTRL *pDrvCtrl
- )
- {
- DRV_LOG (DRV_DEBUG_LOAD, "eiUnloadn", 0, 0, 0, 0, 0, 0);
- /* deallocate lists */
- END_OBJ_FREE (&pDrvCtrl->endObj);
- /* Free the allocated watchdog */
- if (pDrvCtrl->wid != NULL)
- wdDelete (pDrvCtrl->wid);
- /* deallocate allocated shared memory */
- if (DRV_FLAGS_ISSET (EI_MEMOWN) && pDrvCtrl->memBase)
- cacheDmaFree (pDrvCtrl->memBase);
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596InitParse - parse parameter values from initString
- *
- * RETURNS: OK or ERROR.
- */
- LOCAL STATUS ei82596InitParse
- (
- END_CTRL *pDrvCtrl,
- char *initString
- )
- {
- char * tok; /* an initString token */
- char * holder=NULL; /* points to initString fragment beyond tok */
- tok = strtok_r(initString, ":", &holder);
- if (tok == NULL)
- return ERROR;
- pDrvCtrl->unit = atoi(tok);
- tok=strtok_r(NULL, ":", &holder);
- if (tok == NULL)
- return ERROR;
- pDrvCtrl->ivec = strtoul (tok, NULL, 16);
- tok=strtok_r(NULL, ":", &holder);
- if (tok == NULL)
- return ERROR;
- pDrvCtrl->sysbus = (UINT8) strtoul (tok, NULL, 16);
- tok=strtok_r(NULL, ":", &holder);
- if (tok == NULL)
- return ERROR;
- pDrvCtrl->memBase = (char *) strtoul (tok, NULL, 16);
- tok = strtok_r (NULL, ":", &holder);
- if (tok == NULL)
- return ERROR;
- pDrvCtrl->nTFDs = atoi (tok);
- if (!pDrvCtrl->nTFDs || pDrvCtrl->nTFDs == -1)
- pDrvCtrl->nTFDs = DEF_NUM_TFDS;
- tok = strtok_r (NULL, ":", &holder);
- if (tok == NULL)
- return ERROR;
- pDrvCtrl->nRFDs = atoi (tok);
- if (!pDrvCtrl->nRFDs || pDrvCtrl->nRFDs == -1)
- pDrvCtrl->nRFDs = DEF_NUM_RFDS;
- pDrvCtrl->offset = -1;
- tok = strtok_r (NULL, ":", &holder);
- if (tok != NULL)
- {
- pDrvCtrl->offset = atoi (tok);
- }
- if (pDrvCtrl->offset == -1)
- pDrvCtrl->offset = 0;
- DRV_LOG (DRV_DEBUG_LOAD,
- "eiEndLoad: unit=%d ivec=0x%x sysbus=0x%x membase=0x%x "
- "nTfds=%d nRfds=%dn",
- pDrvCtrl->unit, pDrvCtrl->ivec, pDrvCtrl->sysbus,
- (int)pDrvCtrl->memBase, pDrvCtrl->nTFDs, pDrvCtrl->nRFDs);
- if (pDrvCtrl->offset)
- DRV_LOG (DRV_DEBUG_LOAD,
- "eiEndLoad: unit=%d offset=%dn",
- pDrvCtrl->unit, pDrvCtrl->offset, 0, 0, 0, 0);
- return OK;
- }
- /*******************************************************************************
- *
- * ei82596InitMem - initialize memory
- *
- * RETURNS: OK or ERROR
- */
- LOCAL STATUS ei82596InitMem
- (
- END_CTRL * pDrvCtrl
- )
- {
- UINT size;
- UINT sizeScp;
- UINT sizeIscp;
- UINT sizeScb;
- UINT sizeCfd;
- static char *errMsg1 = "nei82596EndLoad: could not obtain memoryn";
- static char *errMsg2 = "nei82596EndLoad: shared memory not cache"
- "coherentn";
- /* initialize the netPool */
- if ((pDrvCtrl->endObj.pNetPool = malloc (sizeof(NET_POOL))) == NULL)
- return (ERROR);
- if (eiMclBlkConfig.mBlkNum == 0) /* pool of mblks */
- eiMclBlkConfig.mBlkNum = pDrvCtrl->nRFDs * 4;
- if (eiClDescTbl [0].clNum == 0)
- {/* pool of receive frames */
- eiClDescTbl[0].clNum = pDrvCtrl->nRFDs * 2;
- eiClDescTbl[0].clSize = EI_CLUSTER_SIZE;
- }
- if (eiMclBlkConfig.clBlkNum == 0) /* pool of cluster blks */
- eiMclBlkConfig.clBlkNum = eiClDescTbl[0].clNum;
- /* Force word (4-byte) alignment */
- sizeScp = MEM_ROUND_UP (sizeof (SCP));
- sizeIscp = MEM_ROUND_UP (sizeof (ISCP));
- sizeScb = MEM_ROUND_UP (sizeof (SCB));
- sizeCfd = MEM_ROUND_UP (sizeof (CFD));
- /* calculate the total size of 82596 memory pool
- * NOTE: When calling netPoolInit(), the library adds
- * a link pointer to the size of every cluster. It
- * also adds a link pointer to the beginning of the
- * cluster pool. This is accounted for below with
- * the CLUSTER_POINTER_SIZE macro.
- */
- size =
- 16 + /* allow for alignment */
- sizeScp +
- sizeIscp +
- sizeScb +
- sizeCfd + /* synch'ed command frame */
- (((eiClDescTbl[0].clSize + CLUSTER_POINTER_SIZE) *
- eiClDescTbl[0].clNum) + CLUSTER_POINTER_SIZE) +
- (sizeof (TFD) * pDrvCtrl->nTFDs) + /* pool of transmit frames */
- (sizeof (TFD)); /* one TFD for polled TX */
- /* 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) pDrvCtrl->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);
- return (ERROR);
- }
- pDrvCtrl->memBase = cacheDmaMalloc (size);
- DRV_FLAGS_SET(EI_MEMOWN);
- if (pDrvCtrl->memBase == NULL) /* no memory available */
- {
- printf (errMsg1);
- return (ERROR);
- }
- pDrvCtrl->cacheFuncs = cacheDmaFuncs;
- break;
- default : /* the user provided an area */
- pDrvCtrl->cacheFuncs = cacheNullFuncs;
- DRV_FLAGS_CLR (EI_MEMOWN);
- break;
- }
- /* 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);
- if (eiMclBlkConfig.memArea == NULL) /* do a default allocation */
- {
- /* memory size adjusted to hold the netPool pointer at the head */
- eiMclBlkConfig.memSize = ((eiMclBlkConfig.mBlkNum *
- (MSIZE + sizeof (long))) +
- (eiMclBlkConfig.clBlkNum *
- (CL_BLK_SZ + sizeof(long))));
- if ((eiMclBlkConfig.memArea = (char *) memalign
- (sizeof(long), eiMclBlkConfig.memSize))
- == NULL)
- return (ERROR);
- }
- eiClDescTbl[0].memArea = (char *) ((int)pDrvCtrl->pCfd + sizeCfd);
- eiClDescTbl[0].memSize = ((eiClDescTbl[0].clSize + 4) *
- eiClDescTbl[0].clNum);
- if (netPoolInit (pDrvCtrl->endObj.pNetPool, &eiMclBlkConfig, &eiClDescTbl [0],
- eiClDescTblNumEnt, NULL) == ERROR)
- return (ERROR);
- if ((pDrvCtrl->pClPoolId = netClPoolIdGet (pDrvCtrl->endObj.pNetPool, sizeof (RFD), FALSE))
- == NULL)
- return (ERROR);
- pDrvCtrl->tfdPool = (TFD *) ((int)eiClDescTbl[0].memArea +
- eiClDescTbl[0].memSize);
- pDrvCtrl->pPollTfd = (TFD *) ((int)pDrvCtrl->tfdPool +
- (sizeof(TFD) * pDrvCtrl->nTFDs));
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596Start - start the device
- *
- * RETURNS: OK or ERROR
- */
- LOCAL STATUS ei82596Start
- (
- END_CTRL *pDrvCtrl
- )
- {
- int ret_val;
- DRV_LOG (DRV_DEBUG_LOAD,
- "eiStart ... beginn", 0, 0, 0, 0, 0, 0);
- SYS_INT_DISABLE (pDrvCtrl);
- /* Start the device */
- if (ei82596DeviceStart (pDrvCtrl) == ERROR)
- return ERROR; /*goto error;*/
- /* run diagnostics */
- if (ei82596Diag (pDrvCtrl) == ERROR)
- return ERROR; /*goto error;*/
- if (END_FLAGS_ISSET (&pDrvCtrl->endObj, IFF_MULTICAST))
- DRV_FLAGS_SET (EI_MCAST);
- ei82596Config (pDrvCtrl); /* configure chip */
- ei82596IASetup (pDrvCtrl); /* setup address */
- /* Connect the interrupt handler */
- SYS_INT_CONNECT (pDrvCtrl, ei82596Int, (int)pDrvCtrl, &ret_val);
- if (ret_val == ERROR)
- return (ERROR);
- /* enable interrupts */
- sys596IntAck (pDrvCtrl->unit);
- SYS_INT_ENABLE (pDrvCtrl);
- /* start the watchdog */
- wdCancel(pDrvCtrl->wid);
- wdStart(pDrvCtrl->wid, (int) pDrvCtrl->wdInterval,
- (FUNCPTR) ei82596WatchDog, (int) pDrvCtrl);
- /* start the receiver */
- ei82596RxStartup (pDrvCtrl);
- DRV_LOG (DRV_DEBUG_LOAD,
- "eiStart ... Donen", 0, 0, 0, 0, 0, 0);
- /* mark the interface -- up */
- END_FLAGS_SET (&pDrvCtrl->endObj, (IFF_UP | IFF_RUNNING));
- return OK;
- }
- /*******************************************************************************
- *
- * ei82596Stop - stop the device
- *
- * This routine cancels the watchdog, disables interrupts, and resets the device.
- *
- * RETURNS: OK or ERROR
- */
- LOCAL STATUS ei82596Stop
- (
- END_CTRL *pDrvCtrl
- )
- {
- int ret_val;
- /* mark the interface -- down */
- END_FLAGS_CLR (&pDrvCtrl->endObj, IFF_UP | IFF_RUNNING);
- wdCancel(pDrvCtrl->wid);
- SYS_INT_DISABLE( pDrvCtrl );
- sys596Port (pDrvCtrl->unit, PORT_RESET, 0);
- SYS_INT_DISCONNECT (pDrvCtrl, ei82596Int, (int)pDrvCtrl, &ret_val);
- return OK;
- }
- /*******************************************************************************
- *
- * ei82596Ioctl - interface ioctl procedure
- *
- * Process an interface ioctl request.
- */
- LOCAL int ei82596Ioctl
- (
- END_CTRL * pDrvCtrl,
- int cmd,
- caddr_t data
- )
- {
- int error=0;
- long value;
- int savedFlags;
- END_OBJ * pEndObj=&pDrvCtrl->endObj;
- DRV_LOG (DRV_DEBUG_IOCTL,
- "eiIoctln", 0, 0, 0, 0, 0, 0);
- switch ((UINT)cmd)
- {
- case EIOCSADDR:
- if (data == NULL)
- error = EINVAL;
- else
- {
- /* Copy and install the new address */
- bcopy ((char *)data, (char *)END_HADDR(pEndObj),
- END_HADDR_LEN(pEndObj));
- ei82596IASetup (pDrvCtrl);
- }
- break;
- case EIOCGADDR:
- if (data == NULL)
- error = EINVAL;
- else
- bcopy ((char *)END_HADDR(pEndObj), (char *)data,
- END_HADDR_LEN(pEndObj));
- #ifdef DRV_DEBUG
- {
- char *cp = (char *)data;
- DRV_LOG (DRV_DEBUG_IOCTL,
- "EIOCGADDR: %x:%x:%x:%x:%x:%xn",
- cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
- }
- #endif /*DRV_DEBUG*/
- break;
- case EIOCSFLAGS:
- value = (long) data;
- if (value < 0)
- {
- value = -value;
- value--;
- END_FLAGS_CLR (pEndObj, value);
- }
- else
- END_FLAGS_SET (pEndObj, value);
- /* handle IFF_PROMISC and IFF_ALLMULTI */
- savedFlags = DRV_FLAGS_GET();
- if (END_FLAGS_ISSET (pEndObj, IFF_PROMISC))
- DRV_FLAGS_SET (EI_PROMISC);
- else
- DRV_FLAGS_CLR (EI_PROMISC);
- if (END_FLAGS_GET(pEndObj) & (IFF_MULTICAST | IFF_ALLMULTI))
- DRV_FLAGS_SET (EI_MCAST);
- else
- DRV_FLAGS_CLR (EI_MCAST);
- if ((DRV_FLAGS_GET() != savedFlags) &&
- (END_FLAGS_GET(pEndObj) & IFF_UP))
- {
- END_FLAGS_CLR(pEndObj, IFF_UP | IFF_RUNNING); /* config down */
- ei82596Config (pDrvCtrl);
- }
- break;
- case EIOCGFLAGS:
- DRV_LOG (DRV_DEBUG_IOCTL, "EIOCGFLAGS: 0x%x: 0x%xn",
- pEndObj->flags, *(long *)data, 0, 0, 0, 0);
- if (data == NULL)
- error = EINVAL;
- else
- *(long *)data = END_FLAGS_GET(pEndObj);
- break;
- case EIOCMULTIADD:
- error = ei82596MCastAdrsAdd (pDrvCtrl, (char *)data);
- break;
- case EIOCMULTIDEL:
- error = ei82596MCastAdrsDel (pDrvCtrl, (char *)data);
- break;
- case EIOCMULTIGET:
- error = ei82596MCastAdrsGet (pDrvCtrl, (MULTI_TABLE *)data);
- break;
- case EIOCPOLLSTART:
- ei82596PollStart (pDrvCtrl);
- break;
- case EIOCPOLLSTOP:
- ei82596PollStop (pDrvCtrl);
- break;
- case EIOCGMIB2:
- if (data == NULL)
- error=EINVAL;
- else
- bcopy((char *)&pEndObj->mib2Tbl, (char *)data,
- sizeof(pEndObj->mib2Tbl));
- break;
- default:
- error = EINVAL;
- }
- return (error);
- }
- /*******************************************************************************
- *
- * ei82596MCastAdrsAdd - add a multicast address for the device
- *
- * This routine adds a multicast address to whatever the driver
- * is already listening for.
- *
- */
- LOCAL STATUS ei82596MCastAdrsAdd
- (
- END_CTRL * pDrvCtrl,
- char * pAddr
- )
- {
- int retVal;
- DRV_LOG (DRV_DEBUG_IOCTL,
- "eiMCastAdrsAdd - %x:%x:%x:%x:%x:%xn",
- pAddr[0], pAddr[1], pAddr[2], pAddr[3], pAddr[4], pAddr[5]);
- retVal = etherMultiAdd (&pDrvCtrl->endObj.multiList, pAddr);
- if (retVal == ENETRESET)
- {
- pDrvCtrl->endObj.nMulti++;
- if (pDrvCtrl->endObj.nMulti > N_MCAST)
- {
- etherMultiDel (&pDrvCtrl->endObj.multiList, pAddr);
- pDrvCtrl->endObj.nMulti--;
- }
- else
- return ei82596MCastConfig (pDrvCtrl);
- }
- return ((retVal == OK) ? OK : ERROR);
- }
- /*******************************************************************************
- *
- * ei82596MCastAdrsDel - delete a multicast address for the device
- *
- * This routine deletes a multicast address from the current list of
- * multicast addresses.
- *
- */
- LOCAL STATUS ei82596MCastAdrsDel
- (
- END_CTRL * pDrvCtrl,
- char * pAddr
- )
- {
- int retVal;
- DRV_LOG (DRV_DEBUG_LOAD,
- "eiMCastAdrsDeln", 0, 0, 0, 0, 0, 0);
- retVal = etherMultiDel (&pDrvCtrl->endObj.multiList, pAddr);
- if (retVal == ENETRESET)
- {
- pDrvCtrl->endObj.nMulti--;
- return ei82596MCastConfig (pDrvCtrl);
- }
- return ((retVal == OK) ? OK : ERROR);
- }
- /*******************************************************************************
- *
- * ei82596MCastAdrsGet - get the current multicast address list
- *
- * This routine returns the current multicast address list in <pTable>
- *
- */
- LOCAL STATUS ei82596MCastAdrsGet
- (
- END_CTRL * pDrvCtrl,
- MULTI_TABLE *pTable
- )
- {
- DRV_LOG (DRV_DEBUG_LOAD,
- "eiMCastAdrsGetn", 0, 0, 0, 0, 0, 0);
- return (etherMultiGet (&pDrvCtrl->endObj.multiList, pTable));
- }
- /*******************************************************************************
- *
- * ei82596MCastConfig - configure multicast addresses.
- *
- */
- LOCAL STATUS ei82596MCastConfig
- (
- END_CTRL *pDrvCtrl
- )
- {
- int count=0;
- char *pAddr;
- ETHER_MULTI *pMCastNode;
- DRV_LOG (DRV_DEBUG_IOCTL,
- "eiMCastConfign", 0, 0, 0, 0, 0, 0);
- bzero ((char *)&pDrvCtrl->pCfd->cfdMcast, sizeof (AC_MCAST));
- pAddr = (char *)pDrvCtrl->pCfd->cfdMcast.cmAddrList;
- pMCastNode = (ETHER_MULTI *)lstFirst (&pDrvCtrl->endObj.multiList);
- while (pMCastNode != NULL)
- {
- count++;
- bcopy (pMCastNode->addr, pAddr, 6);
- pAddr += 6;
- pMCastNode = (ETHER_MULTI *)lstNext (&pMCastNode->node);
- }
- pDrvCtrl->pCfd->cfdMcast.cmMcCount = count * 6; /* byte count */
- SYS_INT_DISABLE( pDrvCtrl );
- ei82596Action (pDrvCtrl, CFD_C_MASETUP);
- SYS_INT_ENABLE (pDrvCtrl);
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596Send - arrange to transmit a packet
- *
- * This routine takes a NET_BUFFER and a NET_ADDRESS and sends the data in the
- * NET_BUFFER to the NET_ADDRESS.
- *
- * RETURNS:
- */
- LOCAL STATUS ei82596Send
- (
- END_CTRL *pDrvCtrl,
- M_BLK_ID pMblk
- )
- {
- TFD * pTfd;
- int len;
- DRV_PRINT (DRV_DEBUG_TX, ("t "));
- #if 0
- SYS_INT_DISABLE (pDrvCtrl);
- if (pDrvCtrl->txBlocked)
- {
- SYS_INT_ENABLE (pDrvCtrl);
- return (END_ERR_BLOCK);
- }
- SYS_INT_ENABLE (pDrvCtrl);
- #endif
- /* check device mode */
- if (DRV_FLAGS_ISSET(EI_POLLING))
- {
- #if 0
- #ifdef DRV_DEBUG
- if (eiDebug & DRV_DEBUG_POLL_REDIR)
- {
- ei82596PollSend (pDrvCtrl, pNetBuf);
- return (OK);
- }
- #endif /*DRV_DEBUG*/
- #endif
- netMblkClChainFree (pMblk); /* free the given mBlk chain */
- errno = EINVAL;
- return (ERROR);
- }
- SYS_INT_DISABLE( pDrvCtrl );
- if ((pTfd = (TFD *) ei82596QGet (&pDrvCtrl->freeQueue)) != NULL)
- {
- SYS_INT_ENABLE( pDrvCtrl );
- len = netMblkToBufCopy (pMblk, (char *)pTfd->enetHdr, NULL);
- netMblkClChainFree (pMblk);
- /* set up the byte count field; use a min frame size of ETHERSMALL */
- pTfd->count = max (ETHERSMALL, len);
- pTfd->count &= ~0xc000;
- /* arrange to to transmit the buffer */
- SYS_INT_DISABLE( pDrvCtrl );
- ei82596TxQPut (pDrvCtrl, pTfd);
- SYS_INT_ENABLE( pDrvCtrl );
- }
- else /* case if the pfd is equal to NULL */
- {
- pDrvCtrl->txBlocked = TRUE;
- SYS_INT_ENABLE( pDrvCtrl );
- return (END_ERR_BLOCK);
- }
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596Int - 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.
- */
- LOCAL void ei82596Int
- (
- END_CTRL *pDrvCtrl
- )
- {
- SCB *pScb=pDrvCtrl->pScb;
- int unit=pDrvCtrl->unit;
- UINT16 event;
- event = pScb->scbStatus & (SCB_S_CX | SCB_S_FR | SCB_S_CNA | SCB_S_RNR);
- DRV_PRINT (DRV_DEBUG_INT, ("eiInt: event=0x%xn", event));
- ei82596Command (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 */
- ei82596QCat (&pDrvCtrl->cleanQueue, &pDrvCtrl->cblQueue);
- ei82596QInit (&pDrvCtrl->cblQueue);
- if (! pDrvCtrl->txCleaning) /* not cleaning? */
- {
- pDrvCtrl->txCleaning = TRUE;
- /* defer cleanup */
- netJobAdd ((FUNCPTR) ei82596TxCleanQ, (int) pDrvCtrl, 0, 0, 0, 0);
- }
- if (pDrvCtrl->txBlocked)
- {
- /* potential race condition ??? */
- pDrvCtrl->txBlocked = FALSE;
- netJobAdd ((FUNCPTR) muxTxRestart, (int) &pDrvCtrl->endObj, 0, 0,
- 0, 0);
- }
- if (pDrvCtrl->txQueue.head != NULL) /* anything to flush? */
- ei82596TxQFlush (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->rxHandling)
- {
- pDrvCtrl->rxHandling = TRUE;
- (void) netJobAdd ((FUNCPTR) ei82596RxIntHandle,
- (int) pDrvCtrl,0, 0, 0, 0);/* netTask processes */
- }
- }
- }
- /*******************************************************************************
- *
- * ei82596TxCleanQ - 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.
- */
- LOCAL void ei82596TxCleanQ
- (
- END_CTRL *pDrvCtrl
- )
- {
- TFD * pTfd;
- do
- {
- pDrvCtrl->txCleaning = TRUE;
- /* process transmitted frames */
- while (1)
- {
- /* INT and SEM locks while manipulating this queue */
- END_TX_SEM_TAKE (&pDrvCtrl->endObj, WAIT_FOREVER);
- SYS_INT_DISABLE( pDrvCtrl );
- pTfd = (TFD*) ei82596QGet ((EI_LIST *)&pDrvCtrl->cleanQueue);
- SYS_INT_ENABLE( pDrvCtrl );
- END_TX_SEM_GIVE (&pDrvCtrl->endObj);
- if (pTfd == NULL)
- break;
- if (!(pTfd->status & CFD_S_OK)) /* packet not sent */
- {
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_OUT_ERRS, +1);
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_OUT_UCAST, -1);
- }
- /* return to tfdQ */
- END_TX_SEM_TAKE (&pDrvCtrl->endObj, WAIT_FOREVER);
- ei82596QPut (pDrvCtrl,(EI_LIST *)&pDrvCtrl->freeQueue,
- (EI_NODE*)pTfd);
- #ifdef DRV_DEBUG
- nLoan--;
- #endif
- END_TX_SEM_GIVE (&pDrvCtrl->endObj);
- }
- pDrvCtrl->txCleaning = FALSE;
- }
- while (pDrvCtrl->cleanQueue.head != NULL); /* check again */
- }
- /*******************************************************************************
- *
- * ei82596RxIntHandle - 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.
- */
- LOCAL void ei82596RxIntHandle
- (
- END_CTRL *pDrvCtrl
- )
- {
- RFD *pRfd;
- do
- {
- pDrvCtrl->rxHandling = TRUE; /* interlock with ei82596Int()*/
- while ((pRfd = ei82596RxQGet (pDrvCtrl)) != NULL)
- ei82596Recv (pDrvCtrl, pRfd);
- pDrvCtrl->rxHandling = FALSE; /* interlock with ei82596Int()*/
- }
- while (ei82596RxQFull (pDrvCtrl)); /* make sure rx q still empty */
- }
- /*******************************************************************************
- *
- * ei82596Recv - pass a received frame to the upper layer
- *
- *
- * RETURNS:
- */
- LOCAL void ei82596Recv
- (
- END_CTRL * pDrvCtrl,
- RFD * pRfd
- )
- {
- RFD * pRfdLoaned = NULL;
- M_BLK_ID pMblk = NULL;
- CL_BLK_ID pClBlk = NULL;
- /* process the frame, if there are no errors */
- if ((pRfd->status & (RFD_S_OK | RFD_S_COMPLETE)) !=
- (RFD_S_OK | RFD_S_COMPLETE))
- goto eiRecvError;
- if ((pMblk = netMblkGet (pDrvCtrl->endObj.pNetPool,
- M_DONTWAIT, MT_DATA)) == NULL)
- goto eiRecvError;
- if ((pClBlk = netClBlkGet (pDrvCtrl->endObj.pNetPool, M_DONTWAIT)) == NULL)
- goto eiRecvError;
- if ((pRfdLoaned = (RFD *) netClusterGet (pDrvCtrl->endObj.pNetPool,
- pDrvCtrl->pClPoolId)) == NULL)
- goto eiRecvError;
- /* offset pRfdLoaned if needed */
- pRfdLoaned = (RFD *) (((char *) pRfdLoaned) + pDrvCtrl->offset );
- DRV_PRINT (DRV_DEBUG_RX, ("r "));
- netClBlkJoin (pClBlk, ((char *)pRfd) - pDrvCtrl->offset,
- sizeof (RFD) + pDrvCtrl->offset,
- NULL, 0, 0, 0);
- netMblkClJoin (pMblk, pClBlk);
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_UCAST, +1);
- pMblk->mBlkHdr.mFlags |= M_PKTHDR;
- pMblk->mBlkHdr.mData = (char *)&pRfd->enetHdr [0];
- pMblk->mBlkHdr.mLen = pRfd->count & ~0xc000;
- pMblk->mBlkPktHdr.len = pMblk->mBlkHdr.mLen;
- /* return the buffer to the receive queue */
- ei82596RxQPut (pDrvCtrl, (RFD *)pRfdLoaned);
- END_RCV_RTN_CALL (&pDrvCtrl->endObj, pMblk);
- return;
- eiRecvError:
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_ERRS, +1);
- /* return the buffer to the receive queue */
- ei82596RxQPut (pDrvCtrl, (RFD *)pRfd);
- if (pClBlk)
- netClBlkFree (pDrvCtrl->endObj.pNetPool, pClBlk);
- if (pMblk)
- netMblkFree (pDrvCtrl->endObj.pNetPool, pMblk);
- return;
- }
- /*******************************************************************************
- *
- * ei82596PollStart - start polling mode
- *
- * RETURNS:
- */
- LOCAL STATUS ei82596PollStart
- (
- END_CTRL *pDrvCtrl
- )
- {
- int intLevel;
- DRV_LOG (DRV_DEBUG_POLL, "PollStart", 0, 1, 2, 3, 4, 5 );
- intLevel = intLock(); /* lock interrupts */
- SYS_INT_DISABLE( pDrvCtrl );
- DRV_FLAGS_SET (EI_POLLING);
- intUnlock (intLevel); /* unlock interupts */
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596PollStop - stop polling mode
- *
- * RETURNS:
- */
- LOCAL STATUS ei82596PollStop
- (
- END_CTRL *pDrvCtrl
- )
- {
- int intLevel;
- DRV_LOG (DRV_DEBUG_POLL, "PollStop", 0, 1, 2, 3, 4, 5);
- intLevel = intLock();
- SYS_INT_ENABLE( pDrvCtrl );
- DRV_FLAGS_CLR (EI_POLLING);
- intUnlock (intLevel);
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596PollSend - transmit a packet in polled mode
- *
- * RETURNS:
- */
- LOCAL STATUS ei82596PollSend
- (
- END_CTRL *pDrvCtrl,
- M_BLK_ID pMblk /* pointer to the mBlk/cluster pair */
- )
- {
- TFD *pTfd;
- char *pTemp;
- int len;
- DRV_LOG (DRV_DEBUG_POLL_TX, "T", 0, 1, 2, 3, 4, 5);
- /* check if the command unit is active */
- if (pDrvCtrl->pScb->scbStatus & SCB_S_CUACTIVE)
- {
- DRV_LOG (DRV_DEBUG_POLL_TX, "e ", 0, 1, 2, 3, 4, 5);
- return (EAGAIN);
- }
- /* Is this ours? */
- pTfd = pDrvCtrl->pPollTfd;
- len = netMblkToBufCopy (pMblk, (char *)pTfd->enetHdr, NULL);
- DRV_LOG (DRV_DEBUG_POLL_TX, "h ", 0, 1, 2, 3, 4, 5);
- pTfd->count = max (len, ETHERSMALL) & ~0xc000;
- /* transmit the buffer */
- pTfd->status = 0;
- pTfd->command = CFD_C_XMIT | CFD_C_EL;
- pTfd->count |= TFD_CNT_EOF;
- pTfd->reserved = 0;
- LINK_WR (&pTfd->lBufDesc, NULL);
- pTemp = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs, pTfd);
- LINK_WR (&pDrvCtrl->pScb->pCB, pTemp);
- ei82596Command (pDrvCtrl, SCB_C_CUSTART);
- /* wait for the command to complete */
- while (pDrvCtrl->pScb->scbStatus & SCB_S_CUACTIVE)
- ; /* HELP: timeout */
- /* update statistics */
- if (pTfd->status & CFD_S_OK)
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_OUT_UCAST, +1);
- if (pTfd->status & CFD_S_RETRY)
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_OUT_ERRS, +1);
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596PollReceive - receive a packet in polled mode
- *
- * RETURNS:
- */
- LOCAL STATUS ei82596PollReceive
- (
- END_CTRL *pDrvCtrl,
- M_BLK_ID pMblk
- )
- {
- RFD *pRfd;
- int len;
- while ((pRfd = ei82596RxQGet (pDrvCtrl)) != NULL)
- {
- len = (pRfd->count & ~0xc000);
- if (((pRfd->status & (RFD_S_OK | RFD_S_COMPLETE)) !=
- (RFD_S_OK | RFD_S_COMPLETE)) ||
- (pMblk->mBlkHdr.mLen < len) ||
- (!(pMblk->mBlkHdr.mFlags & M_EXT)))
- {
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_ERRS, +1);
- ei82596RxQPut (pDrvCtrl, pRfd);
- continue;
- }
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_UCAST, +1);
- pMblk->mBlkHdr.mFlags |= M_PKTHDR;
- pMblk->mBlkHdr.mLen = len;
- pMblk->mBlkPktHdr.len = pMblk->mBlkHdr.mLen;
- bcopy ((char *)pRfd->enetHdr, (char *)pMblk->mBlkHdr.mData, len);
- ei82596RxQPut (pDrvCtrl, pRfd);
- DRV_LOG (DRV_DEBUG_POLL_RX, "R+ ", 0, 1, 2, 3, 4, 5);
- return (OK);
- }
- return (EAGAIN);
- }
- /*******************************************************************************
- *
- * ei82596DeviceStart - reset and start the device
- *
- * This routine assumes interrupts from the device have been disabled, and
- * that the driver control structure has been initialized.
- */
- LOCAL STATUS ei82596DeviceStart
- (
- END_CTRL *pDrvCtrl
- )
- {
- void *pTemp;
- SCP *pScp; /* system config ptr */
- ISCP *pIscp; /* intermediate system config ptr */
- SCB *pScb; /* system control block ptr */
- int unit=pDrvCtrl->unit;
- pDrvCtrl->txBlocked = FALSE;
- /* Get pointers */
- pScp = pDrvCtrl->pScp;
- pIscp = pDrvCtrl->pIscp;
- pScb = pDrvCtrl->pScb;
- /* perform board specific initializations */
- sys596Init (pDrvCtrl->unit);
- /* Issue the reset operation to the device */
- sys596Port (unit, PORT_RESET, 0);
- /* Initialize the SCP */
- pScp->scpRsv1 =
- pScp->scpRsv2 =
- pScp->scpRsv3 = 0;
- pScp->scpSysbus = pDrvCtrl->sysbus;
- pTemp = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs, pIscp);
- LINK_WR (&pScp->pIscp, pTemp); /* point SCP to ISCP */
- /* Initialize the ISCP */
- pIscp->iscpBusy = 1;
- pIscp->iscpRsv1 = 0;
- pTemp = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs, pScb);
- LINK_WR (&pIscp->pScb, pTemp); /* point ISCP to SCB */
- /* Initialize the SCB */
- bzero ((char *)pScb, sizeof (SCB));
- /* Tell the device where the SCP is located */
- pTemp = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs, pScp);
- sys596Port (unit, PORT_NEWSCP, (UINT32) pTemp);
- sys596ChanAtn (unit);
- /*
- * The device will now read our SCP and ISCP. It will clear the busy
- * flag in the ISCP.
- */
- taskDelay (50);
- if ( pIscp->iscpBusy == 1 )
- {
- printf ("nei%d: device did not initializen", pDrvCtrl->unit);
- return (ERROR);
- }
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596Diag - format and issue a diagnostic command
- *
- * Assumes that device interrupts are disabled.
- */
- LOCAL STATUS ei82596Diag
- (
- END_CTRL *pDrvCtrl
- )
- {
- bzero ((char *)pDrvCtrl->pCfd, sizeof (CFD)); /* zero command frame */
- ei82596Action (pDrvCtrl, CFD_C_DIAG); /* run diagnostics */
- if (!(pDrvCtrl->pCfd->cfdStatus & CFD_S_OK))
- {
- printErr ("eiDiag: i82596 diagnostics failed. cfdStatus=0x%xn",
- pDrvCtrl->pCfd->cfdStatus);
- return (ERROR);
- }
- return (OK);
- }
- /*******************************************************************************
- *
- * ei82596Config - format and issue a config command
- */
- LOCAL void ei82596Config
- (
- END_CTRL *pDrvCtrl
- )
- {
- bzero ((char *)pDrvCtrl->pCfd, sizeof (CFD)); /* zero command frame */
- /* Recommeded i82596 User's Manual configuration values. Note that
- * the original manual, #296443-001, was full of errors. Errors in the
- * description of the config bytes that I am aware of are noted below.
- * It is possible there are further errors. Intel has replaced the
- * manual with #296853-001, which does correct some errors, but not all.
- */
- pDrvCtrl->pCfd->cfdConfig.ccByte8 = 0x8e;
- pDrvCtrl->pCfd->cfdConfig.ccByte9 = 0xc8;
- /* The manual is wrong about bit 7 in byte 10.
- * A '0' allows reception of bad packets.
- * A '1' causes rejection of bad packets.
- */
- pDrvCtrl->pCfd->cfdConfig.ccByte10 = 0xc0;
- pDrvCtrl->pCfd->cfdConfig.ccByte11 = 0x2e; /* loopback, NSAI */
- pDrvCtrl->pCfd->cfdConfig.ccByte12 = 0x00;
- pDrvCtrl->pCfd->cfdConfig.ccByte13 = 0x60;
- pDrvCtrl->pCfd->cfdConfig.ccByte14 = 0x00;
- pDrvCtrl->pCfd->cfdConfig.ccByte15 = 0xf2;
- pDrvCtrl->pCfd->cfdConfig.ccByte16 =
- DRV_FLAGS_ISSET (EI_PROMISC) ? 0x01 : 0x00;
- pDrvCtrl->pCfd->cfdConfig.ccByte17 = 0x00;
- pDrvCtrl->pCfd->cfdConfig.ccByte18 = 0x40;
- /* The manual is wrong about 2 bits in byte 19.
- * Bit 5, multicast, a '1' disables.
- * Bit 2, include CRC, a '1' disables.
- */
- pDrvCtrl->pCfd->cfdConfig.ccByte19 =
- DRV_FLAGS_ISSET (EI_MCAST) ? 0xdf : 0xff;
- pDrvCtrl->pCfd->cfdConfig.ccByte20 = 0x00;
- pDrvCtrl->pCfd->cfdConfig.ccByte21 = 0x3f;
- ei82596Action (pDrvCtrl, CFD_C_CONFIG); /* configure the chip */
- }
- /*******************************************************************************
- *
- * eiIASetup - format and issue an interface address command
- */
- LOCAL void ei82596IASetup
- (
- END_CTRL * pDrvCtrl
- )
- {
- bzero ((char *)pDrvCtrl->pCfd, sizeof (CFD)); /* zero command frame */
- bcopy (
- (char *)END_HADDR(&pDrvCtrl->endObj),
- (char *)pDrvCtrl->pCfd->cfdIASetup.ciAddress,
- EADDR_LEN
- );
- ei82596Action (pDrvCtrl, CFD_C_IASETUP); /* setup the address */
- }
- /*******************************************************************************
- *
- * ei82596RxStartup - start up the Receive Unit
- *
- * Starts up the Receive Unit. Assumes that the receive structures are set up.
- */
- LOCAL void ei82596RxStartup
- (
- END_CTRL *pDrvCtrl
- )
- {
- SCB *pScb = pDrvCtrl->pScb;
- void * pTemp;
- if (pScb->scbStatus & SCB_S_RUREADY) /* already running */
- {
- return;
- }
- pTemp = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs, pDrvCtrl->pFreeRfd);
- LINK_WR (&pScb->pRF, pTemp); /* point to free Rfd */
- if (! DRV_FLAGS_ISSET (EI_POLLING))
- SYS_INT_DISABLE( pDrvCtrl );
- if ((pScb->scbStatus & SCB_S_RUMASK) != SCB_S_RUIDLE)
- ei82596Command (pDrvCtrl, SCB_C_RUABORT); /* abort if not idle */
- ei82596Command (pDrvCtrl, SCB_C_RUSTART); /* start receive unit */
- if (! DRV_FLAGS_ISSET (EI_POLLING))
- SYS_INT_ENABLE( pDrvCtrl );
- }
- /*******************************************************************************
- *
- * ei82596Action - execute the specified action with the CFD
- *
- * Do the command contained in the CFD synchronously, so that we know
- * it's complete upon return. This routine assumes that interrupts from the
- * device have been disabled.
- */
- LOCAL void ei82596Action
- (
- END_CTRL *pDrvCtrl,
- UINT16 action
- )
- {
- void *pTemp;
- CFD *pCfd;
- SCB *pScb;
- int unit=pDrvCtrl->unit;
- pCfd = pDrvCtrl->pCfd;
- pScb = pDrvCtrl->pScb;
- while (1) /* wait for idle command unit */
- {
- if ((pScb->scbStatus & SCB_S_CUMASK) == SCB_S_CUIDLE)
- break;
- }
- { /* Prepare and issue the command to the device */
- /* fill in CFD */
- pCfd->cfdStatus = 0; /* clear status */
- pCfd->cfdCommand = CFD_C_EL | action; /* fill in action */
- LINK_WR (&pCfd->link, NULL); /* terminate link */
- /* and the SCB */
- pScb->scbCommand =
- SCB_S_CX | /* ack any events */
- SCB_S_FR |
- SCB_S_CNA |
- SCB_S_RNR |
- SCB_C_CUSTART; /* start command unit */
- pTemp = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs, pCfd);
- LINK_WR (&pScb->pCB, pTemp); /* point chip at CFD */
- sys596ChanAtn (unit); /* notify device of new command */
- }
- while (1) /* wait for command acceptance and interrupt */
- {
- if ((pScb->scbCommand == 0) && (pScb->scbStatus & SCB_S_CNA))
- break;
- }
- /* Acknowledge the event to the device */
- pScb->scbCommand = (SCB_S_CX | SCB_S_CNA);
- sys596ChanAtn (unit);
- while (1) /* wait for acknowledge acceptance */
- {
- if (pScb->scbCommand == 0)
- break;
- }
- }
- /*******************************************************************************
- *
- * ei82596Command - deliver a command to the 82596 via SCB
- *
- * This function causes the device to execute a command. It should be called
- * with interrupts from the device disabled. An error status is returned if
- * the command field does not return to zero, from a previous command, in a
- * reasonable amount of time.
- */
- LOCAL STATUS ei82596Command
- (
- END_CTRL *pDrvCtrl,
- UINT16 cmd
- )
- {
- int loopy;
- SCB * pScb;
- pScb = pDrvCtrl->pScb;
- for (loopy = 0x8000; loopy--;)
- {
- if (pScb->scbCommand == 0) /* wait for cmd zero */
- break;
- }
- if (loopy > 0)
- {
- pScb->scbCommand = cmd; /* fill in command */
- sys596ChanAtn (pDrvCtrl->unit); /* channel attention */
- return (OK);
- }
- else
- {
- logMsg("ei driver: command field frozenn", 0, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- }
- /*******************************************************************************
- *
- * ei82596DeviceRestart - reset the chip and reinitialize the tx and rx queues
- *
- */
- LOCAL void ei82596DeviceRestart
- (
- END_CTRL * pDrvCtrl
- )
- {
- EI_LIST tmpQueue;
- EI_NODE *pNode;
- BOOL doMuxTxRestart; /* Do we need to restart the mux? */
- int intLockLevel;
- /* mark the interface -- down */
- END_FLAGS_CLR (&pDrvCtrl->endObj, (IFF_UP | IFF_RUNNING));
- /* disable interrupts */
- SYS_INT_DISABLE( pDrvCtrl );
- doMuxTxRestart = pDrvCtrl->txBlocked;
- /* reset the device */
- if (ei82596DeviceStart (pDrvCtrl) == ERROR)
- return;
- /* discard the current cbl list, thread it to clean list */
- END_TX_SEM_TAKE (&pDrvCtrl->endObj, WAIT_FOREVER);
- ei82596QCat (&pDrvCtrl->cleanQueue, &pDrvCtrl->cblQueue);
- ei82596QInit (&pDrvCtrl->cblQueue);
- END_TX_SEM_GIVE (&pDrvCtrl->endObj);
- /* reset watchdog timer counts */
- pDrvCtrl->wdTxTimeout = 0;
- pDrvCtrl->wdRxTimeout = 0;
- /* stop the receiver, and command units */
- ei82596Command (pDrvCtrl, SCB_C_RUABORT);
- ei82596Command (pDrvCtrl, SCB_C_CUABORT);
- if (ei82596Diag (pDrvCtrl) == ERROR)
- return;
- ei82596Config (pDrvCtrl);
- ei82596IASetup (pDrvCtrl);
- /* If we are not cleaning now, schedule one */
- if (! pDrvCtrl->txCleaning)
- {
- pDrvCtrl->txCleaning = TRUE;
- netJobAdd ((FUNCPTR) ei82596TxCleanQ, (int) pDrvCtrl, 0, 0, 0, 0);
- }
- if (pDrvCtrl->txQueue.head != NULL) /* anything to flush? */
- ei82596TxQFlush (pDrvCtrl); /* flush the tx q */
- else
- pDrvCtrl->txIdle = TRUE; /* transmitter idle */
- /* interlock with ei82596Int() -- SPR 70154
- *
- * When restarting the device, we normally go through the
- * list of RFDs and simply drop packets which have been
- * queued but not yet processed. However, we do not want
- * to do that if the interrupt handler is running, since
- * that could result in dropped RFDs and a non-functional
- * interface. So, we use the rxHandling mechanism
- * intended to provide communication between the ISR and
- * the ei82596RxIntHandle().
- *
- * There are two other things relevant to this. First,
- * this is a real corner case, which is not likely to
- * happen often. Second, if we don't process the queue
- * here, then the results will be that old frames will be
- * sent up to the network stack, which is not really a
- * terrible thing. So, if an interrupt is already being
- * processed, simply avoid processing the RX queue.
- */
- intLockLevel = intLock();
- if ( ! pDrvCtrl->rxHandling )
- {
- pDrvCtrl->rxHandling = TRUE;
- intUnlock(intLockLevel);
- /* process the rx queue */
- tmpQueue = pDrvCtrl->rxQueue;
- ei82596QInit (&pDrvCtrl->rxQueue);
- pDrvCtrl->pFreeRfd = NULL;
- while ((pNode = ei82596QGet (&tmpQueue)) != NULL)
- ei82596RxQPut (pDrvCtrl, (RFD *)pNode);
- }
- else
- {
- intUnlock(intLockLevel);
- }
- /* We may have missed an interrupt, since rxHandling was TRUE and
- * ei82596Int() drops interrupts when that is so.
- *
- * Call the handler, just in case. ei82596RxIntHandle() will
- * just return if there is no work for it to do.
- */
- (void) netJobAdd ((FUNCPTR) ei82596RxIntHandle,
- (int) pDrvCtrl,0, 0, 0, 0);
- /* mark the interface -- up */
- END_FLAGS_SET (&pDrvCtrl->endObj, (IFF_UP | IFF_RUNNING));
- /* enable interrupts */
- SYS_INT_ENABLE( pDrvCtrl );
- if (doMuxTxRestart)
- muxTxRestart (&pDrvCtrl->endObj);
- wdStart (pDrvCtrl->wid, (int) pDrvCtrl->wdInterval,
- (FUNCPTR) ei82596WatchDog, (int)pDrvCtrl);
- return;
- }
- /*******************************************************************************
- *
- * ei82596TxQPut - 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 and attempt to feed
- * the queue to the device.
- */
- LOCAL void ei82596TxQPut
- (
- END_CTRL * pDrvCtrl,
- TFD * pTfd
- )
- {
- pTfd->status = 0; /* fill in TFD fields */
- pTfd->command = CFD_C_XMIT; /* EL set later */
- pTfd->count |= TFD_CNT_EOF; /* data kept in frame */
- pTfd->reserved = 0; /* must be zero */
- LINK_WR (& pTfd->lBufDesc, NULL); /* TBDs not used */
- SYS_INT_DISABLE( pDrvCtrl );
- /* enqueue the TFD */
- ei82596QPut (pDrvCtrl, (EI_LIST *)&pDrvCtrl->txQueue, (EI_NODE*)pTfd);
- if (pDrvCtrl->txIdle) /* transmitter idle */
- ei82596TxQFlush (pDrvCtrl); /* flush txQueue */
- SYS_INT_ENABLE( pDrvCtrl );
- }
- /*******************************************************************************
- *
- * ei82596TxQFlush - make cmd unit of device start processing cmds
- *
- * This routine flushes the contents of the txQ to the cblQ and starts the
- * device transmitting the cblQ. Called only if transmit queue is not empty.
- * Sometimes called from interrupt handler.
- */
- LOCAL void ei82596TxQFlush
- (
- END_CTRL *pDrvCtrl
- )
- {
- void * pTemp;
- extern int sysClkRateGet(); /* we call this */
- ((TFD*)pDrvCtrl->txQueue.tail)->command |= CFD_C_EL;/* EL terminate q */
- pDrvCtrl->cblQueue.head = pDrvCtrl->txQueue.head; /* remember cbl head */
- pDrvCtrl->cblQueue.tail = pDrvCtrl->txQueue.tail; /* remember cbl tail */
- ei82596QInit ((EI_LIST *)&pDrvCtrl->txQueue); /* tx queue now empty */
- pTemp = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs,
- pDrvCtrl->cblQueue.head);
- LINK_WR (&pDrvCtrl->pScb->pCB, pTemp); /* point CU to head */
- pDrvCtrl->txIdle=FALSE; /* transmitter busy */
- /* start command unit */
- ei82596Command (pDrvCtrl, SCB_C_CUSTART);
- }
- /*******************************************************************************
- *
- * ei82596RxQPut - return a RFD to the receive queue for use by the device
- */
- LOCAL void ei82596RxQPut
- (
- END_CTRL *pDrvCtrl,
- RFD *pRfd
- )
- {
- RFD * pTail;
- RFD * pFree; /* temporary for updating pFreeRfd */
- pRfd->status = 0; /* clear status */
- pRfd->command = CFD_C_EL; /* new end of list */
- pRfd->count = 0; /* clear actual count */
- pRfd->bufSize = ETHERMTU + EH_SIZE; /* fill in size */
- LINK_WR (& pRfd->lBufDesc, 0xffffffff); /* no RBD used */
- pTail = (RFD *) pDrvCtrl->rxQueue.tail; /* remember tail */
- /* Put the RFD on the list */
- ei82596QPut (pDrvCtrl, (EI_LIST *) & pDrvCtrl->rxQueue, (EI_NODE *) pRfd);
- if (pTail != NULL)
- {
- pDrvCtrl->wdRxTimeout = 0; /* reset timeout count */
- pTail->command &= ~CFD_C_EL; /* clear old tail EL */
- if (pTail->status & (CFD_S_COMPLETE | CFD_S_BUSY))
- {
- pDrvCtrl->pFreeRfd = pRfd; /* link questionable */
- }
- else if (!(pDrvCtrl->pScb->scbStatus & SCB_S_RUREADY))
- /* receiver dormant */
- {
- /* SPR #70148
- * It is not possible to maintain pFreeRfd during normal operation.
- *
- * If we do not update pFreeRfd before starting the device, then
- * the result will be multiple pointers to a single cluster. This
- * will cause the network stack to die. Usually, tNetTask will
- * crash with an access to address 0xa002XXXX.
- *
- * To avoid this, we go through the RX queue and explicitly
- * find the first free RFD.
- */
- /* first update pFreeRfd */
- pFree = (RFD *)pDrvCtrl->rxQueue.head;
- while ( pFree && ( pFree->status & ( CFD_S_COMPLETE | CFD_S_BUSY ) ) )
- {
- /* use logical address for linked list */
- pFree = (RFD *)(LINK_RD(&pFree->lNext));
- }
- /* use physical address for pFreeRfd */
- pDrvCtrl->pFreeRfd = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs, pFree);
- /* now start the receiver */
- ei82596RxStartup (pDrvCtrl); /* start receive unit */
- }
- }
- else
- {
- pDrvCtrl->pFreeRfd = pRfd; /* first free RFD */
- }
- }
- /*******************************************************************************
- *
- * ei82596RxQGet - get a successfully received frame from the receive queue
- *
- * RETURNS: ptr to valid RFD, or NULL if none available
- */
- LOCAL RFD *ei82596RxQGet
- (
- END_CTRL *pDrvCtrl
- )
- {
- RFD *pRfd = NULL;
- if (ei82596RxQFull (pDrvCtrl))
- pRfd = (RFD *) ei82596QGet ((EI_LIST *)&pDrvCtrl->rxQueue);
- return (pRfd);
- }
- /*******************************************************************************
- *
- * ei82596RxQFull - boolean function to determine fullness of receive queue
- *
- * RETURNS: TRUE if completely received frame is available, FALSE otherwise.
- */
- LOCAL BOOL ei82596RxQFull
- (
- END_CTRL *pDrvCtrl
- )
- {
- return ((pDrvCtrl->rxQueue.head != NULL) &&
- (((RFD*)pDrvCtrl->rxQueue.head)->status & CFD_S_COMPLETE));
- }
- /*******************************************************************************
- *
- * ei82596QInit - initialize a singly linked node queue
- */
- LOCAL void ei82596QInit
- (
- volatile EI_LIST *pQueue
- )
- {
- pQueue->head = pQueue->tail = NULL; /* init head & tail */
- }
- /*******************************************************************************
- *
- * ei82596QCat - Concatenate a queue to another
- *
- * This routine concatenates the queue <pQSrc> to the tail of <pQDest>.
- *
- * RETURNS: NONE
- */
- LOCAL void ei82596QCat
- (
- volatile EI_LIST * pQDest, /* Destination Queue */
- volatile EI_LIST * pQSrc /* Source Queue */
- )
- {
- if (pQSrc->head == NULL)
- return;
- if (pQDest->head == NULL)
- pQDest->head = pQSrc->head;
- else
- LINK_WR(&pQDest->tail->pNext, (EI_NODE *)pQSrc->head);
- pQDest->tail = pQSrc->tail;
- }
- /*******************************************************************************
- *
- * ei82596QGet - get a node from the head of a node queue
- *
- * RETURNS: ptr to useable node, or NULL ptr if none available
- */
- LOCAL EI_NODE *ei82596QGet
- (
- volatile EI_LIST *pQueue
- )
- {
- EI_NODE *pNode;
- if ((pNode = (EI_NODE *) pQueue->head) != NULL) /* if list not empty */
- pQueue->head = (EI_NODE *) LINK_RD (&pNode->pNext); /* advance ptr */
- return (pNode);
- }
- /*******************************************************************************
- *
- * ei82596QPut - put a node on the tail of a node queue
- */
- LOCAL void ei82596QPut
- (
- END_CTRL *pDrvCtrl,
- EI_LIST *pQueue,
- EI_NODE *pNode
- )
- {
- void * pTemp;
- LINK_WR (&pNode->lNext, NULL); /* mark "end of list" */
- LINK_WR (&pNode->pNext, NULL);
- if (pQueue->head == NULL) /* if list empty */
- pQueue->tail = pQueue->head = pNode; /* set both ptrs */
- else
- {
- pTemp = CACHE_DRV_VIRT_TO_PHYS (&pDrvCtrl->cacheFuncs, pNode);
- LINK_WR (&pQueue->tail->lNext, pTemp); /* link node on tail */
- LINK_WR (&pQueue->tail->pNext, pNode);
- pQueue->tail = pNode; /* update tail ptr */
- }
- }
- /*******************************************************************************
- *
- * ei82596WatchDog - if the watchdog timer fired off, we've hung during transmit
- *
- * Check the scb command to verify and if so, reinit.
- */
- LOCAL void ei82596WatchDog
- (
- END_CTRL *pDrvCtrl
- )
- {
- SCB *pScb;
- int reset = FALSE;
- pScb = pDrvCtrl->pScb;
- /* Test for transmit timeout.
- *
- * Timeout occurs if the scb status indicates that CU (transmit)
- * remains active for EI_TX_TIMEOUT iterations of ei82596WatchDog.
- * It might make sense to loop through the cfd's to look for
- * a complete bit as a sanity check, but given that transmit
- * was active, we will go ahead and do a reset.
- */
- if ((! pDrvCtrl->txIdle)
- && (pScb->scbStatus & SCB_S_CUACTIVE))
- {
- if (++(pDrvCtrl->wdTxTimeout) > EI_TX_TIMEOUT)
- {
- pDrvCtrl->txLockups++; /* failure count */
- END_ERR_ADD (&pDrvCtrl->endObj, MIB2_OUT_ERRS, +1);
- reset = TRUE;
- }
- }
- /* Test for receive timeout.
- *
- * Timeout occurs if the scb status indicates that RU (receive)
- * remains out of resources for EI_RX_TIMEOUT iterations of ei82596WatchDog.
- */
- if (pScb->scbStatus & SCB_S_RUNORSRC)
- {
- if (++(pDrvCtrl->wdRxTimeout) > EI_RX_TIMEOUT)
- {
- pDrvCtrl->rxLockups++; /* failure count */
- reset = TRUE;
- }
- }
- /* reinitialize the unit or restart the watchdog */
- if (reset)
- {
- logMsg ("nei%d: resetn", pDrvCtrl->unit, 0, 0, 0, 0, 0);
- #ifdef DRV_DEBUG
- logMsg ("rxLockups=%d txLockups=%dn", (int)pDrvCtrl->rxLockups,
- (int)pDrvCtrl->txLockups, 0, 0, 0, 0);
- #endif
- (void) netJobAdd ((FUNCPTR) ei82596DeviceRestart, (int) pDrvCtrl
- , 2, 3, 4, 5);
- return;
- }
- if (pDrvCtrl->txBlocked)
- {
- pDrvCtrl->txBlocked = FALSE;
- netJobAdd ((FUNCPTR) muxTxRestart, (int) &pDrvCtrl->endObj, 0, 0,
- 0, 0);
- }
- wdStart (pDrvCtrl->wid, (int) pDrvCtrl->wdInterval,
- (FUNCPTR) ei82596WatchDog, (int)pDrvCtrl);
- }
- /* END OF DRIVER */
- #ifdef DRV_DEBUG
- eiPoolShow ()
- {
- END_CTRL *pDrvCtrl = (END_CTRL *)endFindByName ("ei", 0);
- netPoolShow (pDrvCtrl->endObj.pNetPool);
- }
- eiTxRestart ()
- {
- END_CTRL *pDrvCtrl = pEndCtrl;
- pDrvCtrl->txBlocked = FALSE;
- muxTxRestart (&pDrvCtrl->endObj);
- }
- eiQShow
- (
- char *str,
- EI_LIST *pQ
- )
- {
- printf ("%s head=0x%x tail=0x%xn", str, pQ->head, pQ->tail);
- taskDelay (2);
- return 0;
- }
- /*******************************************************************************
- *
- * ei82596NodeCount - count the number of nodes in an EI_LIST
- *
- */
- LOCAL int ei82596NodeCount
- (
- volatile EI_LIST *pList
- )
- {
- int count=0;
- volatile EI_NODE *pNode = pList->head;
- while (pNode)
- {
- count++;
- pNode = LINK_RD (&pNode->pNext);
- }
- return count;
- }
- int eiShow()
- {
- END_CTRL *pDrvCtrl = pEndCtrl;
- int cblCount;
- int freeCount;
- int cleanCount;
- int txCount;
- int rxCount;
- eiQShow ("cbl", &pDrvCtrl->cblQueue);
- eiQShow ("free", &pDrvCtrl->freeQueue);
- eiQShow ("clean", &pDrvCtrl->cleanQueue);
- eiQShow ("tx", &pDrvCtrl->txQueue);
- eiQShow ("rx", &pDrvCtrl->rxQueue);
- END_TX_SEM_TAKE (&pDrvCtrl->endObj, WAIT_FOREVER);
- cblCount = ei82596NodeCount (&pDrvCtrl->cblQueue);
- freeCount = ei82596NodeCount (&pDrvCtrl->freeQueue);
- cleanCount = ei82596NodeCount (&pDrvCtrl->cleanQueue);
- txCount = ei82596NodeCount (&pDrvCtrl->txQueue);
- END_TX_SEM_GIVE (&pDrvCtrl->endObj);
- rxCount = ei82596NodeCount (&pDrvCtrl->rxQueue);
- printf ("ei%d: END_CTRL=0x%x flags=0x%x txIdle=%d "
- "txCleaning=%d rxHandling=%dn",
- pDrvCtrl->unit, (int)pDrvCtrl, pDrvCtrl->flags, pDrvCtrl->txIdle,
- pDrvCtrl->txCleaning, pDrvCtrl->rxHandling);
- printf ("ei%d: rxLockups=%d txLockups=%d resetCount=%dn",
- pDrvCtrl->unit, (int)pDrvCtrl->rxLockups, (int)pDrvCtrl->txLockups,
- pDrvCtrl->resetCount);
- printf ("ei%d: txQ -- cbl=%d free=%d clean=%d txQueue=%d nLoan=%dn",
- pDrvCtrl->unit, cblCount, freeCount, cleanCount,txCount, nLoan);
- printf ("ei%d: rxQ -- count=%dn",
- pDrvCtrl->unit, rxCount);
- taskDelay(2);
- return 0;
- }
- int printQueue
- (
- char *str,
- EI_LIST *pQueue
- )
- {
- EI_NODE *pNode;
- printf( "%s: ", str);
- pNode = pQueue->head;
- do
- {
- printf ("-> 0x%x ", pNode);
- if (pNode && pNode != 0xffffffff)
- pNode = LINK_RD (&pNode->pNext);
- } while (pNode && pNode != pQueue->tail);
- printf ("n");
- taskDelay (20);
- }
- #endif /* DRV_DEBUG */
- /* END OF FILE */