/* ns83902End.c - National Semiconductor DP83902A ST-NIC */ /* * Copyright (c) 1998-2008, 2013 Wind River Systems, Inc. * * The right to copy, distribute, modify or otherwise make use * of this software may be licensed only pursuant to the terms * of an applicable Wind River license agreement. */ /* Copyright 1998-2000 Hitachi ULSI Systems Co.,LTD.,All Rights Reserved */ /* modification history -------------------- 01u,14jun13,fao Fix Coverity Prevent warnings.(WIND00414265) 01t,25nov08,dlk Use 'scalable' semaphore APIs rather than 'inline' APIs. 01s,27aug08,dlk Move from deprecated 'minimal' semaphore API to 'inline' API. for TX semaphore. 01r,07jan08,dlk Rename 'fast' mutex as 'minimal' mutex. 01q,14dec07,dlk Use fast mutex for END TX semaphore. 01q,05dec07,h_k fixed wrong mac address byte order in BE. (CQ:107499) 01p,05sep06,kch Removed unnecessary coreip header includes. 01o,31aug04,mdo Documentation fixes for apigen 01n,10dec03,rcs fixed warnings for base6 01m,24jul03,rcs SPR# 88553 in ISR moved interface up test to after acknowledge 01l,03feb03,m_h refgen problems 01k,15jan03,m_h Unsupported by IPv6 01j,24apr02,h_k changed dummy read to read from pDrvCtrl->ioPort (SPR #74019). 01i,25mar02,h_k changed overrun handling to be done in ISR and transmit buffer taking from a static data (SPR #74019). 01h,14jan02,dat Removing warnings from Diab compiler 01g,10oct01,dat SPR 70829, driver used fp register - fixed., removed warnings, fixed casts for netClFree, fixed macros 01f,03oct00,zl ns83902Send() frees the Mblk chain in non-polled mode only. 01e,23may00,zl minor code cleanup. 01d,29apr00,zl redone TX interrupts, register read/write. 01c,21apr00,zl call netMblkClChainFree only if TX succeeds, use TX interrupts instead of polling. 01b,10apr00,zl renamed file to ns83902End.c 01a,20sep99,zl written based on nicMsEnd.c and nicEvbEnd.c */ /* This module implements the National Semiconductor dp83902A ST-NIC Ethernet network interface driver. This driver is moderately generic. The driver must be given several target-specific parameters. These parameters, and the mechanisms used to communicate them to the driver, are detailed below. The driver supports big-endian or little-endian architectures. EXTERNAL INTERFACE The only external interface is the ns83902EndLoad() routine, which expects the parameter as input. This parameter passes in a colon-delimited string of the format: ":::::" The ns83902EndLoad() function uses strtok() to parse the string. TARGET-SPECIFIC PARAMETERS \is \i unit A convenient holdover from the former model. This parameter is used only in the string name for the driver. \i baseAdrs Base address at which the NIC hardware device registers are located. \i vecNum This is the interrupt vector number of the hardware interrupt generated by this Ethernet device. \i intLvl This parameter defines the level of the hardware interrupt. \i dmaPort Address of the DMA port used to transfer data to the host CPU. \i bufSize Size of the NIC buffer memory in bytes. \i options Target specific options: bit0 - wide (0: byte, 1: word) bit1 - register interval (0: 1byte, 1: 2 bytes) \ie EXTERNAL SUPPORT REQUIREMENTS This driver requires four external support functions, and provides a hook function: \is \i void sysLanIntEnable (int level) This routine provides a target-specific interface for enabling Ethernet device interrupts at a specified interrupt level. \i void sysLanIntDisable (void) This routine provides a target-specific interface for disabling Ethernet device interrupts. \i STATUS sysEnetAddrGet (int unit, char *enetAdrs) This routine provides a target-specific interface for accessing a device Ethernet address. \i sysNs83902DelayCount This variable is used to introduce at least a 4 bus cycle (BSCK) delay between successive NIC chip selects. \ie SYSTEM RESOURCE USAGE This driver requires the following system resources: - one mutual exclusion semaphore - one interrupt vector SEE ALSO: muxLib, \tb DP83902A ST-NIC Serial Interface Controller for Twisted Pair */ /* configurations */ #undef NS83902_INSTRUMENT /* instrument the driver */ #undef NS83902_DEBUG /* log debug messages */ /* includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WR_IPV6 #include #endif /*WR_IPV6*/ #include #undef ETHER_MAP_IP_MULTICAST #include #include #include /* defines */ #define NS83902_DEV_NAME "nic" #define NS83902_DEV_NAME_LEN 4 #define NS83902_EADR_LEN 6 #define NS83902_SPEED 10000000 /* 10Mbps or 100Mbps */ #define NS83902_CRC_POLY 0x04c11db7 #define IMR_DISABLE 0 #define IMR_ENABLE (IMR_PRXE | IMR_OVWE | IMR_PTXE | IMR_TXEE) #define IMR_RX_DISABLE (IMR_OVWE | IMR_PTXE | IMR_TXEE) /* debug macros */ #ifdef NS83902_DEBUG #ifdef LOCAL #undef LOCAL #define LOCAL #endif #define NS83902_DEBUG_OFF 0x0000 #define NS83902_DEBUG_RX 0x0001 #define NS83902_DEBUG_TX 0x0002 #define NS83902_DEBUG_INT 0x0004 #define NS83902_DEBUG_POLL (NS83902_DEBUG_POLL_RX | NS83902_DEBUG_POLL_TX) #define NS83902_DEBUG_POLL_RX 0x0008 #define NS83902_DEBUG_POLL_TX 0x0010 #define NS83902_DEBUG_LOAD 0x0020 #define NS83902_DEBUG_IOCTL 0x0040 #define NS83902_DEBUG_POLL_REDIR 0x10000 #define NS83902_DEBUG_LOG_NVRAM 0x20000 #define NS83902_DEBUG_MB 0x40000 #define NS83902_DEBUG_TMP 0x80000 int ns83902Debug = NS83902_DEBUG_OFF; NET_POOL ns83902NetPool; /* global for easy access of end.pNetPool */ void * ns83902EndDevice; /* global for easy access of the device */ CL_POOL_ID ns83902ClPoolId; /* global for easy access to cluster pool */ SEM_ID ns83902DmaSemId; /* global for easy access to sem ID */ #define DRV_LOG(FLG, X0, X1, X2, X3, X4, X5, X6) \ if (ns83902Debug & FLG) \ logMsg(X0, X1, X2, X3, X4, X5, X6); #else /* NS83902_DEBUG */ #define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6) #endif /* NS83902_DEBUG */ /* * 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) \ do { \ *pResult = intConnect ((VOIDFUNCPTR *)INUM_TO_IVEC (pDrvCtrl->ivec),\ rtn, (int)arg); \ } while (0) #endif /* SYS_INT_CONNECT */ #ifndef SYS_INT_DISCONNECT #define SYS_INT_DISCONNECT(pDrvCtrl,rtn,arg,pResult) \ do { \ *pResult = OK; \ } while (0) #endif /* SYS_INT_DISCONNECT */ #ifndef SYS_INT_ENABLE #define SYS_INT_ENABLE(pDrvCtrl) \ do { \ IMPORT void sysLanIntEnable(int); \ sysLanIntEnable (pDrvCtrl->ilevel); \ } while (0) #endif /* SYS_INT_ENABLE */ #ifndef SYS_INT_DISABLE #define SYS_INT_DISABLE(pDrvCtrl) \ do { \ IMPORT STATUS sysLanIntDisable(int); \ sysLanIntDisable(pDrvCtrl->ilevel); \ } while (0) #endif /*SYS_INT_DISABLE*/ #ifndef SYS_ENET_ADDR_GET #define SYS_ENET_ADDR_GET(pDrvCtrl, pAddress) \ do { \ IMPORT STATUS sysEnetAddrGet (int, char*); \ sysEnetAddrGet (pDrvCtrl->unit, pAddress); \ } while (0) #endif /* SYS_ENET_ADDR_GET */ #ifndef SYS_NS83902_DELAY #define SYS_NS83902_DELAY() \ do { \ IMPORT UINT32 sysNs83902DelayCount; \ volatile UINT32 cx = 0; \ for (cx = 0; cx < sysNs83902DelayCount; cx++) \ /* spin */ ; \ } while (0) #endif /* SYS_NS83902_DELAY */ #ifndef SYS_MS_DELAY #define SYS_MS_DELAY(delay) \ do { \ IMPORT void sysMsDelay (UINT); \ sysMsDelay (delay); \ } while (0) #endif /* SYS_MS_DELAY */ #ifndef SYS_IN_SHORT #define SYS_IN_SHORT(pDrvCtrl,addr,data) \ do { \ ((data) = *((volatile USHORT *)(addr))); \ } while (0) #endif /* SYS_IN_SHORT*/ #ifndef SYS_OUT_SHORT #define SYS_OUT_SHORT(pDrvCtrl,addr,value) \ do { \ *((volatile USHORT *)(addr)) = (value); \ } while (0) #endif /* SYS_OUT_SHORT */ #ifndef SYS_IN_BYTE #define SYS_IN_BYTE(pDrvCtrl, addr, data) \ do { \ ((data) = *((volatile UCHAR *)(addr))); \ } while (0) #endif /* SYS_IN_BYTE */ #ifndef SYS_OUT_BYTE #define SYS_OUT_BYTE(pDrvCtrl, addr, value) \ do { \ *((volatile UCHAR *)(addr)) = (value); \ } while (0) #endif /* SYS_OUT_BYTE */ /* Setting/Getting on-chip registers */ #define NS83902_CR_GET(pDrvCtrl, value) \ do { \ SYS_IN_BYTE ((pDrvCtrl), \ ((pDrvCtrl)->pNic + \ (NS83902_CR) * (pDrvCtrl)->regInterval), \ (value)); \ SYS_NS83902_DELAY(); \ } while (0) #define NS83902_CR_SET(pDrvCtrl, value) \ do { \ SYS_OUT_BYTE ((pDrvCtrl), \ ((pDrvCtrl)->pNic + \ (NS83902_CR) * (pDrvCtrl)->regInterval), \ (value)); \ SYS_NS83902_DELAY(); \ } while (0) #define NS83902_REG_GET(pDrvCtrl, reg, value, page) \ (value = ns83902RegRead (pDrvCtrl, reg, page)) #define NS83902_REG_SET(pDrvCtrl, reg, value, page) \ (ns83902RegSet (pDrvCtrl, reg, value, page)) /* Macros for dealing with flags */ #define DRV_FLAGS_SET(setBits) \ (pDrvCtrl->flags |= (setBits)) #define DRV_FLAGS_ISSET(setBits) \ (pDrvCtrl->flags & (setBits)) #define DRV_FLAGS_CLR(clrBits) \ (pDrvCtrl->flags &= ~(clrBits)) #define DRV_FLAGS_GET() \ (pDrvCtrl->flags) #define NS83902_IS_IN_POLL_MODE() \ ((DRV_FLAGS_GET() & NS83902_FLAG_POLL) == NS83902_FLAG_POLL) #ifdef END_TX_SEM_INLINE #define NS83902_SEM_TAKE(pDrvCtrl, timeout) \ (void)semMTake_inline ((pDrvCtrl)->endObj.txSem, timeout, END_TXSEM_INLINE_OPTS) #define NS83902_SEM_GIVE(pDrvCtrl) \ (void)semMGive_inline((pDrvCtrl)->endObj.txSem, END_TXSEM_INLINE_OPTS) #elif defined(END_TX_SEM_SCALABLE) #define NS83902_SEM_TAKE(pDrvCtrl, timeout) \ (void)semMTakeScalable ((pDrvCtrl)->endObj.txSem, timeout, END_TXSEM_SCALABLE_OPTS) #define NS83902_SEM_GIVE(pDrvCtrl) \ (void)semMGiveScalable ((pDrvCtrl)->endObj.txSem, END_TXSEM_SCALABLE_OPTS) #else #define NS83902_SEM_TAKE(pDrvCtrl, timeout) \ (void)semTake ((pDrvCtrl)->endObj.txSem, timeout) #define NS83902_SEM_GIVE(pDrvCtrl) \ (void)semGive((pDrvCtrl)->endObj.txSem) #endif /* 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 END_FLAGS_ISSET(pEnd, setBits) \ ((pEnd)->flags & (setBits)) /* * Macros for read and write descriptors. * */ #define NS83902_RX_BUF_SZ sizeof(NS83902_RX_FRAME) #define NS83902_RX_HDR_SZ sizeof(NS83902_RX_HDR) #define NS83902_ETH_CRC_LEN 4 /* typedefs */ typedef struct rx_hdr { #if (_BYTE_ORDER == _BIG_ENDIAN) UINT8 nextRxPage; /* page next pkt starts at */ UINT8 status; /* status of packet */ #else UINT8 status; /* status of packet */ UINT8 nextRxPage; /* page next pkt starts at */ #endif UINT16 count; /* frame length */ } NS83902_RX_HDR; typedef struct rx_frame { UINT16 pad1; NS83902_RX_HDR rxHdr; /* receive status header */ ENET_HDR enetHdr; /* ethernet header */ UINT8 data[ETHERMTU]; /* frame data */ } NS83902_RX_FRAME; typedef char* NS83902_CLUSTER; typedef struct ns83902_end_device /* driver control structure */ { END_OBJ endObj; /* The class we inherit from. */ END_ERR lastError; /* Last error passed to muxError */ int unit; /* unit number */ int rringSize; /* RMD ring size */ UCHAR enetAddr[NS83902_EADR_LEN]; /* ethernet address */ char* pNic; /* address of NIC chip */ char* ioPort; /* port address of remote DMA */ NS83902_CLUSTER pCluster; /* Rx frame memory */ CL_POOL_ID pClPoolId; /* cluster pool */ int regInterval; /* address diff of adjacent regs */ BOOL wide; /* I/O port is 16 bit (when TRUE) */ int ivec; /* interrupt vector */ int ilevel; /* interrupt level */ UINT8 regPage; /* current register page */ UINT8 txStartPage; /* ptr to Tx start buffer */ UINT8 rxStartPage; /* ptr to Rx start buffer */ UINT8 rxStopPage; /* ptr to Rx stop buffer */ UINT8 nextPage; /* ptr to next page */ NS83902_DRV_FLAG flags; /* device specific flags */ BOOL rxHandling; /* rcv task is scheduled */ BOOL txBlocked; /* to implement flow control */ BOOL rxOvw; /* overwrite warning */ BOOL txResend; /* resend need at overwrite */ } NS83902_END_DEVICE; /* network buffers configuration */ M_CL_CONFIG ns83902MclBlkConfig = /* network mbuf configuration table */ { /* no. mBlks no. clBlks memArea memSize --------- ---------- ------- ------- */ 0, 0, NULL, 0 }; CL_DESC ns83902ClDescTbl [] = /* cluster pool configuration table */ { /* clSize num memArea memSize ------ ---- ------- ------- */ {0, 0, NULL, 0} }; int ns83902ClDescTblNumEnt = (NELEMENTS(ns83902ClDescTbl)); /* globals */ #ifdef NS83902_INSTRUMENT UINT32 ns83902TxTimeout = 0; /* number of transmit time-out */ UINT32 ns83902TxError = 0; /* number of transmit errors */ UINT32 ns83902RestartNb = 0; /* number of restart due to ring overflow */ UINT32 ns83902InitNb = 0; /* number of time device is re-initialized */ UINT32 ns83902TxNb = 0; /* number of transmitted packets */ UINT32 ns83902IntNb = 0; /* number of receive interrupt */ UINT32 ns83902Len = 0; /* length of the current received packet */ UINT32 ns83902HdrStat = 0; /* status byte of the current received packet */ UINT32 ns83902NextPage = 0; /* page pointer to the next received packet */ UINT32 ns83902CurrentPage = 0; /* start page of the current packet */ #endif /* forward declarations */ LOCAL void ns83902MARSet (NS83902_END_DEVICE* pDrvCtrl, UINT8 index, BOOL bSet); LOCAL STATUS ns83902AddrFilterSet (NS83902_END_DEVICE *pDrvCtrl, char* pAddr, BOOL bSet); LOCAL int ns83902HashIndex (char* eAddr); LOCAL void ns83902Int (NS83902_END_DEVICE* pDrvCtrl); LOCAL void ns83902Restart (NS83902_END_DEVICE *pDrvCtrl, UINT8 cr); LOCAL void ns83902HandleInt (NS83902_END_DEVICE *pDrvCtrl); LOCAL void ns83902Recv (NS83902_END_DEVICE *pDrvCtrl, NS83902_CLUSTER pCluster); LOCAL NS83902_CLUSTER ns83902ReadFrame (NS83902_END_DEVICE* pDrvCtrl); LOCAL void ns83902Config (NS83902_END_DEVICE* pDrvCtrl); LOCAL STATUS ns83902PktBufRead (NS83902_END_DEVICE *pDrvCtrl, UINT32 ns83902BufAddr, UINT32 len, char *pData); LOCAL STATUS ns83902Transmit (NS83902_END_DEVICE *pDrvCtrl, M_BLK* pMblk); LOCAL void ns83902ReadPort (NS83902_END_DEVICE* pDrvCtrl, char *pBuf, int len); LOCAL void ns83902WritePort (NS83902_END_DEVICE* pDrvCtrl, char *pBuf, int len); LOCAL STATUS ns83902InitParse (NS83902_END_DEVICE* pDrvCtrl, char * initString); LOCAL STATUS ns83902InitMem (NS83902_END_DEVICE* pDrvCtrl); LOCAL void ns83902RegSet (NS83902_END_DEVICE* pDrvCtrl, int reg, int value, int page); LOCAL UINT8 ns83902RegRead (NS83902_END_DEVICE* pDrvCtrl, int reg, int page); /* END Specific interfaces. */ END_OBJ * ns83902EndLoad (char *initString); LOCAL STATUS ns83902Unload (NS83902_END_DEVICE* pDrvCtrl); LOCAL STATUS ns83902Start (NS83902_END_DEVICE* pDrvCtrl); LOCAL STATUS ns83902Stop (NS83902_END_DEVICE* pDrvCtrl); LOCAL int ns83902Ioctl (NS83902_END_DEVICE* pDrvCtrl, int cmd, caddr_t data); LOCAL STATUS ns83902Send (NS83902_END_DEVICE* pDrvCtrl, M_BLK *pMblk); LOCAL STATUS ns83902MCastAddrAdd (NS83902_END_DEVICE *pDrvCtrl, char* pAddress); LOCAL STATUS ns83902MCastAddrDel (NS83902_END_DEVICE *pDrvCtrl, char* pAddress); LOCAL STATUS ns83902MCastAddrGet (NS83902_END_DEVICE *pDrvCtrl, MULTI_TABLE *pTable); LOCAL STATUS ns83902PollSend (NS83902_END_DEVICE *pDrvCtrl, M_BLK *pMblk); LOCAL STATUS ns83902PollReceive (NS83902_END_DEVICE *pDrvCtrl, M_BLK *pMblk); LOCAL STATUS ns83902PollStart (NS83902_END_DEVICE *pDrvCtrl); LOCAL STATUS ns83902PollStop (NS83902_END_DEVICE *pDrvCtrl); /* * Declare our function table. This is static across all driver * instances. */ LOCAL NET_FUNCS ns83902FuncTable = { (FUNCPTR)ns83902Start, /* Function to start the device. */ (FUNCPTR)ns83902Stop, /* Function to stop the device. */ (FUNCPTR)ns83902Unload, /* Unloading function */ (FUNCPTR)ns83902Ioctl, /* Ioctl function */ (FUNCPTR)ns83902Send, /* Send function */ (FUNCPTR)ns83902MCastAddrAdd, /* Multicast address add */ (FUNCPTR)ns83902MCastAddrDel, /* Multicast address delete */ (FUNCPTR)ns83902MCastAddrGet, /* Multicast table retrieve */ (FUNCPTR)ns83902PollSend, /* Polling send function */ (FUNCPTR)ns83902PollReceive, /* Polling receive function */ endEtherAddressForm, /* Put address info into a packet. */ endEtherPacketDataGet, /* Get a pointer to packet data. */ endEtherPacketAddrGet, /* Get packet addresses. */ NULL /* Bind function */ }; /****************************************************************************** * * ns83902EndLoad - initialize the driver and device * * This routine initializes the driver and the device to the operational state. * All of the device-specific parameters are passed in . * This routine can be called in two modes. If it is called with an empty but * allocated string, it places the name of this device (that is, "ln") into * the and returns 0. * * If the string is allocated and not empty, the routine attempts to load * the driver using the values specified in the string. * * RETURNS: An END object pointer, or NULL on error, or 0 and the name of the * device if the was NULL. */ END_OBJ* ns83902EndLoad ( char* initString /* string to be parsed */ ) { NS83902_END_DEVICE *pDrvCtrl; DRV_LOG (NS83902_DEBUG_LOAD, "Loading ns83902End...\n", 1, 2, 3, 4, 5, 6); if (initString == NULL) return (NULL); if (initString[0] == '\0') { bcopy((char *)NS83902_DEV_NAME, initString, NS83902_DEV_NAME_LEN); return (0); } /* allocate the device structure */ pDrvCtrl = (NS83902_END_DEVICE *)calloc (sizeof (NS83902_END_DEVICE), 1); if (pDrvCtrl == NULL) goto errorExit; /* parse the init string, filling in the device structure */ if (ns83902InitParse (pDrvCtrl, initString) == ERROR) goto errorExit; /* Have the BSP hand us our address. */ SYS_ENET_ADDR_GET (pDrvCtrl, (char*) &(pDrvCtrl->enetAddr)); DRV_LOG (NS83902_DEBUG_LOAD, "ENET Addr: %x:%x:%x:%x:%x:%x \n", pDrvCtrl->enetAddr[0], pDrvCtrl->enetAddr[1], pDrvCtrl->enetAddr[2], pDrvCtrl->enetAddr[3], pDrvCtrl->enetAddr[4], pDrvCtrl->enetAddr[5]); /* initialize the END and MIB2 parts of the structure */ if (END_OBJ_INIT (&pDrvCtrl->endObj, (DEV_OBJ *)pDrvCtrl, NS83902_DEV_NAME, pDrvCtrl->unit, &ns83902FuncTable, "ST-NIC Enhanced Network Driver") == ERROR || END_MIB_INIT (&pDrvCtrl->endObj, M2_ifType_ethernet_csmacd, &pDrvCtrl->enetAddr[0], NS83902_EADR_LEN, ETHERMTU, NS83902_SPEED) == ERROR) goto errorExit; /* set buffer address; reserve 8 pages for TX */ pDrvCtrl->txStartPage = 0; pDrvCtrl->rxStartPage = pDrvCtrl->txStartPage + 8; pDrvCtrl->nextPage = pDrvCtrl->rxStartPage; /* size of the whole ring */ pDrvCtrl->rringSize = pDrvCtrl->rxStopPage - pDrvCtrl->rxStartPage + 1; /* Perform memory allocation */ if (ns83902InitMem (pDrvCtrl) == ERROR) { DRV_LOG (NS83902_DEBUG_LOAD, "Error in InitMem...\n", 1, 2, 3, 4, 5, 6); goto errorExit; } /* set the flags to indicate readiness */ END_OBJ_READY (&pDrvCtrl->endObj, IFF_NOTRAILERS | IFF_MULTICAST | IFF_BROADCAST); DRV_LOG (NS83902_DEBUG_LOAD, "Done loading ns83902End...\n", 1, 2, 3, 4, 5, 6); /* save the device address */ #ifdef NS83902_DEBUG ns83902EndDevice = pDrvCtrl; ns83902DmaSemId = pDrvCtrl->endObj.txSem; #endif return (&pDrvCtrl->endObj); errorExit: ns83902Unload (pDrvCtrl); return NULL; } /******************************************************************************* * * ns83902InitParse - parse the initialization string * * Parse the input string and fill in values in the driver control structure. * * RETURNS: OK, or ERROR if any arguments are invalid. */ LOCAL STATUS ns83902InitParse ( NS83902_END_DEVICE * pDrvCtrl, char * initString ) { char * tok; char * pHolder = NULL; long address; UINT8 options; /* Parse the initString */ /* Unit number. */ tok = strtok_r (initString, ":", &pHolder); if (tok == NULL) return ERROR; pDrvCtrl->unit = atoi (tok); /* NIC address. */ tok = strtok_r (NULL, ":", &pHolder); if (tok == NULL) return ERROR; address = strtoul (tok, NULL, 16); pDrvCtrl->pNic = (char*) address; /* Interrupt vector. */ tok = strtok_r (NULL, ":", &pHolder); if (tok == NULL) return ERROR; pDrvCtrl->ivec = atoi (tok); /* Interrupt level. */ tok = strtok_r (NULL, ":", &pHolder); if (tok == NULL) return ERROR; pDrvCtrl->ilevel = atoi (tok); /* Remote DMA I/O address. */ tok = strtok_r (NULL, ":", &pHolder); if (tok == NULL) return ERROR; pDrvCtrl->ioPort = (char *) strtoul (tok, NULL, 16); /* Get ring buffer size in pages */ tok = strtok_r (NULL, ":", &pHolder); if (tok == NULL) return ERROR; pDrvCtrl->rxStopPage = strtoul (tok, NULL, 16) / NS83902_PAGE_SIZE; /* Get target options */ tok = strtok_r (NULL, ":", &pHolder); if (tok == NULL) return ERROR; options = strtoul (tok, NULL, 16); pDrvCtrl->wide = options & NS83902_WIDE_MASK; pDrvCtrl->regInterval = 1 << ((options & NS83902_INTERVAL_MASK) >> 1); DRV_LOG (NS83902_DEBUG_LOAD, "Processed all arguments\n", 1, 2, 3, 4, 5, 6); DRV_LOG (NS83902_DEBUG_LOAD, "Address %p Lvl %u Vec %d Port %p Ring %p \n", (int)pDrvCtrl->pNic, pDrvCtrl->ilevel, pDrvCtrl->ivec, (int)pDrvCtrl->ioPort, pDrvCtrl->rxStopPage, 6); DRV_LOG (NS83902_DEBUG_LOAD, "Transfer width: %s \n", (int)(pDrvCtrl->wide ? "word":"byte"), 2, 3, 4, 5, 6); DRV_LOG (NS83902_DEBUG_LOAD, "Register interval: %d \n", pDrvCtrl->regInterval, 2, 3, 4, 5, 6); return OK; } /******************************************************************************* * * ns83902InitMem - initialize memory for NIC chip * * Using data in the control structure, setup and initialize the memory * areas needed. If the memory address is not already specified, then allocate * cache safe memory. * * RETURNS: OK or ERROR. */ LOCAL STATUS ns83902InitMem ( NS83902_END_DEVICE * pDrvCtrl /* device to be initialized */ ) { /* allocate netpool */ if ((pDrvCtrl->endObj.pNetPool = malloc (sizeof(NET_POOL))) == NULL) return (ERROR); /* Set number of M-Blks and CL-Blks*/ ns83902MclBlkConfig.mBlkNum = pDrvCtrl->rringSize; ns83902MclBlkConfig.clBlkNum= pDrvCtrl->rringSize / 2; ns83902ClDescTbl[0].clNum = ns83902MclBlkConfig.clBlkNum; /* Calculate the total memory for all the M-Blks and CL-Blks. */ ns83902MclBlkConfig.memSize = ns83902MclBlkConfig.mBlkNum * (MSIZE + sizeof (long)) + ns83902MclBlkConfig.clBlkNum * (CL_BLK_SZ + sizeof(long)); /* allocate memory for M-Blks and CL-Blks */ ns83902MclBlkConfig.memArea = (char *) memalign (sizeof(long), ns83902MclBlkConfig.memSize); if (ns83902MclBlkConfig.memArea == NULL) return (ERROR); /* Calculate the memory size of all the clusters. */ ns83902ClDescTbl[0].clSize = NS83902_RX_BUF_SZ; ns83902ClDescTbl[0].memSize = sizeof(long) + ns83902ClDescTbl[0].clNum * (ns83902ClDescTbl[0].clSize + 8); /* Allocate memory for clusters */ ns83902ClDescTbl[0].memArea = (char *) memalign (sizeof(long), ns83902ClDescTbl[0].memSize); if (ns83902ClDescTbl[0].memArea == NULL) { DRV_LOG (NS83902_DEBUG_LOAD, "system memory unavailable\n", 1, 2, 3, 4, 5, 6); return (ERROR); } /* Save clusters start address for later use */ pDrvCtrl->pCluster = (NS83902_CLUSTER) ns83902ClDescTbl[0].memArea; /* Initialize the net buffer pool with buffers */ if (netPoolInit (pDrvCtrl->endObj.pNetPool, &ns83902MclBlkConfig, &ns83902ClDescTbl[0], ns83902ClDescTblNumEnt, NULL) == ERROR) { DRV_LOG (NS83902_DEBUG_LOAD, "Could not init buffering\n", 1, 2, 3, 4, 5, 6); return (ERROR); } /* Store the cluster pool id as others need it later. */ pDrvCtrl->pClPoolId = netClPoolIdGet (pDrvCtrl->endObj.pNetPool, NS83902_RX_BUF_SZ, FALSE); if (pDrvCtrl->pClPoolId == NULL) return (ERROR); #ifdef NS83902_DEBUG ns83902NetPool = *pDrvCtrl->endObj.pNetPool; ns83902ClPoolId = pDrvCtrl->pClPoolId; #endif return (OK); } /******************************************************************************* * * ns83902Start - start the device * * This function calls BSP functions to connect interrupts and start the * device running in interrupt mode. * * RETURNS: OK or ERROR * */ LOCAL STATUS ns83902Start ( NS83902_END_DEVICE *pDrvCtrl ) { STATUS result; /* Clear statuses */ pDrvCtrl->rxHandling = FALSE; pDrvCtrl->txBlocked = FALSE; pDrvCtrl->rxOvw = FALSE; pDrvCtrl->txResend = FALSE; /* reset the device */ NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_STP | CR_ABORT, CR_RPAGE0); /* connect interrupt handler */ SYS_INT_CONNECT (pDrvCtrl, ns83902Int, (int)pDrvCtrl, &result); if (result == ERROR) return ERROR; DRV_LOG (NS83902_DEBUG_LOAD, "Interrupt connected.\n", 1, 2, 3, 4, 5, 6); /* device config */ ns83902Config (pDrvCtrl); /* mark the interface -- up */ END_FLAGS_SET (&pDrvCtrl->endObj, (IFF_UP | IFF_RUNNING)); SYS_INT_ENABLE (pDrvCtrl); DRV_LOG (NS83902_DEBUG_LOAD, "interrupt enabled.\n", 1, 2, 3, 4, 5, 6); return (OK); } /******************************************************************************* * * ns83902Config - configure the NIC chip and program address * * This routine follows the algorithm in the ST-NIC manual for enabling * a NIC device on an active network. Essentially, this routine initializes * the NIC device. * * RETURNS: N/A. */ LOCAL void ns83902Config ( NS83902_END_DEVICE * pDrvCtrl /* device to be configured */ ) { UCHAR* pEnetAddr = pDrvCtrl->enetAddr; UCHAR dcr; /* Page 0 */ NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_STP | CR_ABORT, CR_RPAGE0); /* Enable broadcast, Endian, Word Size */ #if (_BYTE_ORDER == _BIG_ENDIAN) dcr = DCR_NOTLS | DCR_FIFO8 | DCR_BOS; #else dcr = DCR_NOTLS | DCR_FIFO8; #endif if (pDrvCtrl->wide == TRUE) dcr |= DCR_WTS; NS83902_REG_SET (pDrvCtrl, NS83902_DCR, dcr, CR_RPAGE0); /* Clear the remote byte count reg. */ NS83902_REG_SET (pDrvCtrl, NS83902_RBCR0, 0, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RBCR1, 0, CR_RPAGE0); /* Set to monitor and loopback mode */ NS83902_REG_SET (pDrvCtrl, NS83902_RCR, RCR_MON, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_TCR, TCR_MODE1, CR_RPAGE0); /* Accept Broadcast */ NS83902_REG_SET (pDrvCtrl, NS83902_RCR, (RCR_AB | RCR_AM), CR_RPAGE0); /* Set the transmit page and receive page */ NS83902_REG_SET (pDrvCtrl, NS83902_PSTART, pDrvCtrl->rxStartPage, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_PSTOP, pDrvCtrl->rxStopPage, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_TPSR, pDrvCtrl->txStartPage, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_BNRY, pDrvCtrl->rxStopPage - 1, CR_RPAGE0); /* Clear the pending interrupts and mask. */ NS83902_REG_SET (pDrvCtrl, NS83902_ISR, 0xff, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_IMR, IMR_DISABLE, CR_RPAGE0); /* set MAC Address */ NS83902_REG_SET (pDrvCtrl, NS83902_PAR0, pEnetAddr [0], CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_PAR1, pEnetAddr [1], CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_PAR2, pEnetAddr [2], CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_PAR3, pEnetAddr [3], CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_PAR4, pEnetAddr [4], CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_PAR5, pEnetAddr [5], CR_RPAGE1); /* Initialize the multicast list to accept all. */ NS83902_REG_SET (pDrvCtrl, NS83902_MAR0, 0x00, CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_MAR1, 0x00, CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_MAR2, 0x00, CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_MAR3, 0x00, CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_MAR4, 0x00, CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_MAR5, 0x00, CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_MAR6, 0x00, CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_MAR7, 0x00, CR_RPAGE1); NS83902_REG_SET (pDrvCtrl, NS83902_CURR, pDrvCtrl->rxStartPage, CR_RPAGE1); /* Page 0 */ NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_STP | CR_ABORT, CR_RPAGE0); /* set the interrupt mask */ NS83902_REG_SET (pDrvCtrl, NS83902_IMR, IMR_ENABLE, CR_RPAGE0); /* Start */ NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_ABORT | CR_STA, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_TCR, TCR_MODE0, CR_RPAGE0); } /******************************************************************************* * * ns83902WritePort - write to the DMA port * * RETURNS: N/A. */ LOCAL void ns83902WritePort ( NS83902_END_DEVICE* pDrvCtrl, char * pBuf, int len ) { int ix; UINT16 * pWide; if (pDrvCtrl->wide == TRUE) { len++; len = len >> 1; pWide = (UINT16 *) pBuf; for (ix = 0 ; ix < len; ix++) { SYS_OUT_SHORT (pDrvCtrl, pDrvCtrl->ioPort, *pWide++); SYS_NS83902_DELAY(); } } else { for (ix = 0 ; ix < len; ix++) { SYS_OUT_BYTE (pDrvCtrl, pDrvCtrl->ioPort, *pBuf++); SYS_NS83902_DELAY(); } } } /******************************************************************************* * * ns83902ReadPort - read the DMA port * * RETURNS: N/A. */ LOCAL void ns83902ReadPort ( NS83902_END_DEVICE* pDrvCtrl, char * pBuf, int len ) { int ix; UINT16 * pWide; if (pDrvCtrl->wide == TRUE) { len++; len = len >> 1; pWide = (UINT16 *) pBuf; for (ix = 0 ; ix < len; ix++) { SYS_IN_SHORT (pDrvCtrl, pDrvCtrl->ioPort, *pWide++); } } else { for (ix = 0 ; ix < len; ix++) { SYS_IN_BYTE (pDrvCtrl, pDrvCtrl->ioPort, *pBuf++); } } } /******************************************************************************* * * ns83902PktBufRead - read data from the NIC receive ring buffer * * This routine gets exclusive access to the remote DMA, and gets data from * the NIC's receive ring buffer. * * RETURNS: OK, or ERROR if obtaining the requested bytes encountered an error. */ LOCAL STATUS ns83902PktBufRead ( NS83902_END_DEVICE* pDrvCtrl, UINT32 ns83902BufAddr, UINT32 len, char* pData ) { /* Exclusive DMA access */ if (!NS83902_IS_IN_POLL_MODE()) NS83902_SEM_TAKE (pDrvCtrl, WAIT_FOREVER); DRV_LOG (NS83902_DEBUG_RX, "ns83902Receive: ns83902BufAddr:%u pData:%p len:%d\n", ns83902BufAddr, (int)pData, len, 4, 5, 6); /* Set up remote read DMA transfer */ NS83902_REG_SET (pDrvCtrl, NS83902_RBCR0, LSB(len), CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RBCR1, MSB(len), CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RSAR0, LSB(ns83902BufAddr), CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RSAR1, MSB(ns83902BufAddr), CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_RREAD, CR_RPAGE0); /* Get data through DMA port */ ns83902ReadPort (pDrvCtrl, pData, len); if (!NS83902_IS_IN_POLL_MODE()) NS83902_SEM_GIVE (pDrvCtrl); return (OK); } /******************************************************************************* * * ns83902ReadFrame - read a packet off the interface ring buffer into a cluster * * Allocates a new cluster from the cluster pool, and reads the frame from the * device into the cluster. * * RETURNS: a cluster or NULL if any error */ LOCAL NS83902_CLUSTER ns83902ReadFrame ( NS83902_END_DEVICE* pDrvCtrl ) { NS83902_RX_FRAME* pRx; NS83902_CLUSTER pRetCluster = NULL; UINT32 frameAddress; UINT16 len; /* len of Rx pkt */ UINT8 bnry; UINT8 rsr; UINT8 cur; /* Check receive status */ NS83902_REG_GET (pDrvCtrl, NS83902_RSR, rsr, CR_RPAGE0); if ((rsr & RSR_PRX) != RSR_PRX) return (pRetCluster); NS83902_REG_GET (pDrvCtrl, NS83902_CURR, cur, CR_RPAGE1); if (!(END_FLAGS_GET(&pDrvCtrl->endObj) & (IFF_UP | IFF_RUNNING)) || (pDrvCtrl->nextPage == cur)) return (pRetCluster); DRV_LOG (NS83902_DEBUG_RX, "Start Read!\n", 1, 2, 3, 4, 5, 6); /* * There is work to be done. * First we copy the NIC receive status header from the NIC buffer * into our local area. This is done so that we can obtain the length * of the packet before copying out the rest of it. Note that the length * field in the NIC header includes the Ethernet header, the data, and * the 4 byte FCS field. */ /* Get a cluster */ if ((pRx = (NS83902_RX_FRAME*) netClusterGet (pDrvCtrl->endObj.pNetPool, pDrvCtrl->pClPoolId)) == NULL) { pDrvCtrl->lastError.errCode = END_ERR_NO_BUF; muxError (&pDrvCtrl->endObj, &pDrvCtrl->lastError); DRV_LOG (NS83902_DEBUG_RX, "Cannot loan!\n", 1, 2, 3, 4, 5, 6); END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_ERRS, +1); return (pRetCluster); } /* read header */ DRV_LOG (NS83902_DEBUG_RX, "ns83902PktBufRead : Reading Header\n", 1, 2, 3, 4, 5, 6); if (ns83902PktBufRead (pDrvCtrl, pDrvCtrl->nextPage << 8, (NS83902_RX_HDR_SZ), (char *) &pRx->rxHdr) == ERROR) { DRV_LOG (NS83902_DEBUG_RX, "ns83902PktBufRead could not read packet header\n", 0, 0, 0, 0, 0, 0); netClFree (pDrvCtrl->endObj.pNetPool, (UCHAR *)pRx); return (pRetCluster); } /* Calculate real length */ len = pRx->rxHdr.count; #if (_BYTE_ORDER == _BIG_ENDIAN) len = ((len >> 8) & 0x00ff) + ((len << 8) & 0xff00) - NS83902_ETH_CRC_LEN; #else len = len - NS83902_ETH_CRC_LEN; #endif /* write back value to rxHdr */ pRx->rxHdr.count = len; /* Current frame address and next page */ frameAddress = (pDrvCtrl->nextPage << 8) + NS83902_RX_HDR_SZ; pDrvCtrl->nextPage = pRx->rxHdr.nextRxPage; /* valid frame checks */ #ifdef NS83902_INSTRUMENT ns83902Len = len; ns83902HdrStat = pRx->rxHdr.status; ns83902NextPage = pRx->rxHdr.nextRxPage; ns83902CurrentPage = cur; #endif /* check for critical error; should never happen */ if (pDrvCtrl->nextPage < pDrvCtrl->rxStartPage || pDrvCtrl->nextPage >= pDrvCtrl->rxStopPage ) { /* Can't recover, restart */ #ifdef NS83902_INSTRUMENT ns83902InitNb++; #endif ns83902Config (pDrvCtrl); pDrvCtrl->nextPage = pDrvCtrl->rxStartPage; /* mark the interface up */ END_FLAGS_SET (&pDrvCtrl->endObj, (IFF_UP | IFF_RUNNING | IFF_MULTICAST | IFF_BROADCAST)); netClFree (pDrvCtrl->endObj.pNetPool, (UCHAR *)pRx); return (pRetCluster); } /* * If the packet's receive status byte indicates an error the packet is * discarded and the receive page pointers are updated to point to the * next packet. */ if ((len < 60) || (len > 1514) || ((pRx->rxHdr.status & ~RSR_PHY) != RSR_PRX)) { DRV_LOG (NS83902_DEBUG_RX, "ns83902Rcv receive error: statusHeader=0x%x nextRxPage=%d \ currentPage=%d len=%d IntNb=%d\n", pRx->rxHdr.status, pRx->rxHdr.nextRxPage, cur, len, ns83902IntNb, 0); END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_ERRS, +1); /* update boundary pointer */ if (pDrvCtrl->nextPage == pDrvCtrl->rxStartPage) bnry = pDrvCtrl->rxStopPage; else bnry = pDrvCtrl->nextPage - 1; NS83902_REG_SET (pDrvCtrl, NS83902_BNRY, bnry, CR_RPAGE0); netClFree (pDrvCtrl->endObj.pNetPool, (UCHAR *)pRx); return (pRetCluster); } /* copy Ethernet packet section of the frame */ DRV_LOG (NS83902_DEBUG_RX, "ns83902PktBufRead : Reading Frame\n", 1, 2, 3, 4, 5, 6); if (ns83902PktBufRead (pDrvCtrl, frameAddress, len, (char *) &pRx->enetHdr) == ERROR) { DRV_LOG (NS83902_DEBUG_RX, "ns83902Recv: Could not read packet data\n", 1, 2, 3, 4, 5, 6); END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_ERRS, +1); netClFree (pDrvCtrl->endObj.pNetPool, (UCHAR *)pRx); } else { /* Successfully got a frame */ pRetCluster = (NS83902_CLUSTER) pRx; END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_UCAST, +1); } /* update boundary pointer */ if (pDrvCtrl->nextPage == pDrvCtrl->rxStartPage) bnry = pDrvCtrl->rxStopPage; else bnry = pDrvCtrl->nextPage - 1; NS83902_REG_SET (pDrvCtrl, NS83902_BNRY, bnry, CR_RPAGE0); DRV_LOG (NS83902_DEBUG_RX, "End Read!\n", 1, 2, 3, 4, 5, 6); return (pRetCluster); } /******************************************************************************* * * ns83902Recv - read a packet off the interface ring buffer * * ns83902Recv copies packets from local memory into an mbuf and hands it to * the next higher layer (IP or etherInputHook). * * RETURNS: TRUE, or FALSE if the packet reception encountered errors. */ LOCAL void ns83902Recv ( NS83902_END_DEVICE* pDrvCtrl, NS83902_CLUSTER pCluster ) { NS83902_RX_FRAME* pRx = (NS83902_RX_FRAME*) pCluster; CL_BLK_ID pClBlk; M_BLK_ID pMblk; UINT32 len = pRx->rxHdr.count; /* Process frame received */ if ((pClBlk = netClBlkGet (pDrvCtrl->endObj.pNetPool, M_DONTWAIT)) == NULL) { pDrvCtrl->lastError.errCode = END_ERR_NO_BUF; muxError (&pDrvCtrl->endObj, &pDrvCtrl->lastError); DRV_LOG (NS83902_DEBUG_RX, "Out of Cluster Blocks!\n", 1, 2, 3, 4, 5, 6); netClFree (pDrvCtrl->endObj.pNetPool, (UCHAR *)pCluster); END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_ERRS, +1); goto cleanRXD; } /* * OK we've got a spare, let's get an M_BLK_ID and marry it to the * one in the ring. */ if ((pMblk = mBlkGet (pDrvCtrl->endObj.pNetPool, M_DONTWAIT, MT_DATA)) == NULL) { netClBlkFree (pDrvCtrl->endObj.pNetPool, pClBlk); netClFree (pDrvCtrl->endObj.pNetPool, (UCHAR *)pCluster); DRV_LOG (NS83902_DEBUG_MB, "Out of M Blocks!\n", 1, 2, 3, 4, 5, 6); END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_ERRS, +1); goto cleanRXD; } END_ERR_ADD (&pDrvCtrl->endObj, MIB2_IN_UCAST, +1); /* Join the cluster to the MBlock */ netClBlkJoin (pClBlk, (char*)pCluster, len, NULL, 0, 0, 0); netMblkClJoin (pMblk, pClBlk); pMblk->mBlkHdr.mData = (char *)&pRx->enetHdr; pMblk->mBlkHdr.mFlags |= M_PKTHDR; /* set the packet header */ pMblk->mBlkHdr.mLen = len; /* set the data len */ pMblk->mBlkPktHdr.len = len; /* set the total len */ /* Call the upper layer's receive routine. */ END_RCV_RTN_CALL (&pDrvCtrl->endObj, pMblk); cleanRXD: return; } /******************************************************************************* * * ns83902Restart - restart chip after receive ring buffer overflow * * This routine is called from an isr handler that deals with a receive DMA * overflow condition. It gets access to the remote DMA, cleans up NIC * registers and defers work to task context to empty the receive ring buffers. * * RETURNS: N/A. */ LOCAL void ns83902Restart ( NS83902_END_DEVICE* pDrvCtrl, UINT8 cr ) { UINT8 isr; #ifdef NS83902_INSTRUMENT ns83902RestartNb++; #endif /* Buffer ring overflow algorithm */ NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_STP | CR_ABORT, CR_RPAGE0); /* wait at least 1.6 mSec */ SYS_MS_DELAY (2); /* Remove packets from buffer */ NS83902_REG_SET (pDrvCtrl, NS83902_RBCR0, 0, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RBCR1, 0, CR_RPAGE0); NS83902_REG_GET (pDrvCtrl, NS83902_ISR, isr, CR_RPAGE0); if ((cr & CR_TXP) && (!(isr & (ISR_TXE | ISR_PTX)))) pDrvCtrl->txResend = TRUE; NS83902_REG_SET (pDrvCtrl, NS83902_TCR, TCR_MODE1, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_ABORT | CR_STA, CR_RPAGE0); END_FLAGS_SET (&pDrvCtrl->endObj, IFF_UP | IFF_RUNNING | IFF_NOTRAILERS); pDrvCtrl->rxOvw = TRUE; if (pDrvCtrl->rxHandling == FALSE) { pDrvCtrl->rxHandling = TRUE; netJobAdd ((FUNCPTR) ns83902HandleInt, (int) pDrvCtrl, 0, 0, 0, 0); } } /******************************************************************************* * * ns83902HandleInt - deferred receive interrupt handler * * This function handles the received frames from the device. It runs in the * context of the netTask, which was triggered by a received packet interrupt. * It resends any packet that was in the midst of transmission when the overflow * hit. * Actual processing of the packet is done by calling ns83902Recv(). * * RETURNS: N/A. */ LOCAL void ns83902HandleInt ( NS83902_END_DEVICE* pDrvCtrl ) { NS83902_CLUSTER pCluster; /* empties the receive ring buffer of its packets */ pDrvCtrl->rxHandling = TRUE; while ((pCluster = ns83902ReadFrame (pDrvCtrl)) != NULL) ns83902Recv (pDrvCtrl, pCluster); pDrvCtrl->rxHandling = FALSE; if (pDrvCtrl->rxOvw == TRUE) { /* Reset overflow bit */ NS83902_REG_SET (pDrvCtrl, NS83902_ISR, ISR_OVW, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_TCR, TCR_MODE0, CR_RPAGE0); /* If resend needed, issue transmit command */ if (pDrvCtrl->txResend == TRUE) { NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_STA | CR_TXP | CR_ABORT, CR_RPAGE0); pDrvCtrl->txResend = FALSE; } pDrvCtrl->rxOvw = FALSE; } /* Re-enable interrupts */ NS83902_REG_SET (pDrvCtrl, NS83902_IMR, IMR_ENABLE, CR_RPAGE0); } /******************************************************************************* * * ns83902Int - The driver's interrupt handler * * This function clears the cause of the device interrupt(s) and then acts * on the individual possible causes. The primary goal of this routine is to * minimize the time spent in it. This is accomplished by deferring processing * to the netTask via the netJobAdd() function. * * Note that in case the receiver overruns, we promptly mark the interface as * "down" and leave error handling to task-level. This is in case netTask * is in the midst of DMA activity, we must allow it to complete. The receive * handler will give up when it discovers the interface is down, which will * then allow netTask to run our OVW handler. This provides orderly error * recovery. * * RETURNS: N/A. */ LOCAL void ns83902Int ( NS83902_END_DEVICE* pDrvCtrl ) { UINT8 isr; /* copy of NS83902_ISR */ UINT8 cr; /* copy of NS83902_CR */ #ifdef NS83902_INSTRUMENT ns83902IntNb++; #endif DRV_LOG (NS83902_DEBUG_INT, "Inside INT\n", 1, 2, 3, 4, 5, 6); /* Read and clear ISR */ NS83902_REG_GET (pDrvCtrl, NS83902_ISR, isr, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_ISR, isr, CR_RPAGE0); /* Check if interface is up and running */ if ((END_FLAGS_GET(&pDrvCtrl->endObj) & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) return; /* handle transmit interrupts */ if (isr & (ISR_PTX | ISR_TXE)) { UINT8 tsr; NS83902_REG_GET (pDrvCtrl, NS83902_TSR, tsr, CR_RPAGE0); DRV_LOG (NS83902_DEBUG_TX, "ns83902Tx Interrupt TSR=%d \n", tsr, 2, 3, 4, 5, 6); if ((tsr & TSR_PTX) == 0) { /* PTX zero, something went wrong */ if (tsr & TSR_ABT) { #ifdef NS83902_INSTRUMENT ns83902TxTimeout++; #endif DRV_LOG (NS83902_DEBUG_TX, "ns83902Transmit Aborted \n", 1, 2, 3, 4, 5, 6); } if (tsr & TSR_FU) { DRV_LOG (NS83902_DEBUG_TX, "ns83902Transmit FIFO Underrun \n", 1, 2, 3, 4, 5, 6); } /* Notify upper layers about the error */ END_ERR_ADD (&pDrvCtrl->endObj, MIB2_OUT_ERRS, +1); pDrvCtrl->lastError.errCode = END_ERR_WARN; pDrvCtrl->lastError.pMesg = "Transmit error"; netJobAdd ((FUNCPTR) muxError, (int) &pDrvCtrl->endObj, (int) &pDrvCtrl->lastError, 0, 0, 0); DRV_LOG (NS83902_DEBUG_TX, "ns83902Transmit Error\n", 0, 0, 0, 0, 0, 0); #ifdef NS83902_INSTRUMENT ns83902TxError++; #endif } if (tsr & TSR_CRS) { DRV_LOG (NS83902_DEBUG_TX, "%s%d - no carrier\n", (int)NS83902_DEV_NAME, pDrvCtrl->unit, 3, 4, 5, 6); } #if 0 /* no collision statistics for END drivers? */ if (tsr & TSR_COL) { UINT8 ncr; NS83902_REG_GET (pDrvCtrl, NS83902_NCR, ncr, CR_RPAGE0); END_ERR_ADD (pEndObj, MIB2_COLLISIONS, ncr); } #endif if (pDrvCtrl->txBlocked == TRUE) { pDrvCtrl->txBlocked = FALSE; netJobAdd ((FUNCPTR)muxTxRestart, (int)&pDrvCtrl->endObj, 0, 0, 0, 0); } } /* handle receiver overrun */ if (isr & ISR_OVW) { /* disable interrupts */ NS83902_REG_SET (pDrvCtrl, NS83902_IMR, IMR_DISABLE, CR_RPAGE0); NS83902_CR_GET (pDrvCtrl, cr); /* mark the interface down */ END_FLAGS_CLR (&pDrvCtrl->endObj, IFF_UP | IFF_RUNNING); ns83902Restart (pDrvCtrl, cr); return; } /* handle packet received */ if (isr & ISR_PRX) if (pDrvCtrl->rxHandling == FALSE) { pDrvCtrl->rxHandling = TRUE; /* disable receive interrupts and defer work to task context */ NS83902_REG_SET (pDrvCtrl, NS83902_IMR, IMR_RX_DISABLE, CR_RPAGE0); netJobAdd ((FUNCPTR) ns83902HandleInt, (int) pDrvCtrl, 0, 0, 0, 0); } } /******************************************************************************* * * ns83902Send - the driver's actual output routine * * This routine accepts outgoing packets from the snd queue, and then * gains exclusive access to the DMA (through a mutex semaphore), * then calls ns83902Transmit() to send the packet out onto the interface. * * RETURNS: OK, or ERROR if the packet could not be transmitted. */ LOCAL STATUS ns83902Send ( NS83902_END_DEVICE* pDrvCtrl, M_BLK* pMblk ) { int status; DRV_LOG (NS83902_DEBUG_TX, "Begin ns83902Send pDrvCtrl %p pMblk %p\n", (int)pDrvCtrl, (int)pMblk, 3, 4, 5, 6); if ((END_FLAGS_GET(&pDrvCtrl->endObj) & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) { DRV_LOG (NS83902_DEBUG_TX, "Device is NOT UP and RUNNING\n", 1, 2, 3, 4, 5, 6); return (ERROR); } /* send packet out over interface */ if ((status = ns83902Transmit (pDrvCtrl, pMblk)) == OK) { /* Success, free the Mblk chain */ if (!NS83902_IS_IN_POLL_MODE()) netMblkClChainFree (pMblk); END_ERR_ADD (&pDrvCtrl->endObj, MIB2_OUT_UCAST, +1); } else { DRV_LOG (NS83902_DEBUG_TX, "FAILED ns83902Transmit\n", 0, 0, 0, 0, 0, 0); /* update statistics */ END_ERR_ADD (&pDrvCtrl->endObj, MIB2_OUT_ERRS, +1); #ifdef NS83902_INSTRUMENT ns83902TxError++; #endif } DRV_LOG (NS83902_DEBUG_TX, "End ns83902Send \n", 0, 0, 0, 0, 0, 0); #ifdef NS83902_INSTRUMENT ns83902TxNb++; #endif return status; } /******************************************************************************* * * ns83902Transmit - send data over the NIC network interface * * This routine transfers data to the NIC device via the remote DMA, and * then signal for a transmission. * * RETURNS: OK, or ERROR if the transmitter signalled an error. */ LOCAL STATUS ns83902Transmit ( NS83902_END_DEVICE* pDrvCtrl, M_BLK* pMblk ) { int status = OK; int txLen = 0; UINT8 cr; LOCAL UINT8 txData[ETHERMTU + ENET_HDR_REAL_SIZ]; int cnt; USHORT dummyRead; /* Get exclusive access to DMA */ if (!NS83902_IS_IN_POLL_MODE()) NS83902_SEM_TAKE (pDrvCtrl, WAIT_FOREVER); txLen = netMblkToBufCopy (pMblk,(char *)txData, NULL); txLen = max (txLen, ETHERSMALL); /* Check if we are ready to transmit */ NS83902_CR_GET (pDrvCtrl, cr); if (cr & CR_TXP) { DRV_LOG (NS83902_DEBUG_TX, "ns83902Transmit: waiting for TX_IN_PROGRESS\n", 1, 2, 3, 4, 5, 6); /* Wait for end of TX */ cnt = 100; /* timeout 100 mSec */ while ((cr & CR_TXP) && (cnt-- > 0)) { SYS_MS_DELAY (1); NS83902_CR_GET (pDrvCtrl, cr); } } /* If still not ready, bail */ NS83902_CR_GET (pDrvCtrl, cr); if (cr & CR_TXP) { DRV_LOG (NS83902_DEBUG_TX, "Not Ready to transmit!\n", 1, 2, 3, 4, 5, 6); pDrvCtrl->txBlocked = TRUE; /* transmitter not ready */ if (!NS83902_IS_IN_POLL_MODE()) NS83902_SEM_GIVE (pDrvCtrl); return (END_ERR_BLOCK); } /* Dummy read with RBCR > 1 before (see DP83902 manual) */ NS83902_REG_SET (pDrvCtrl, NS83902_RBCR0, 2, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RBCR1, 0, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RSAR0, 0, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RSAR1, LSB(pDrvCtrl->txStartPage), CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_RREAD, CR_RPAGE0); if (pDrvCtrl->wide == TRUE) { SYS_IN_SHORT (pDrvCtrl, pDrvCtrl->ioPort, dummyRead); } else { SYS_IN_BYTE (pDrvCtrl, pDrvCtrl->ioPort, dummyRead); SYS_IN_BYTE (pDrvCtrl, pDrvCtrl->ioPort, dummyRead); } /* Set up remote DMA transfer */ NS83902_REG_SET (pDrvCtrl, NS83902_RSAR0, 0, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RSAR1, LSB(pDrvCtrl->txStartPage), CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RBCR0, LSB(txLen), CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_RBCR1, MSB(txLen), CR_RPAGE0); /* Start remote DMA transfer */ NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_RWRITE, CR_RPAGE0); /* write data to the DMA port */ ns83902WritePort (pDrvCtrl,(char *) txData, txLen); /* Set up local DMA */ NS83902_REG_SET (pDrvCtrl, NS83902_TPSR, pDrvCtrl->txStartPage, CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_TBCR0, LSB(txLen), CR_RPAGE0); NS83902_REG_SET (pDrvCtrl, NS83902_TBCR1, MSB(txLen), CR_RPAGE0); DRV_LOG (NS83902_DEBUG_TX, "Sending %d bytes from %p \n", txLen, (int)txData, 3, 4, 5, 6); /* Start transmission while preserving DMA command bits */ NS83902_CR_GET (pDrvCtrl, cr); NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_TXP | (cr & (CR_RWRITE | CR_RREAD | CR_ABORT)), CR_RPAGE0); /* Release Semaphore */ if (!NS83902_IS_IN_POLL_MODE()) { NS83902_SEM_GIVE (pDrvCtrl); } else { UINT8 tsr; UINT8 isr; FOREVER { NS83902_REG_GET (pDrvCtrl, NS83902_ISR, isr, CR_RPAGE0); if ((isr & (ISR_PTX | ISR_TXE)) != 0) break; } /* Check for errors */ NS83902_REG_GET (pDrvCtrl, NS83902_TSR, tsr, CR_RPAGE0); if ((tsr & TSR_PTX) == 0) { /* PTX zero, something went wrong */ status = ERROR; if (tsr & TSR_ABT) { #ifdef NS83902_INSTRUMENT ns83902TxTimeout++; #endif DRV_LOG (NS83902_DEBUG_TX, "ns83902Transmit Aborted \n", 1, 2, 3, 4, 5, 6); } if (tsr & TSR_FU) { DRV_LOG (NS83902_DEBUG_TX, "ns83902Transmit FIFO Underrun \n", 1, 2, 3, 4, 5, 6); } } if (tsr & TSR_CRS) { logMsg ("%s%d - no carrier\n", (int)NS83902_DEV_NAME, pDrvCtrl->unit, 3, 4, 5, 6); } #if 0 /* no collision statistics for END drivers? */ if (tsr & TSR_COL) { UINT8 ncr; NS83902_REG_GET (pDrvCtrl, NS83902_NCR, ncr, CR_RPAGE0); END_ERR_ADD (pEndObj, MIB2_COLLISIONS, ncr); } #endif /* Clear transmit flags in ISR */ NS83902_REG_SET (pDrvCtrl, NS83902_ISR, (ISR_PTX | ISR_TXE), CR_RPAGE0); } return (status); } /******************************************************************************* * * ns83902Ioctl - the driver's I/O control routine * * Perform device-specific commands. * * RETURNS: 0, or EINVAL if the command 'cmd' is not supported. */ LOCAL int ns83902Ioctl ( NS83902_END_DEVICE* pDrvCtrl, int cmd, caddr_t data ) { int error = 0; long value; int saveFlags; DRV_LOG (NS83902_DEBUG_LOAD, "ns83902Ioctl Command %d\n", cmd, 2, 3, 4, 5, 6); switch (cmd) { case EIOCSADDR: if (data == NULL) return (EINVAL); bcopy ((char *)data, (char *)END_HADDR(&pDrvCtrl->endObj), END_HADDR_LEN(&pDrvCtrl->endObj)); ns83902Config (pDrvCtrl); /* HELP Will it work? */ break; case EIOCGADDR: if (data == NULL) return (EINVAL); bcopy ((char *)END_HADDR(&pDrvCtrl->endObj), (char *)data, END_HADDR_LEN(&pDrvCtrl->endObj)); break; case EIOCSFLAGS: value = (long) data; if( value < 0 ) { value = -value; value--; END_FLAGS_CLR (&pDrvCtrl->endObj, value); } else { END_FLAGS_SET (&pDrvCtrl->endObj, value); } saveFlags = DRV_FLAGS_GET(); if (DRV_FLAGS_GET() != saveFlags && (END_FLAGS_GET(&pDrvCtrl->endObj) & IFF_UP)) { END_FLAGS_CLR (&pDrvCtrl->endObj, IFF_UP | IFF_RUNNING); ns83902Config( pDrvCtrl ); } break; case EIOCGFLAGS: if (data == NULL) error = EINVAL; else *(int *)data = END_FLAGS_GET(&pDrvCtrl->endObj); break; case EIOCMULTIADD: /* move to mux */ error = ns83902MCastAddrAdd (pDrvCtrl, (char *)data); break; case EIOCMULTIDEL: /* move to mux */ error = ns83902MCastAddrDel (pDrvCtrl, (char *)data); break; case EIOCMULTIGET: /* move to mux */ error = ns83902MCastAddrGet (pDrvCtrl, (MULTI_TABLE *)data); break; case EIOCPOLLSTART: /* move to mux */ error = ns83902PollStart (pDrvCtrl); break; case EIOCPOLLSTOP: /* move to mux */ error = ns83902PollStop (pDrvCtrl); break; case EIOCGMIB2: if (data == NULL) return (EINVAL); bcopy((char *)&pDrvCtrl->endObj.mib2Tbl, (char *)data, sizeof(pDrvCtrl->endObj.mib2Tbl)); break; default: error = EINVAL; } return (error); } /******************************************************************************* * * ns83902Stop - stop the device * * This function calls BSP functions to disconnect interrupts and stop * the device from operating in interrupt mode. * * RETURNS: OK or ERROR */ LOCAL STATUS ns83902Stop ( NS83902_END_DEVICE* pDrvCtrl ) { STATUS result = OK; END_FLAGS_CLR (&pDrvCtrl->endObj,IFF_UP | IFF_RUNNING ); /* Stop the device. */ NS83902_REG_SET (pDrvCtrl, NS83902_CR, CR_STP | CR_ABORT, CR_RPAGE0); /* disable all interrupts */ NS83902_REG_SET (pDrvCtrl, NS83902_IMR, IMR_DISABLE, CR_RPAGE0); /* Disable LAN interrupts */ SYS_INT_DISABLE (pDrvCtrl); /* disconnect interrupt */ SYS_INT_DISCONNECT (pDrvCtrl, ns83902Int, (int)pDrvCtrl, &result); return (result); } /****************************************************************************** * * ns83902Unload - unload a driver from the system * * This function first brings down the device, and then frees any * stuff that was allocated by the driver in the load function. */ LOCAL STATUS ns83902Unload ( NS83902_END_DEVICE* pDrvCtrl ) { if (pDrvCtrl != NULL) { END_OBJECT_UNLOAD (&pDrvCtrl->endObj); /* free resources */ if (pDrvCtrl->pCluster != NULL) free (pDrvCtrl->pCluster); } free ((char *) pDrvCtrl); return (OK); } /******************************************************************************* * * ns83902MCastAddrAdd - add a multicast address * * * RETURNS: OK on success, ERROR otherwise. */ LOCAL STATUS ns83902MCastAddrAdd ( NS83902_END_DEVICE* pDrvCtrl, char * pAddr ) { int retVal; DRV_LOG (NS83902_DEBUG_IOCTL, "MCastAddrAdd\n", 0, 0, 0, 0, 0, 0); retVal = etherMultiAdd (&pDrvCtrl->endObj.multiList, pAddr); if (retVal == ENETRESET) return ns83902AddrFilterSet (pDrvCtrl, pAddr, TRUE); return ((retVal == OK) ? OK : ERROR); } /******************************************************************************* * * ns83902MCastAddrDel - remove a multicast address * * * RETURNS: OK on success, ERROR otherwise. */ LOCAL STATUS ns83902MCastAddrDel ( NS83902_END_DEVICE* pDrvCtrl, char * pAddr ) { int retVal; DRV_LOG (NS83902_DEBUG_IOCTL, "MCastAddrDel\n", 0, 0, 0, 0, 0, 0); retVal = etherMultiDel (&pDrvCtrl->endObj.multiList, pAddr); if (retVal == ENETRESET) return ns83902AddrFilterSet (pDrvCtrl, pAddr, FALSE); return ((retVal == OK) ? OK : ERROR); } /******************************************************************************* * * ns83902MCastAddrGet - retrieve current multicast address list * * * RETURNS: OK on success; otherwise ERROR. */ LOCAL STATUS ns83902MCastAddrGet ( NS83902_END_DEVICE* pDrvCtrl, MULTI_TABLE * pTable ) { DRV_LOG (NS83902_DEBUG_IOCTL, "MCastAddrGet\n", 0, 0, 0, 0, 0, 0); return (etherMultiGet (&pDrvCtrl->endObj.multiList, pTable)); } /******************************************************************************* * * ns83902PollStart - starting polling mode * * RETURNS: OK, always. */ LOCAL STATUS ns83902PollStart ( NS83902_END_DEVICE* pDrvCtrl ) { int intLevel; DRV_LOG (NS83902_DEBUG_POLL, "S ", 0, 0, 0, 0, 0, 0); intLevel = intLock(); /* disable all interrupts */ NS83902_REG_SET (pDrvCtrl, NS83902_IMR, IMR_DISABLE, CR_RPAGE0); DRV_FLAGS_SET (NS83902_FLAG_POLL); intUnlock (intLevel); return (OK); } /******************************************************************************* * * ns83902PollStop - stop polling mode * * RETURNS: OK, always. */ LOCAL STATUS ns83902PollStop ( NS83902_END_DEVICE* pDrvCtrl ) { int intLevel; intLevel = intLock(); /* enable interrupts */ NS83902_REG_SET (pDrvCtrl, NS83902_IMR, IMR_ENABLE, CR_RPAGE0); DRV_FLAGS_CLR(NS83902_FLAG_POLL); DRV_LOG (NS83902_DEBUG_POLL, "s", 0, 0, 0, 0, 0, 0); intUnlock (intLevel); return (OK); } /******************************************************************************* * * ns83902PollSend - send a packet in polled mode * * RETURNS: OK on success, EAGAIN on failure */ LOCAL STATUS ns83902PollSend ( NS83902_END_DEVICE* pDrvCtrl, M_BLK* pMblk ) { return (ns83902Send (pDrvCtrl, pMblk)); } /******************************************************************************* * * ns83902PollReceive - get a packet in polled mode * * RETURNS: OK on success, EAGAIN on failure. */ LOCAL STATUS ns83902PollReceive ( NS83902_END_DEVICE* pDrvCtrl, M_BLK* pMblk ) { STATUS nRetValue = OK; NS83902_CLUSTER pCluster; int len; DRV_LOG (NS83902_DEBUG_POLL, "Start Poll Read!\n", 1, 2, 3, 4, 5, 6); if ((pCluster = ns83902ReadFrame (pDrvCtrl)) == NULL) nRetValue = EAGAIN; else { NS83902_RX_FRAME* pRx = (NS83902_RX_FRAME*)pCluster; len = pRx->rxHdr.count; pMblk->mBlkHdr.mFlags |= M_PKTHDR; /* set the packet header */ pMblk->mBlkHdr.mLen = len; /* set the data len */ pMblk->mBlkPktHdr.len = len; /* set the total len */ pMblk->mBlkHdr.mData += 2; bcopy ((char *)&pRx->enetHdr, (char *)pMblk->mBlkHdr.mData, len); netClFree (pDrvCtrl->endObj.pNetPool, (UCHAR *)pRx); } DRV_LOG (NS83902_DEBUG_POLL, "End Poll Read!\n", 1, 2, 3, 4, 5, 6); return nRetValue; } /******************************************************************************* * * ns83902HashIndex - compute the hash index for an ethernet address * * RETURNS: hash index for an ethernet address. */ LOCAL int ns83902HashIndex ( char * eAddr ) { UINT8 eAddrByte; int index; /* hash index - return value */ int byte; /* loop - counter */ int bit; /* loop - counter */ UINT crc = 0xffffffff; UINT8 msb; for (byte=0; byte<6; byte++) { eAddrByte = eAddr[byte]; for (bit=0; bit<8; bit++) { msb = crc >> 31; crc <<= 1; if (msb ^ (eAddrByte & 0x1)) { crc ^= NS83902_CRC_POLY; crc |= 0x1; } eAddrByte >>= 1; } } /* Just want the 6 most significant bits. */ index = crc >> 26 ; return index; } /****************************************************************************** * * ns83902AddrFilterSet - set the address filter for multicast addresses * * This routine goes through all of the multicast addresses on the list * of addresses (added with the MCastAddrAdd() routine) and sets the * device's filter correctly. * */ LOCAL STATUS ns83902AddrFilterSet ( NS83902_END_DEVICE* pDrvCtrl, char* pAddr, BOOL bSet ) { UINT8 nHashIndex; /* get hash index for the address */ nHashIndex = ns83902HashIndex (pAddr); /* Turn on the corresponding bit in the filter. */ ns83902MARSet (pDrvCtrl, nHashIndex, bSet); return OK; } /******************************************************************************* * * ns83902MARSet - sets/resets the MAR for the specified hash index * * This routine sets/resets the MAR bit for the specified hash index * * RETURNS: OK or ERROR. */ LOCAL void ns83902MARSet ( NS83902_END_DEVICE* pDrvCtrl, UINT8 index, /* hash index */ BOOL bSet /* Set/Reset */ ) { UINT8 nRegOffset = index; UINT8 nBitPosition = index; UINT8 nBitMask = (UINT8)0x01; UINT8 nValue; /* Find register and bit position */ nBitPosition = nBitPosition & 0x07; /* 3 LSB bits */ nBitMask <<= nBitPosition; nRegOffset >>= 3; /* next 3 bits */ /* set the bit in bit array*/ NS83902_REG_GET (pDrvCtrl, NS83902_MAR0 + nRegOffset, nValue, CR_RPAGE1); DRV_LOG (NS83902_DEBUG_LOAD, "Hash Index:%d MAR Offset:%d value:%d\n", index, nRegOffset, nValue, 4, 5, 6); if (bSet) { nValue |= nBitMask; /* set */ } else { nBitMask = ~nBitMask; /* reset */ nValue &= nBitMask; } NS83902_REG_SET (pDrvCtrl, NS83902_MAR0 + nRegOffset, nValue, CR_RPAGE1); DRV_LOG (NS83902_DEBUG_LOAD, "Hash Index:%d MAR Offset:%d value:%d\n", index, nRegOffset, nValue, 4, 5, 6); } /******************************************************************************* * * ns83902RegRead - read the current value of a NIC register * * This routine reads the register values of a NIC register * * RETURNS: register value. */ LOCAL UINT8 ns83902RegRead ( NS83902_END_DEVICE* pDrvCtrl, int reg, int page ) { UINT8 value; int oldLevel = intLock(); if ((pDrvCtrl)->regPage != (page)) { /* Set the register page */ UINT8 cr; (pDrvCtrl)->regPage = (page); NS83902_CR_GET ((pDrvCtrl), cr); NS83902_CR_SET ((pDrvCtrl), (cr & ~CR_PAGE_MASK) | (page)); } SYS_IN_BYTE (pDrvCtrl, pDrvCtrl->pNic + reg * pDrvCtrl->regInterval,value); SYS_NS83902_DELAY(); intUnlock (oldLevel); return (value); } /******************************************************************************* * * ns83902RegSet - set a NIC register * * This routine reads and displays the register values of the NIC registers * * RETURNS: N/A. */ LOCAL void ns83902RegSet ( NS83902_END_DEVICE* pDrvCtrl, int reg, int value, int page ) { int oldLevel = intLock(); if (pDrvCtrl->regPage != page) { /* Set the page */ UINT8 cr; pDrvCtrl->regPage = page; NS83902_CR_GET (pDrvCtrl, cr); NS83902_CR_SET (pDrvCtrl, ((cr & ~CR_PAGE_MASK) | page)); } SYS_OUT_BYTE (pDrvCtrl, pDrvCtrl->pNic + reg * pDrvCtrl->regInterval, value); SYS_NS83902_DELAY(); intUnlock (oldLevel); } #ifdef NS83902_DEBUG /******************************************************************************* * * ns83902RegShow - prints the current value of the NIC registers * * This routine reads and displays the register values of the NIC registers * * RETURNS: N/A. */ void ns83902RegShow ( NS83902_END_DEVICE* pDrvCtrl ) { UINT8 value; NS83902_CR_GET (pDrvCtrl, value); printf ("CR: 0x%x \n", value); NS83902_REG_GET (pDrvCtrl, NS83902_CLDA0, value, CR_RPAGE0); printf ("CLDA0: 0x%x \n", value); NS83902_REG_GET (pDrvCtrl, NS83902_CLDA1, value, CR_RPAGE0); printf ("CLDA1: 0x%x \n", value); NS83902_REG_GET (pDrvCtrl, NS83902_BNRY, value, CR_RPAGE0); printf ("BNRY: 0x%x \n", value); NS83902_REG_GET (pDrvCtrl, NS83902_TSR, value, CR_RPAGE0); printf ("TSR: 0x%x \n", value); NS83902_REG_GET (pDrvCtrl, NS83902_ISR, value, CR_RPAGE0); printf ("ISR: 0x%x \n", value); NS83902_REG_GET (pDrvCtrl, NS83902_CRDA0, value, CR_RPAGE0); printf ("CRDA0: 0x%x \n", value); NS83902_REG_GET (pDrvCtrl, NS83902_CRDA1, value, CR_RPAGE0); printf ("CRDA1: 0x%x \n", value); NS83902_REG_GET (pDrvCtrl, NS83902_RSR, value, CR_RPAGE0); printf ("RSR: 0x%x \n", value); } #endif /*NS83902_DEBUG*/