2473 lines
66 KiB
C
2473 lines
66 KiB
C
/* 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 <initString> parameter as input. This parameter passes in a
|
|
colon-delimited string of the format:
|
|
|
|
"<baseAdrs>:<intVec>:<intLvl>:<dmaPort>:<bufSize>:<options>"
|
|
|
|
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 <vxWorks.h>
|
|
#include <iv.h>
|
|
#include <vme.h>
|
|
#include <net/mbuf.h>
|
|
#include <lstLib.h>
|
|
#include <semLib.h>
|
|
#include <sys/times.h>
|
|
#include <sys/ioctl.h>
|
|
#include <errno.h>
|
|
#include <memLib.h>
|
|
#include <intLib.h>
|
|
#include <iosLib.h>
|
|
#include <errnoLib.h>
|
|
#include <logLib.h>
|
|
|
|
#include <cacheLib.h>
|
|
#include <netLib.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sysLib.h>
|
|
|
|
#include <etherLib.h>
|
|
#ifdef WR_IPV6
|
|
#include <adv_net.h>
|
|
#endif /*WR_IPV6*/
|
|
|
|
#include <drv/end/ns83902End.h>
|
|
|
|
#undef ETHER_MAP_IP_MULTICAST
|
|
#include <etherMultiLib.h>
|
|
#include <end.h>
|
|
#include <endLib.h>
|
|
|
|
/* 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 <initString>.
|
|
* 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 <initString> 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 <initString> 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*/
|
|
|
|
|
|
|