/* network.c: An 82596 ethernet driver for rtems-bsd.
*/
#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
#define KERNEL
/*
* Selectively define to debug the network driver. If you define any of these
* you must run with polled console I/O.
*/
/*
#define DBG_ADD_CMD
#define DBG_WAIT
#define DBG_SEND
#define DBG_MEM
#define DBG_SELFTEST_CMD
#define DBG_DUMP_CMD
#define DBG_RESET
#define DBG_ATTACH
#define DBG_START
#define DBG_INIT
#define DBG_STOP
#define DBG_RX
#define DBG_ISR
#define DBG_IOCTL
#define DBG_STAT
#define DBG_PACKETS
*/
#define IGNORE_SPURIOUS_IRQ
#define IGNORE_NO_RFA
#define IGNORE_MULTIPLE_RF
/*
* Default number of buffer descriptors and buffer sizes.
*/
#define RX_BUF_COUNT 15
#define TX_BUF_COUNT 4
#define TX_BD_PER_BUF 4
#define RBUF_SIZE 1520
#define UTI_596_ETH_MIN_SIZE 60
#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255")
/*
* RTEMS events
*/
#define INTERRUPT_EVENT RTEMS_EVENT_1
#define START_TRANSMIT_EVENT RTEMS_EVENT_2
#define NIC_RESET_EVENT RTEMS_EVENT_3
#include <bsp.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <rtems/error.h>
#include <rtems/bspIo.h>
#include <rtems/rtems_bsdnet.h>
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/types.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include "uti596.h"
/* If we are running interrupt driven I/O no debug output is printed */
#if CD2401_IO_MODE == 0
#define printk(arglist) do { printk arglist; printk("\r"); } while (0);
#else
#define printk(arglist)
#endif
#define UTI_596_ASSERT( condition, str ) if (!( condition ) ) { printk((str)) }
/* Types of PORT commands */
#define UTI596_RESET_PORT_FUNCTION 0
#define UTI596_SELFTEST_PORT_FUNCTION 1
#define UTI596_SCP_PORT_FUNCTION 2
#define UTI596_DUMP_PORT_FUNCTION 3
/* Types of waiting for commands */
#define UTI596_NO_WAIT 0
#define UTI596_WAIT_FOR_CU_ACCEPT 1
#define UTI596_WAIT_FOR_INITIALIZATION 2
#define UTI596_WAIT_FOR_STAT_C 3
/* Device dependent data structure */
static uti596_softc_ uti596_softc;
/* Globals */
int count_rx = 0;
static int scbStatus;
static rtems_status_code sc;
static i596_cmd *pIsrCmd;
static i596_rfd *pIsrRfd;
/*
* Initial 596 configuration
*/
char uti596initSetup[] = {
0x0E, /* Byte 0: length, prefetch off ( no RBD's ) */
0xC8, /* Byte 1: fifo to 8, monitor off */
0x40, /* Byte 2: don't save bad frames ( was save= 80, use intel's 40 )*/
0x2E, /* Byte 3: No source address insertion, 8 byte preamble */
0x00, /* Byte 4: priority and backoff defaults */
0x60, /* Byte 5: interframe spacing */
0x00, /* Byte 6: slot time LSB */
0xf2, /* Byte 7: slot time and retries */
0x0C, /* Byte 8: not promisc, enable bcast, tx no crs, crc inserted 32bit, 802.3 framing */
0x08, /* Byte 9: collision detect */
0x40, /* Byte 10: minimum frame length */
0xfb, /* Byte 11: tried C8 same as byte 1 in bits 6-7, else ignored*/
0x00, /* Byte 12: disable full duplex */
0x3f /* Byte 13: no multi IA, backoff enabled */
};
/* Local Routines */
static unsigned long word_swap ( unsigned long );
static void * malloc_16byte_aligned ( void **, void ** adjusted_pointer, size_t );
RTEMS_INLINE_ROUTINE void uti596_writePortFunction ( volatile void *, unsigned long );
/* currently unused by RTEMS */
#if 0
RTEMS_INLINE_ROUTINE void uti596_portReset( void );
static unsigned long uti596_portSelfTest( i596_selftest * );
static int uti596_portDump ( i596_dump_result * );
static void uti596_CU_dump ( i596_dump_result * );
#endif
static int uti596_wait ( uti596_softc_ *, uint8_t);
static int uti596_issueCA ( uti596_softc_ *, uint8_t);
static void uti596_addCmd ( i596_cmd * );
static void uti596_addPolledCmd ( i596_cmd * );
static int uti596_setScpAndScb ( uti596_softc_ * );
static int uti596_diagnose ( void );
static int uti596_configure ( uti596_softc_ * );
static int uti596_IAsetup ( uti596_softc_ * );
static int uti596_initTBD ( uti596_softc_ * );
static int uti596_initRFA ( int );
static void uti596_initMem ( uti596_softc_ * );
static void uti596_initialize ( uti596_softc_ * );
static void uti596_initialize_hardware ( uti596_softc_ * );
static void uti596_reset_hardware ( uti596_softc_ *);
static void uti596_reset ( void );
static void uti596_clearListStatus ( i596_rfd * );
static i596_rfd * uti596_dequeue ( i596_rfd ** );
static void uti596_append ( i596_rfd ** , i596_rfd * );
static void uti596_supplyFD ( i596_rfd * );
static void send_packet ( struct ifnet *, struct mbuf * );
/* Required RTEMS network driver functions and tasks (plus reset daemon) */
static void uti596_start ( struct ifnet * );
void uti596_init ( void * );
void uti596_stop ( uti596_softc_ * );
void uti596_txDaemon ( void * );
void uti596_rxDaemon ( void * );
void uti596_resetDaemon( void * );
rtems_isr uti596_DynamicInterruptHandler ( rtems_vector_number );
static int uti596_ioctl ( struct ifnet *, u_long, caddr_t );
void uti596_stats ( uti596_softc_ * );
#ifdef DBG_PACKETS
static void dumpQ( void );
static void show_buffers( void );
static void show_queues( void );
static void print_eth ( unsigned char * );
static void print_hdr ( unsigned char * );
static void print_pkt ( unsigned char * );
static void print_echo ( unsigned char * );
#endif
/*
* word_swap
*
* Return a 32 bit value, swapping the upper and lower words first.
*
* Input parameters:
* val - 32 bit value to swap
*
* Output parameters: NONE
*
* Return value:
* Input value with upper and lower 16-bit words swapped
*/
static unsigned long word_swap(
unsigned long val
)
{
return (((val >> 16)&(0x0000ffff)) | ((val << 16)&(0xffff0000)));
}
/*
* malloc_16byte_aligned
*
* Allocate a block of a least nbytes aligned on a 16-byte boundary.
* Clients are responsible to store both the real address and the adjusted
* address. The real address must be used to free the block.
*
* Input parameters:
* real_pointer - pointer to a void * pointer in which to store the starting
* address of the block. Required for free.
* adjusted_pointer - pointer to a void * pointer in which to store the
* starting address of the block rounded up to the next
* 16 byte boundary.
* nbytes - number of bytes of storage requested
*
* Output parameters:
* real_pointer - starting address of the block.
* adjusted_pointer - starting address of the block rounded up to the next
* 16 byte boundary.
*
* Return value:
* starting address of the block rounded up to the next 16 byte boundary.
* NULL if no storage was allocated.
*/
static void * malloc_16byte_aligned(
void ** real_pointer,
void ** adjusted_pointer,
size_t nbytes
)
{
*real_pointer = malloc( nbytes + 0xF, 0, M_NOWAIT );
*adjusted_pointer = (void *)(((unsigned long)*real_pointer + 0xF ) & 0xFFFFFFF0 );
return *adjusted_pointer;
}
/*
* uti596_scp_alloc
*
* Allocate a new scp, possibly freeing a previously allocated one.
*
* Input parameters:
* sc - pointer to the global uti596_softc in which to store pointers
* to the newly allocated block.
*
* Output parameters: NONE
*
* Return value:
* Pointer to the newly allocated, 16-byte aligned scp.
*/
static i596_scp * uti596_scp_alloc(
uti596_softc_ * sc
)
{
if( sc->base_scp != NULL ) {
#ifdef DBG_MEM
printk(("uti596_scp_alloc: Already have an SCP at %p\n", sc->base_scp))
#endif
return sc->pScp;
}
/* allocate enough memory for the Scp block to be aligned on 16 byte boundary */
malloc_16byte_aligned( (void *)&(sc->base_scp), (void *)&(sc->pScp), sizeof( i596_scp ) );
#ifdef DBG_MEM
printk(("uti596_scp_alloc: Scp base address is %p\n", sc->base_scp))
printk(("uti596_scp_alloc: Scp aligned address is : %p\n",sc->pScp))
#endif
return sc->pScp;
}
/*
* uti596_writePortFunction
*
* Write the command into the PORT.
*
* Input parameters:
* addr - 16-byte aligned address to write into the PORT.
* cmd - 4-bit cmd to write into the PORT
*
* Output parameters: NONE
*
* Return value: NONE
*
* The Motorola manual swapped the high and low registers.
*/
RTEMS_INLINE_ROUTINE void uti596_writePortFunction(
volatile void * addr,
unsigned long cmd
)
{
i82596->port_lower = (unsigned short)(((unsigned long)addr & 0xFFF0) | cmd);
i82596->port_upper = (unsigned short)(((unsigned long)addr >> 16 ) & 0xFFFF);
}
/*
* uti596_portReset
*
* Issue a port Reset to the uti596
*
* Input parameters: NONE
*
* Output parameters: NONE
*
* Return value: NONE
*/
RTEMS_INLINE_ROUTINE void uti596_portReset( void )
{
uti596_writePortFunction( NULL, UTI596_RESET_PORT_FUNCTION );
}
/* currently unused by RTEMS */
#if 0
/*
* uti596_portSelfTest
*
* Perform a self-test. Wait for up to 1 second for the test to
* complete. Normally, the test should complete in a very short time,
* so busy waiting is not an issue.
*
* Input parameters:
* stp - pointer to a 16-byte aligned uti596_selftest structure.
*
* Output parameters: NONE
*
* Return value:
* 32-bit result field if successful, -1 otherwise.
*/
static unsigned long uti596_portSelfTest(
i596_selftest * stp
)
{
rtems_interval ticks_per_second, start_ticks, end_ticks;
stp->results = 0xFFFFFFFF;
uti596_writePortFunction( stp, UTI596_SELFTEST_PORT_FUNCTION );
ticks_per_second = rtems_clock_get_ticks_per_second();
start_ticks = rtems_clock_get_ticks_since_boot();
end_ticks = start_ticks + ticks_per_second;
do {
if( stp->results != 0xFFFFFFFF )
break;
else
start_ticks = rtems_clock_get_ticks_since_boot();
} while (start_ticks <= end_ticks);
if (start_ticks > end_ticks ) {
#ifdef DBG_SELFTEST_CMD
printk(("uti596_selftest: Timed out\n" ))
#endif
return -1;
}
else {
#ifdef DBG_SELFTEST_CMD
printk(("uti596_selftest: Succeeded with signature = 0x%08x, result = 0x%08x\n",
stp->signature,
stp->results))
#endif
return stp->results;
}
}
#endif
/* currently unused by RTEMS */
#if 0
/*
* uti596_portDump
*
* Perform a dump Wait for up to 1 second for the test to
* complete. Normally, the test should complete in a very short time,
* so busy waiting is not an issue.
*
* Input parameters:
* dp - pointer to a 16-byte aligned uti596_dump structure.
*
* Output parameters: NONE
*
* Return value:
* 16-bit dump_status field if successful, -1 otherwise.
*/
static int uti596_portDump(
i596_dump_result * dp
)
{
rtems_interval ticks_per_second, start_ticks, end_ticks;
dp->dump_status = 0;
uti596_writePortFunction( dp, UTI596_DUMP_PORT_FUNCTION );
ticks_per_second = rtems_clock_get_ticks_per_second();
start_ticks = rtems_clock_get_ticks_since_boot();
end_ticks = start_ticks + ticks_per_second;
do {
if( dp->dump_status != 0xA006 )
break;
else
start_ticks = rtems_clock_get_ticks_since_boot();
} while (start_ticks <= end_ticks);
if (start_ticks > end_ticks ) {
#ifdef DBG_DUMP_CMD
printk(("uti596_dump: Timed out with dump at 0x%08x\n", (unsigned long)dp ))
#endif
return -1;
}
else {
#ifdef DBG_DUMP_CMD
printk(("uti596_dump: Succeeded with dump at = 0x%08x\n", (unsigned long)dp ))
#endif
return dp->dump_status;
}
}
#endif
/*
* uti596_wait
*
* Wait for a certain condition.
*
* Input parameters:
* sc - pointer to the uti596_softc struct
* wait_type - UTI596_NO_WAIT
* UTI596_WAIT
* UTI596_WAIT_FOR_CU_ACCEPT
* UTI596_WAIT_FOR_INITIALIZATION
* UTI596_WAIT_FOR_STAT_C
*
* Output parameters: NONE
*
* Return value:
* 0 if successful, -1 otherwise.
*/
static int uti596_wait(
uti596_softc_ *sc,
uint8_t waitType
)
{
rtems_interval ticks_per_second, start_ticks, end_ticks;
ticks_per_second = rtems_clock_get_ticks_per_second();
start_ticks = rtems_clock_get_ticks_since_boot();
end_ticks = start_ticks + ticks_per_second;
switch( waitType ) {
case UTI596_NO_WAIT:
return 0;
case UTI596_WAIT_FOR_CU_ACCEPT:
do {
if (sc->scb.command == 0)
break;
else
start_ticks = rtems_clock_get_ticks_since_boot();
} while (start_ticks <= end_ticks);
if( (sc->scb.command != 0) || (start_ticks > end_ticks) ) {
printf("i82596 timed out with status %x, cmd %x.\n",
sc->scb.status, sc->scb.command);
return -1;
}
else
return 0;
case UTI596_WAIT_FOR_INITIALIZATION:
do {
if( !sc->iscp.busy )
break;
else
start_ticks = rtems_clock_get_ticks_since_boot();
} while (start_ticks <= end_ticks);
if (start_ticks > end_ticks ) {
#ifdef DBG_WAIT
printk(("uti596_setScpAndScb: Timed out\n" ))
#endif
return -1;
}
else {
#ifdef DBG_WAIT
printk(("uti596_setScpAndScb: Succeeded\n" ))
#endif
return 0;
}
case UTI596_WAIT_FOR_STAT_C:
do {
if( *sc->pCurrent_command_status & STAT_C )
break;
else
start_ticks = rtems_clock_get_ticks_since_boot();
} while (start_ticks <= end_ticks);
if (start_ticks > end_ticks ) {
#ifdef DBG_WAIT
printk(("uti596_initMem: timed out - STAT_C not obtained\n" ))
#endif
return -1;
}
else {
#ifdef DBG_WAIT
printk(("uti596_initMem: STAT_C obtained OK\n" ))
#endif
return 0;
}
}
return -1;
}
/*
* uti596_issueCA
*
* Issue a Channel Attention command. Possibly wait for the
* command to start or complete.
*
* Input parameters:
* sc - pointer to the uti596_softc
* wait_type - UTI596_NO_WAIT
* UTI596_WAIT_BEGIN
* UTI596_WAIT_COMPLETION
*
* Output parameters: NONE
*
* Return value:
* 0 if successful, -1 otherwise.
*/
static int uti596_issueCA(
uti596_softc_ *sc,
uint8_t waitType
)
{
/* Issue Channel Attention */
i82596->chan_attn = 0x00000000;
return (uti596_wait ( sc, waitType ));
}
/*
* uti596_addCmd
*
* Add a uti596_cmd onto the end of the CBL command chain,
* or to the start if the chain is empty.
*
* Input parameters:
* pCmd - a pointer to the command to be added.
*
* Output parameters: NONE
*
* Return value: NONE
*/
static void uti596_addCmd(
i596_cmd *pCmd
)
{
ISR_Level level;
#ifdef DBG_ADD_CMD
printk(("uti596_addCmd: Adding command 0x%x\n", pCmd -> command ))
#endif
/* Mark command as last in list, to return an interrupt */
pCmd->command |= (CMD_EOL | CMD_INTR );
pCmd->status = 0;
pCmd->next = I596_NULL;
_ISR_Local_disable(level);
if (uti596_softc.pCmdHead == I596_NULL) {
uti596_softc.pCmdHead = uti596_softc.pCmdTail = uti596_softc.scb.pCmd = pCmd;
uti596_softc.scb.cmd_pointer = word_swap ((unsigned long)pCmd);
uti596_wait ( &uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT );
uti596_softc.scb.command = CUC_START;
uti596_issueCA ( &uti596_softc, UTI596_NO_WAIT );
_ISR_Local_enable(level);
}
else {
uti596_softc.pCmdTail->next = (i596_cmd *) word_swap ((unsigned long)pCmd);
uti596_softc.pCmdTail = pCmd;
_ISR_Local_enable(level);
}
#ifdef DBG_ADD_CMD
printk(("uti596_addCmd: Scb status & command 0x%x 0x%x\n",
uti596_softc.scb.status,
uti596_softc.scb.command ))
#endif
}
/*
* uti596_addPolledCmd
*
* Add a single uti596_cmd to the end of the command block list
* for processing, send a CU_START and wait for its acceptance
*
* Input parameters:
* sc - a pointer to the uti596_softc struct
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_addPolledCmd(
i596_cmd *pCmd
)
{
#ifdef DBG_ADD_CMD
printk(("uti596_addPolledCmd: Adding command 0x%x\n", pCmd -> command ))
#endif
pCmd->status = 0;
pCmd->command |= CMD_EOL ; /* only command in list*/
pCmd->next = I596_NULL;
uti596_wait ( &uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT );
uti596_softc.pCmdHead = uti596_softc.pCmdTail = uti596_softc.scb.pCmd = pCmd;
uti596_softc.scb.cmd_pointer = word_swap((unsigned long)pCmd);
uti596_softc.scb.command = CUC_START;
uti596_issueCA ( &uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT );
uti596_softc.pCmdHead = uti596_softc.pCmdTail = uti596_softc.scb.pCmd = I596_NULL;
uti596_softc.scb.cmd_pointer = (unsigned long) I596_NULL;
#ifdef DBG_ADD_CMD
printk(("uti596_addPolledCmd: Scb status & command 0x%x 0x%x\n",
uti596_softc.scb.status,
uti596_softc.scb.command ))
#endif
}
/* currently unused by RTEMS */
#if 0
/*
* uti596_CU_dump
*
* Dump the LANC 82596 registers
* The outcome is the same as the portDump() but executed
* via the CU instead of via a PORT access.
*
* Input parameters:
* drp - a pointer to a i596_dump_result structure.
*
* Output parameters: NONE
*
* Return value: NONE
*/
static void uti596_CU_dump ( i596_dump_result * drp)
{
i596_dump dumpCmd;
dumpCmd.cmd.command = CmdDump;
dumpCmd.cmd.next = I596_NULL;
dumpCmd.pData = (char *) drp;
uti596_softc.cmdOk = 0;
uti596_addCmd ( (i596_cmd *) &dumpCmd );
}
#endif
#if defined(DBG_STAT) || !defined(IGNORE_NO_RFA)
/*
* uti596_dump_scb
*
* Dump the system control block
* This function expands to nothing when using interrupt driven I/O
*
* Input parameters: NONE
*
* Output parameters: NONE
*
* Return value: NONE
*/
static void uti596_dump_scb ( void )
{
printk(("status 0x%x\n",uti596_softc.scb.status))
printk(("command 0x%x\n",uti596_softc.scb.command))
printk(("cmd 0x%x\n",(int)uti596_softc.scb.pCmd))
printk(("rfd 0x%x\n",(int)uti596_softc.scb.pRfd))
printk(("crc_err 0x%" PRIx32 "\n",uti596_softc.scb.crc_err))
printk(("align_err 0x%" PRIx32 "\n",uti596_softc.scb.align_err))
printk(("resource_err 0x%" PRIx32 "\n",uti596_softc.scb.resource_err ))
printk(("over_err 0x%" PRIx32 "\n",uti596_softc.scb.over_err))
printk(("rcvdt_err 0x%" PRIx32 "\n",uti596_softc.scb.rcvdt_err))
printk(("short_err 0x%" PRIx32 "\n",uti596_softc.scb.short_err))
printk(("t_on 0x%x\n",uti596_softc.scb.t_on))
printk(("t_off 0x%x\n",uti596_softc.scb.t_off))
}
#endif
/*
* uti596_setScpAndScb
*
* Issue the first channel attention after reset and wait for the busy
* field to clear in the iscp.
*
* Input parameters:
* sc - pointer to the global uti596_softc
*
* Output parameters: NONE
*
* Return value:
* 0 if successful, -1 otherwise.
*/
static int uti596_setScpAndScb(
uti596_softc_ * sc
)
{
/* set the busy flag in the iscp */
sc->iscp.busy = 1;
/* the command block list (CBL) is empty */
sc->scb.command = 0;
sc->scb.cmd_pointer = (unsigned long) I596_NULL; /* all 1's */
sc->pCmdHead = sc->scb.pCmd = I596_NULL; /* all 1's */
uti596_writePortFunction( sc->pScp, UTI596_SCP_PORT_FUNCTION );
/* Issue CA: pass the scb address to the 596 */
return ( uti596_issueCA ( sc, UTI596_WAIT_FOR_INITIALIZATION ) );
}
/*
* uti596_diagnose
*
* Send a diagnose command to the CU
*
* Input parameters: NONE
*
* Output parameters: NONE
*
* Return value:
* 0 if successful, -1 otherwise
*/
static int uti596_diagnose( void )
{
i596_cmd diagnose;
diagnose.command = CmdDiagnose;
diagnose.status = 0;
uti596_softc.pCurrent_command_status = (unsigned short *)&diagnose.status;
uti596_addPolledCmd(&diagnose);
return (uti596_wait ( &uti596_softc, UTI596_WAIT_FOR_STAT_C ));
#ifdef DBG_INIT
printk(("Status diagnostic: 0xa000 is a success ... 0x%2.2x\n", diagnose.status))
#endif
}
/*
* uti596_configure
*
* Send the CU a configure command with the desired
* configuration structure
*
* Input parameters:
* sc - a pointer to the uti596_softc struct
*
* Output parameters: NONE
*
* Return value:
* 0 if successful, -1 otherwise
*/
static int uti596_configure (
uti596_softc_ * sc
)
{
sc->set_conf.cmd.command = CmdConfigure;
memcpy ( (void *)sc->set_conf.data, uti596initSetup, 14);
uti596_addPolledCmd( (i596_cmd *) &sc->set_conf);
/* Poll for successful command completion */
sc->pCurrent_command_status = (unsigned short *)&(sc->set_conf.cmd.status);
return ( uti596_wait ( sc, UTI596_WAIT_FOR_STAT_C ) );
}
/*
* uti596_IAsetup
*
* Send the CU an Individual Address setup command with
* the ethernet hardware address
*
* Input parameters:
* sc - a pointer to the uti596_softc struct
*
* Output parameters: NONE
*
* Return value:
* 0 if successful, -1 otherwise
*/
static int uti596_IAsetup (
uti596_softc_ * sc
)
{
int i;
sc->set_add.cmd.command = CmdSASetup;
for ( i=0; i<6; i++) {
sc->set_add.data[i]=sc->arpcom.ac_enaddr[i];
}
sc->cmdOk = 0;
uti596_addPolledCmd((i596_cmd *)&sc->set_add);
/* Poll for successful command completion */
sc->pCurrent_command_status = (unsigned short *)&(sc->set_add.cmd.status);
return ( uti596_wait ( sc, UTI596_WAIT_FOR_STAT_C ) );
}
/*
* uti596_initTBD
*
* Initialize transmit buffer descriptors
* dynamically allocate mem for the number of tbd's required
*
* Input parameters:
* sc - a pointer to the uti596_softc struct
*
* Output parameters: NONE
*
* Return value:
* 0 if successful, -1 otherwise
*/
static int uti596_initTBD ( uti596_softc_ * sc )
{
int i;
i596_tbd *pTbd, *pPrev;
/* Set up a transmit command with a tbd ready */
sc->pLastUnkRFD = I596_NULL;
sc->pTxCmd = (i596_tx *) calloc (1,sizeof (struct i596_tx) );
sc->pTbd = (i596_tbd *) calloc (1,sizeof (struct i596_tbd) );
if ((sc->pTxCmd == NULL) || (sc->pTbd == NULL)) {
return -1;
}
sc->pTxCmd->pTbd = (i596_tbd *) word_swap ((unsigned long) sc->pTbd);
sc->pTxCmd->cmd.command = CMD_FLEX|CmdTx;
sc->pTxCmd->pad = 0;
sc->pTxCmd->count = 0; /* all bytes are in list of TBD's */
pPrev = pTbd = sc->pTbd;
/* Allocate a linked list of tbd's each with it's 'next' field written
* with upper and lower words swapped (for big endian), and mark the end.
*/
for ( i=0; i<sc->txBdCount; i++) {
if ( (pTbd = (i596_tbd *) calloc (1,sizeof (struct i596_tbd) )) == NULL ) {
return -1;
}
pPrev->next = (i596_tbd *) word_swap ((unsigned long) pTbd);
pPrev = pTbd;
}
pTbd->next = I596_NULL;
return 0;
}
/*
* uti596_initRFA
*
* Initialize the Receive Frame Area
* dynamically allocate mem for the number of rfd's required
*
* Input parameters:
* sc - a pointer to the uti596_softc struct
*
* Output parameters: NONE
*
* Return value:
* # of buffer descriptors successfully allocated
*/
static int uti596_initRFA( int num )
{
i596_rfd *pRfd;
int i = 0;
#ifdef DBG_INIT
printk(("uti596_initRFA: begins\n Requested frame descriptors ... %d.\n", num))
#endif
/*
* Create the first RFD in the RFA
*/
pRfd = (i596_rfd *) calloc (1, sizeof (struct i596_rfd));
if ( !pRfd ) {
printk(("Can't allocate first buffer.\n"))
return 0;
}
else {
uti596_softc.countRFD = 1;
uti596_softc.pBeginRFA = uti596_softc.pEndRFA = pRfd;
}
/* Create remaining RFDs */
for (i = 1; i < num; i++) {
pRfd = (i596_rfd *) calloc (1, sizeof (struct i596_rfd) );
if ( pRfd != NULL ) {
uti596_softc.countRFD++; /* update count */
uti596_softc.pEndRFA->next =
(i596_rfd *) word_swap ((unsigned long) pRfd); /* write the link */
uti596_softc.pEndRFA = pRfd; /* move the end */
}
else {
printk(("Can't allocate all buffers: only %d allocated\n", i))
break;
}
} /* end for */
uti596_softc.pEndRFA->next = I596_NULL;
UTI_596_ASSERT(uti596_softc.countRFD == num,"INIT:WRONG RFD COUNT\n" )
#ifdef DBG_INIT
printk (("uti596_initRFA: Head of RFA is buffer %p \n\
uti596_initRFA: End of RFA is buffer %p \n",
uti596_softc.pBeginRFA, uti596_softc.pEndRFA ))
#endif
/* Walk and initialize the RFD's */
for ( pRfd = uti596_softc.pBeginRFA;
pRfd != I596_NULL;
pRfd = (i596_rfd *) word_swap ((unsigned long)pRfd->next) )
{
pRfd->cmd = 0x0000;
pRfd->stat = 0x0000;
pRfd->pRbd = I596_NULL;
pRfd->count = 0; /* number of bytes in buffer: usually less than size */
pRfd->size = 1532; /* was 1532; buffer size ( All RBD ) */
} /* end for */
/* mark the last RFD as the last one in the RDL */
uti596_softc.pEndRFA -> cmd = CMD_EOL;
uti596_softc.pSavedRfdQueue =
uti596_softc.pEndSavedQueue = I596_NULL; /* initially empty */
uti596_softc.savedCount = 0;
uti596_softc.nop.cmd.command = CmdNOp; /* initialize the nop command */
return (i); /* the number of allocated buffers */
}
/*
* uti596_initMem
*
* Initialize the 82596 memory structures for Tx and Rx
* dynamically allocate mem for the number of tbd's required
*
* Input parameters:
* sc - a pointer to the uti596_softc struct
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_initMem(
uti596_softc_ * sc
)
{
int i;
#ifdef DBG_INIT
printk(("uti596_initMem: begins\n"))
#endif
sc->resetDone = 0;
/*
* Set up receive frame area (RFA)
*/
i = uti596_initRFA( sc->rxBdCount );
if ( i < sc->rxBdCount ) {
printk(("init_rfd: only able to allocate %d receive frame descriptors\n", i))
}
/*
* Write the SCB with a pointer to the receive frame area
* and keep a pointer for our use.
*/
sc->scb.rfd_pointer = word_swap((unsigned long)sc->pBeginRFA);
sc->scb.pRfd = sc->pBeginRFA;
/*
* Diagnose the health of the board
*/
uti596_diagnose();
/*
* Configure the 82596
*/
uti596_configure( sc );
/*
* Set up the Individual (hardware) Address
*/
uti596_IAsetup ( sc );
/*
* Initialize the transmit buffer descriptors
*/
uti596_initTBD( sc );
/* Padding used to fill short tx frames */
memset ( RTEMS_DEVOLATILE( char *, sc->zeroes ), 0, 64);
/* now need ISR */
sc->resetDone = 1;
}
/*
* uti596_initialize
*
* Reset the 82596 and initialize it with a new SCP.
*
* Input parameters:
* sc - pointer to the uti596_softc
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_initialize(
uti596_softc_ *sc
)
{
/* Reset the device. Stops it from doing whatever it might be doing. */
uti596_portReset();
/* Get a new System Configuration Pointer */
uti596_scp_alloc( sc );
/* write the SYSBUS: interrupt pin active high, LOCK disabled,
* internal triggering, linear mode
*/
sc->pScp->sysbus = 0x44;
/* provide the iscp to the scp, keep a pointer for our use */
sc->pScp->iscp_pointer = word_swap((unsigned long)&sc->iscp);
sc->pScp->iscp = &sc->iscp;
/* provide the scb to the iscp, keep a pointer for our use */
sc->iscp.scb_pointer = word_swap((unsigned long)&sc->scb);
sc->iscp.scb = &sc->scb;
#ifdef DBG_INIT
printk(("uti596_initialize: Starting i82596.\n"))
#endif
/* Set up the 82596 */
uti596_setScpAndScb( sc );
/* clear the scb command word */
sc->scb.command = 0;
}
/*
* uti596_initialize_hardware
*
* Reset the 82596 and initialize it with a new SCP. Enable bus snooping.
* Install the interrupt handlers.
*
* Input parameters:
* sc - pointer to the uti596_softc
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_initialize_hardware(
uti596_softc_ *sc
)
{
printk(("uti596_initialize_hardware: begins\n"))
/* Get the PCCChip2 to assert bus snooping signals on behalf of the i82596 */
pccchip2->LANC_berr_ctl = 0x40;
uti596_initialize( sc );
/*
* Configure interrupt control in PCCchip2
*/
pccchip2->LANC_error = 0xff; /* clear status register */
pccchip2->LANC_int_ctl = 0x5d; /* lvl 5, enabled, edge-sensitive rising */
pccchip2->LANC_berr_ctl = 0x5d; /* bus error: lvl 5, enabled, snoop control
* will supply dirty data and leave dirty data
* on read access and sink any data on write
*/
/*
* Install the interrupt handler
* calls rtems_interrupt_catch
*/
set_vector( uti596_DynamicInterruptHandler, 0x57, 1 );
/* Initialize the 82596 memory */
uti596_initMem(sc);
#ifdef DBG_INIT
printk(("uti596_initialize_hardware: After attach, status of board = 0x%x\n", sc->scb.status ))
#endif
}
/*
* uti596_reset_hardware
*
* Reset the 82596 and initialize it with an SCP.
*
* Input parameters:
* sc - pointer to the uti596_softc
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_reset_hardware(
uti596_softc_ *sc
)
{
rtems_status_code status_code;
i596_cmd *pCmd;
pCmd = sc->pCmdHead; /* This is a tx command for sure (99.99999%) */
/* the command block list (CBL) is empty */
sc->scb.cmd_pointer = (unsigned long) I596_NULL; /* all 1's */
sc->pCmdHead = sc->scb.pCmd = I596_NULL; /* all 1's */
#ifdef DBG_RESET
printk(("uti596_reset_hardware\n"))
#endif
uti596_initialize( sc );
/*
* Wake the transmitter if needed.
*/
if ( sc->txDaemonTid && pCmd != I596_NULL ) {
printk(("****RESET: wakes transmitter!\n"))
status_code = rtems_bsdnet_event_send (sc->txDaemonTid,
INTERRUPT_EVENT);
if ( status_code != RTEMS_SUCCESSFUL ) {
printk(("****ERROR:Could NOT send event to tid 0x%" PRIx32 " : %s\n",
sc->txDaemonTid, rtems_status_text (status_code) ))
}
}
#ifdef DBG_RESET
printk(("uti596_reset_hardware: After reset_hardware, status of board = 0x%x\n", sc->scb.status ))
#endif
}
/*
* uti596_clearListStatus
*
* Clear the stat fields for all RFDs
*
* Input parameters:
* pRfd - a pointer to the head of the RFA
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_clearListStatus(
i596_rfd *pRfd
)
{
while ( pRfd != I596_NULL ) {
pRfd -> stat = 0;
pRfd = (i596_rfd *) word_swap((unsigned long)pRfd-> next);
}
}
/*
* uti596_reset
*
* Reset the 82596 and reconfigure
*
* Input parameters: NONE
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_reset( void )
{
uti596_softc_ *sc = &uti596_softc;
#ifdef DBG_RESET
printk(("uti596_reset: begin\n"))
#endif
/* Wait for the CU to be available, then
* reset the ethernet hardware. Must re-config.
*/
sc->resetDone = 0;
uti596_wait ( sc, UTI596_WAIT_FOR_CU_ACCEPT );
uti596_reset_hardware ( &uti596_softc );
#ifdef DBG_RESET
uti596_diagnose();
#endif
/*
* Configure the 82596
*/
uti596_configure( sc );
/*
* Set up the Individual (hardware) Address
*/
uti596_IAsetup ( sc );
sc->pCmdHead = sc->pCmdTail = sc->scb.pCmd = I596_NULL;
/* restore the RFA */
if ( sc->pLastUnkRFD != I596_NULL ) {
sc-> pEndRFA = sc->pLastUnkRFD; /* The end position can be updated */
sc-> pLastUnkRFD = I596_NULL;
}
sc->pEndRFA->next = (i596_rfd*)word_swap((uint32_t)sc->pSavedRfdQueue);
if ( sc->pSavedRfdQueue != I596_NULL ) {
sc->pEndRFA = sc->pEndSavedQueue;
sc->pSavedRfdQueue = sc->pEndSavedQueue = I596_NULL;
sc -> countRFD = sc->rxBdCount ;
}
/* Re-address the head of the RFA in the SCB */
sc->scb.pRfd = sc->pBeginRFA;
sc->scb.rfd_pointer = word_swap((unsigned long)sc->pBeginRFA);
/* Clear the status of all RFDs */
uti596_clearListStatus( sc->pBeginRFA );
printk(("uti596_reset: Starting NIC\n"))
/* Start the receiver */
sc->scb.command = RX_START;
sc->started = 1; /* assume that the start is accepted */
sc->resetDone = 1;
uti596_issueCA ( sc, UTI596_WAIT_FOR_CU_ACCEPT );
UTI_596_ASSERT(sc->pCmdHead == I596_NULL, "Reset: CMD not cleared\n")
#ifdef DBG_RESET
printk(("uti596_reset: completed\n"))
#endif
}
/*
* uti596_dequeue
*
* Remove an RFD from the received fram queue
*
* Input parameters:
* ppQ - a pointer to a i596_rfd pointer
*
* Output parameters: NONE
*
* Return value:
* pRfd - a pointer to the dequeued RFD
*/
i596_rfd * uti596_dequeue(
i596_rfd ** ppQ
)
{
ISR_Level level;
i596_rfd * pRfd;
_ISR_Local_disable(level);
/* invalid address, or empty queue or emptied queue */
if( ppQ == NULL || *ppQ == NULL || *ppQ == I596_NULL) {
_ISR_Local_enable(level);
return I596_NULL;
}
/*
* Point to the dequeued buffer, then
* adjust the queue pointer and detach the buffer
*/
pRfd = *ppQ;
*ppQ = (i596_rfd *) word_swap ((unsigned long) pRfd->next);
pRfd->next = I596_NULL; /* unlink the rfd being returned */
_ISR_Local_enable(level);
return pRfd;
}
/*
* uti596_append
*
* Remove an RFD buffer from the RFA and tack it on to
* the received frame queue for processing.
*
* Input parameters:
* ppQ - a pointer to the queue pointer
* pRfd - a pointer to the buffer to be returned
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_append(
i596_rfd ** ppQ,
i596_rfd * pRfd
)
{
i596_rfd *p;
if ( pRfd != NULL && pRfd != I596_NULL) {
pRfd -> next = I596_NULL;
pRfd -> cmd |= CMD_EOL; /* set EL bit */
if ( *ppQ == NULL || *ppQ == I596_NULL ) {
/* empty list */
*ppQ = pRfd;
}
else {
/* walk to the end of the list */
for ( p=*ppQ;
p->next != I596_NULL;
p=(i596_rfd *) word_swap ((unsigned long)p->next) );
/* append the rfd */
p->cmd &= ~CMD_EOL; /* Clear EL bit at end */
p->next = (i596_rfd *) word_swap ((unsigned long)pRfd);
}
}
else {
printk(("Illegal attempt to append: %p\n", pRfd))
}
}
/*
* uti596_supplyFD
*
* Return a buffer (RFD) to the receive frame area (RFA).
* Call with interrupts disabled.
*
* Input parameters:
* pRfd - a pointer to the buffer to be returned
*
* Output parameters: NONE
*
* Return value: NONE
*/
void uti596_supplyFD (
i596_rfd * pRfd
)
{
i596_rfd *pLastRfd;
UTI_596_ASSERT(pRfd != I596_NULL, "Supplying NULL RFD!\n")
pRfd -> cmd = CMD_EOL;
pRfd -> pRbd = I596_NULL;
pRfd -> next = I596_NULL;
pRfd -> stat = 0x0000; /* clear STAT_C and STAT_B bits */
/*
* Check if the list is empty:
*/
if ( uti596_softc.pBeginRFA == I596_NULL ) {
/* Start a list with one entry */
uti596_softc.pBeginRFA = uti596_softc.pEndRFA = pRfd;
UTI_596_ASSERT(uti596_softc.countRFD == 0, "Null begin, but non-zero count\n")
if ( uti596_softc.pLastUnkRFD != I596_NULL ) {
printk(("LastUnkRFD is NOT NULL!!\n"))
}
uti596_softc.countRFD = 1;
return;
}
/*
* Check if the last RFD is used/read by the 596.
*/
pLastRfd = uti596_softc.pEndRFA;
/* C = complete, B = busy (prefetched) */
if ( pLastRfd != I596_NULL && ! (pLastRfd -> stat & ( STAT_C | STAT_B ) )) {
/*
* Not yet too late to add it
*/
pLastRfd -> next = (i596_rfd *) word_swap ((unsigned long)pRfd);
pLastRfd -> cmd &= ~CMD_EOL; /* RESET_EL : reset EL bit to 0 */
uti596_softc.countRFD++; /* Lets assume we add it successfully
If not, the RFD may be used, and may
decrement countRFD < 0 !! */
/*
* Check if the last RFD was used while appending.
*/
if ( pLastRfd -> stat & ( STAT_C | STAT_B ) ) { /* completed or was prefetched */
/*
* Either the EL bit of the last rfd has been read by the 82596,
* and it will stop after reception,( true when RESET_EL not reached ) or
* the EL bit was NOT read by the 82596 and it will use the linked
* RFD for the next reception. ( true when RESET_EL was reached )
* So, it is unknown whether or not the linked rfd will be used.
* Therefore, the end of list CANNOT be updated.
*/
UTI_596_ASSERT ( uti596_softc.pLastUnkRFD == I596_NULL, "Too many Unk RFD's\n" )
uti596_softc.pLastUnkRFD = pRfd;
return;
}
else {
/*
* The RFD being added was not touched by the 82596
*/
if (uti596_softc.pLastUnkRFD != I596_NULL ) {
uti596_append((i596_rfd **)&uti596_softc.pSavedRfdQueue, pRfd); /* Only here! saved Q */
uti596_softc.pEndSavedQueue = pRfd;
uti596_softc.savedCount++;
uti596_softc.countRFD--;
}
else {
uti596_softc.pEndRFA = pRfd; /* the RFA has been extended */
if ( ( uti596_softc.scb.status & SCB_STAT_RNR ||
uti596_softc.scb.status & RU_NO_RESOURCES ) &&
uti596_softc.countRFD > 1 ) {
/* Ensure that beginRFA is not EOL */
uti596_softc.pBeginRFA -> cmd &= ~CMD_EOL;
UTI_596_ASSERT(uti596_softc.pEndRFA -> next == I596_NULL, "supply: List buggered\n")
UTI_596_ASSERT(uti596_softc.pEndRFA -> cmd & CMD_EOL, "supply: No EOL at end.\n")
UTI_596_ASSERT(uti596_softc.scb.command == 0, "Supply: scb command must be zero\n")
#ifdef DBG_MEM
printk(("uti596_supplyFD: starting receiver"))
#endif
/* start the receiver */
UTI_596_ASSERT(uti596_softc.pBeginRFA != I596_NULL, "rx start w/ NULL begin! \n")
uti596_softc.scb.pRfd = uti596_softc.pBeginRFA;
uti596_softc.scb.rfd_pointer = word_swap ((unsigned long) uti596_softc.pBeginRFA);
/* Don't ack RNR! The receiver should be stopped in this case */
uti596_softc.scb.command = RX_START | SCB_STAT_RNR;
UTI_596_ASSERT( !(uti596_softc.scb.status & SCB_STAT_FR),"FRAME RECEIVED INT COMING!\n")
/* send CA signal */
uti596_issueCA ( &uti596_softc, UTI596_NO_WAIT );
}
}
return;
}
}
else {
/*
* too late , pLastRfd in use ( or NULL ),
* in either case, EL bit has been read, and RNR condition will occur
*/
uti596_append( (i596_rfd **)&uti596_softc.pSavedRfdQueue, pRfd); /* save it for RNR */
uti596_softc.pEndSavedQueue = pRfd; /* reset end of saved queue */
uti596_softc.savedCount++;
return;
}
}
/*
* send_packet
*
* Send a raw ethernet packet, add a
* transmit command to the CBL
*
* Input parameters:
* ifp - a pointer to the ifnet structure
* m - a pointer to the mbuf being sent
*
* Output parameters: NONE
*
* Return value: NONE
*/
void send_packet(
struct ifnet *ifp, struct mbuf *m
)
{
i596_tbd *pPrev = I596_NULL;
i596_tbd *pRemainingTbdList;
i596_tbd *pTbd;
struct mbuf *n, *input_m = m;
uti596_softc_ *sc = ifp->if_softc;
struct mbuf *l = NULL;
unsigned int length = 0;
rtems_status_code status;
int bd_count = 0;
rtems_event_set events;
/*
* For all mbufs in the chain,
* fill a transmit buffer descriptor for each
*/
pTbd = (i596_tbd*) word_swap ((unsigned long)sc->pTxCmd->pTbd);
do {
if (m->m_len) {
/*
* Fill in the buffer descriptor
*/
length += m->m_len;
pTbd->data = (char *) word_swap ((unsigned long) mtod (m, void *));
pTbd->size = m->m_len;
pPrev = pTbd;
pTbd = (i596_tbd *) word_swap ((unsigned long) pTbd->next);
l = m;
m = m->m_next;
}
else {
/*
* Just toss empty mbufs
*/
MFREE (m, n);
m = n;
if (l != NULL)
l->m_next = m;
}
} while( m != NULL && ++bd_count < 16 );
if ( length < UTI_596_ETH_MIN_SIZE ) {
pTbd->data = (char *) word_swap ((unsigned long) sc->zeroes); /* add padding to pTbd */
pTbd->size = UTI_596_ETH_MIN_SIZE - length; /* zeroes have no effect on the CRC */
}
else /* Don't use pTbd in the send routine */
pTbd = pPrev;
/* Disconnect the packet from the list of Tbd's */
pRemainingTbdList = (i596_tbd *) word_swap ((unsigned long)pTbd->next);
pTbd->next = I596_NULL;
pTbd->size |= UTI_596_END_OF_FRAME;
sc->rawsndcnt++;
#ifdef DBG_SEND
printk(("send_packet: sending packet\n"))
#endif
/* Sending Zero length packet: shouldn't happen */
if (pTbd->size <= 0) return;
#ifdef DBG_PACKETS
printk (("\nsend_packet: Transmitter adds packet\n"))
print_hdr ( sc->pTxCmd->pTbd->data ); /* print the first part */
print_pkt ( sc->pTxCmd->pTbd->next->data ); /* print the first part */
print_echo (sc->pTxCmd->pTbd->data);
#endif
/* add the command to the output command queue */
uti596_addCmd ( (i596_cmd *) sc->pTxCmd );
/* sleep until the command has been processed or Timeout encountered. */
status= rtems_bsdnet_event_receive (INTERRUPT_EVENT,
RTEMS_WAIT|RTEMS_EVENT_ANY,
RTEMS_NO_TIMEOUT,
&events);
if ( status != RTEMS_SUCCESSFUL ) {
printk(("Could not sleep %s\n", rtems_status_text(status)))
}
#ifdef DBG_SEND
printk(("send_packet: RAW - wake\n"))
#endif
sc->txInterrupts++;
if ( sc->pTxCmd -> cmd.status & STAT_OK ) {
sc->stats.tx_packets++;
}
else {
printk(("*** send_packet: Driver Error 0x%x\n", sc->pTxCmd -> cmd.status ))
sc->stats.tx_errors++;
if ( sc->pTxCmd->cmd.status & 0x0020 )
sc->stats.tx_retries_exceeded++;
if (!(sc->pTxCmd->cmd.status & 0x0040))
sc->stats.tx_heartbeat_errors++;
if ( sc->pTxCmd->cmd.status & 0x0400 )
sc->stats.tx_carrier_errors++;
if ( sc->pTxCmd->cmd.status & 0x0800 )
sc->stats.collisions++;
if ( sc->pTxCmd->cmd.status & 0x1000 )
sc->stats.tx_aborted_errors++;
} /* end if stat_ok */
/*
* Restore the transmitted buffer descriptor chain.
*/
pTbd -> next = (i596_tbd *) word_swap ((unsigned long)pRemainingTbdList);
/*
* Free the mbufs used by the sender.
*/
m = input_m;
while ( m != NULL ) {
MFREE(m,n);
m = n;
}
}
/***********************************************************************
* Function: uti596_attach
*
* Description:
* Configure the driver, and connect to the network stack
*
* Algorithm:
*
* Check parameters in the ifconfig structure, and
* set driver parameters accordingly.
* Initialize required rx and tx buffers.
* Link driver data structure onto device list.
* Return 1 on successful completion.
*
***********************************************************************/
int uti596_attach(
struct rtems_bsdnet_ifconfig * pConfig,
int attaching
)
{
uti596_softc_ *sc = &uti596_softc; /* device dependent data structure */
struct ifnet * ifp = (struct ifnet *)&sc->arpcom.ac_if; /* ifnet structure */
int unitNumber;
char *unitName;
#if defined(mvme167)
unsigned char j1; /* State of J1 jumpers */
char *pAddr;
int addr;
#endif
#ifdef DBG_ATTACH
printk(("uti596_attach: begins\n"))
#endif
/* The NIC is not started yet */
sc->started = 0;
/* Indicate to ULCS that this is initialized */
ifp->if_softc = (void *)sc;
sc->pScp = NULL;
/* Parse driver name */
if ((unitNumber = rtems_bsdnet_parse_driver_name (pConfig, &unitName)) < 0)
return 0;
ifp->if_name = unitName;
ifp->if_unit = unitNumber;
/* Assign mtu */
if ( pConfig -> mtu )
ifp->if_mtu = pConfig -> mtu;
else
ifp->if_mtu = ETHERMTU;
/*
* Check whether parameters should be obtained from NVRAM. If
* yes, and if an IP address, netmask, or ethernet address are
* provided in NVRAM, cheat, and stuff them into the ifconfig
* structure, OVERRIDING and existing or NULL values.
*
* Warning: If values are provided in NVRAM, the ifconfig entries
* must be NULL because buffer memory allocated to hold the
* structure values is unrecoverable and would be lost here.
*/
#if defined(mvme167)
/* Read the J1 header */
j1 = (unsigned char)(lcsr->vector_base & 0xFF);
if ( !(j1 & 0x10) ) {
/* Jumper J1-4 is on, configure from NVRAM */
if ( (addr = nvram->ipaddr) ) {
/* We have a non-zero entry, copy the value */
if ( (pAddr = malloc ( INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT )) )
pConfig->ip_address = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1 );
else
rtems_panic("Can't allocate ip_address buffer!\n");
}
if ( (addr = nvram->netmask) ) {
/* We have a non-zero entry, copy the value */
if ( (pAddr = malloc ( INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT )) )
pConfig->ip_netmask = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1 );
else
rtems_panic("Can't allocate ip_netmask buffer!\n");
}
/* Ethernet address requires special handling -- it must be copied into
* the arpcom struct. The following if construct serves only to give the
* NVRAM parameter the highest priority if J1-4 indicates we are configuring
* from NVRAM.
*
* If the ethernet address is specified in NVRAM, go ahead and copy it.
* (ETHER_ADDR_LEN = 6 bytes).
*/
if ( nvram->enaddr[0] || nvram->enaddr[1] || nvram->enaddr[2] ) {
/* Anything in the first three bytes indicates a non-zero entry, copy value */
memcpy ((void *)sc->arpcom.ac_enaddr, &nvram->enaddr, ETHER_ADDR_LEN);
}
else if ( pConfig->hardware_address) {
/* There is no entry in NVRAM, but there is in the ifconfig struct, so use it. */
memcpy ((void *)sc->arpcom.ac_enaddr, pConfig->hardware_address, ETHER_ADDR_LEN);
}
else {
/* There is no ethernet address provided, so it will be read
* from BBRAM at $FFFC1F2C by default. [mvme167 manual p. 1-47]
*/
memcpy ((void *)sc->arpcom.ac_enaddr, (char *)0xFFFC1F2C, ETHER_ADDR_LEN);
}
}
else if ( pConfig->hardware_address) {
/* We are not configuring from NVRAM (J1-4 is off), and the ethernet address
* is given in the ifconfig structure. Copy it.
*/
memcpy ((void *)sc->arpcom.ac_enaddr, pConfig->hardware_address, ETHER_ADDR_LEN);
}
else
#endif
{
/* We are not configuring from NVRAM (J1-4 is off), and there is no ethernet
* address provided in the ifconfig struct, so it will be read from BBRAM at
* $FFFC1F2C by default. [mvme167 manual p. 1-47]
*/
memcpy ((void *)sc->arpcom.ac_enaddr, (char *)0xFFFC1F2C, ETHER_ADDR_LEN);
}
/* Possibly override default acceptance of broadcast packets */
if (pConfig->ignore_broadcast)
uti596initSetup[8] |= 0x02;
/* Assign requested receive buffer descriptor count */
if (pConfig->rbuf_count)
sc->rxBdCount = pConfig->rbuf_count;
else
sc->rxBdCount = RX_BUF_COUNT;
/* Assign requested tx buffer descriptor count */
if (pConfig->xbuf_count)
sc->txBdCount = pConfig->xbuf_count;
else
sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF;
/* Set up fields in the ifnet structure*/
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
ifp->if_snd.ifq_maxlen = ifqmaxlen;
ifp->if_init = uti596_init;
ifp->if_ioctl = uti596_ioctl;
ifp->if_start = uti596_start;
ifp->if_output = ether_output;
/* uti596_softc housekeeping */
sc->started = 1;
sc->pInboundFrameQueue = I596_NULL;
sc->scb.command = 0;
/*
* Attach the interface
*/
if_attach (ifp);
ether_ifattach (ifp);
return 1;
}
/***********************************************************************
* Function: uti596_start
*
* Description:
* start the driver
*
* Algorithm:
* send an event to the tx task
* set the if_flags
*
***********************************************************************/
static void uti596_start(
struct ifnet *ifp
)
{
uti596_softc_ *sc = ifp->if_softc;
#ifdef DBG_START
printk(("uti596_start: begins\n"))
#endif
rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
ifp->if_flags |= IFF_OACTIVE;
}
/***********************************************************************
* Function: uti596_init
*
* Description:
* driver initialization
*
* Algorithm:
* initialize the 82596
* start driver tx and rx tasks, and reset task
* send the RX_START command the the RU
* set if_flags
*
*
***********************************************************************/
void uti596_init(
void * arg
)
{
uti596_softc_ *sc = arg;
struct ifnet *ifp = (struct ifnet *)&sc->arpcom.ac_if;
if (sc->txDaemonTid == 0) {
/*
* Initialize the 82596
*/
#ifdef DBG_INIT
printk(("uti596_init: begins\nuti596_init: initializing the 82596...\n"))
#endif
uti596_initialize_hardware(sc);
/*
* Start driver tasks
*/
#ifdef DBG_INIT
printk(("uti596_init: starting driver tasks...\n"))
#endif
sc->txDaemonTid = rtems_bsdnet_newproc ("UTtx", 2*4096, uti596_txDaemon, (void *)sc);
sc->rxDaemonTid = rtems_bsdnet_newproc ("UTrx", 2*4096, uti596_rxDaemon, (void *)sc);
sc->resetDaemonTid = rtems_bsdnet_newproc ("UTrt", 2*4096, uti596_resetDaemon, (void *)sc);
#ifdef DBG_INIT
printk(("uti596_init: After attach, status of board = 0x%x\n", sc->scb.status ))
#endif
}
/*
* In case the ISR discovers there are no resources it reclaims
* them and restarts
*/
sc->started = 1;
/*
* Enable receiver
*/
#ifdef DBG_INIT
printk(("uti596_init: enabling the reciever...\n" ))
#endif
sc->scb.command = RX_START;
uti596_issueCA ( sc, UTI596_WAIT_FOR_CU_ACCEPT );
/*
* Tell the world that we're running.
*/
ifp->if_flags |= IFF_RUNNING;
#ifdef DBG_INIT
printk(("uti596_init: completed.\n"))
#endif
}
/***********************************************************************
* Function: uti596stop
*
* Description:
* stop the driver
*
* Algorithm:
* mark driver as not started,
* mark transmitter as busy
* abort any transmissions/receptions
* clean-up all buffers ( RFD's et. al. )
*
*
***********************************************************************/
/* static */ void uti596_stop(
uti596_softc_ *sc
)
{
struct ifnet *ifp = (struct ifnet *)&sc->arpcom.ac_if;
ifp->if_flags &= ~IFF_RUNNING;
sc->started = 0;
#ifdef DBG_STOP
printk(("uti596stop: %s: Shutting down ethercard, status was %4.4x.\n",
uti596_softc.arpcom.ac_if.if_name, uti596_softc.scb.status))
#endif
printk(("Stopping interface\n"))
sc->scb.command = CUC_ABORT | RX_ABORT;
i82596->chan_attn = 0x00000000;
}
/***********************************************************************
* Function: void uti596_txDaemon
*
* Description: Transmit task
*
* Algorithm: Get mbufs to be transmitted, stuff into RFDs, send
*
***********************************************************************/
void uti596_txDaemon(
void *arg
)
{
uti596_softc_ *sc = (uti596_softc_ *)arg;
struct ifnet *ifp = (struct ifnet *)&sc->arpcom.ac_if;
struct mbuf *m;
rtems_event_set events;
for (;;) {
/*
* Wait for packet from stack
*/
rtems_bsdnet_event_receive (START_TRANSMIT_EVENT,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT, &events);
/*
* Send packets till queue is empty.
* Ensure that irq is on before sending.
*/
for (;;) {
/* Get the next mbuf chain to transmit. */
IF_DEQUEUE(&ifp->if_snd, m);
if (!m)
break;
send_packet (ifp, m); /* blocks */
}
ifp->if_flags &= ~IFF_OACTIVE; /* no more to send, mark output inactive */
}
}
/***********************************************************************
* Function: uti596_rxDaemon
*
* Description: Receiver task
*
* Algorithm: Extract the packet from an RFD, and place into an
* mbuf chain. Place the mbuf chain in the network task
* queue. Assumes that the frame check sequence is removed
* by the 82596.
*
***********************************************************************/
/* static */ void uti596_rxDaemon(
void *arg
)
{
uti596_softc_ *sc = (uti596_softc_ *)arg;
struct ifnet *ifp = (struct ifnet *)&sc->arpcom.ac_if;
struct mbuf *m;
i596_rfd *pRfd;
ISR_Level level;
rtems_id tid;
rtems_event_set events;
struct ether_header *eh;
int frames = 0;
#ifdef DBG_RX
printk(("uti596_rxDaemon: begin\n"))
printk(("&scb = %p, pRfd = %p\n", &sc->scb,sc->scb.pRfd))
#endif
rtems_task_ident (0, 0, &tid);
for(;;) {
/*
* Wait for packet.
*/
#ifdef DBG_RX
printk(("uti596_rxDaemon: Receiver sleeps\n"))
#endif
rtems_bsdnet_event_receive (INTERRUPT_EVENT,
RTEMS_WAIT|RTEMS_EVENT_ANY,
RTEMS_NO_TIMEOUT,
&events);
#ifdef DBG_RX
printk(("uti596_rxDaemon: Receiver wakes\n"))
#endif
/*
* While received frames are available. Note that the frame may be
* a fragment, so it is NOT a complete packet.
*/
pRfd = uti596_dequeue( (i596_rfd **)&sc->pInboundFrameQueue);
while ( pRfd &&
pRfd != I596_NULL &&
pRfd -> stat & STAT_C )
{
if ( pRfd->stat & STAT_OK) { /* a good frame */
int pkt_len = pRfd->count & 0x3fff; /* the actual # of bytes received */
#ifdef DBG_RX
printk(("uti596_rxDaemon: Good frame, @%p, data @%p length %d\n", pRfd, pRfd -> data , pkt_len))
#endif
frames++;
/*
* Allocate an mbuf to give to the stack
* The format of the data portion of the RFD is:
* <ethernet header, payload>.
* The FRAME CHECK SEQUENCE / CRC is stripped by the uti596.
* This is to be optimized later.... should not have to memcopy!
*/
MGETHDR(m, M_WAIT, MT_DATA);
MCLGET(m, M_WAIT);
m->m_pkthdr.rcvif = ifp;
/* move everything into an mbuf */
memcpy(m->m_data, (const char *)pRfd->data, pkt_len);
m->m_len = m->m_pkthdr.len = pkt_len - sizeof(struct ether_header) - 4;
/* move the header to an mbuf */
eh = mtod (m, struct ether_header *);
m->m_data += sizeof(struct ether_header);
#ifdef DBG_PACKETS
{
int i;
printk(("uti596_rxDaemon: mbuf contains:\n"))
print_eth( (char *) (((int)m->m_data)-sizeof(struct ether_header)));
for ( i = 0; i<20; i++) {
printk(("."))
}
}
#endif
ether_input (ifp, eh, m);
} /* end if STAT_OK */
else {
/*
* A bad frame is present: Note that this could be the last RFD!
*/
#ifdef DBG_RX
printk(("uti596_rxDaemon: Bad frame\n"))
#endif
/*
* FIX ME: use the statistics from the SCB
*/
sc->stats.rx_errors++;
if ((sc->scb.pRfd->stat) & 0x0001)
sc->stats.collisions++;
if ((sc->scb.pRfd->stat) & 0x0080)
sc->stats.rx_length_errors++;
if ((sc->scb.pRfd->stat) & 0x0100)
sc->stats.rx_over_errors++;
if ((sc->scb.pRfd->stat) & 0x0200)
sc->stats.rx_fifo_errors++;
if ((sc->scb.pRfd->stat) & 0x0400)
sc->stats.rx_frame_errors++;
if ((sc->scb.pRfd->stat) & 0x0800)
sc->stats.rx_crc_errors++;
if ((sc->scb.pRfd->stat) & 0x1000)
sc->stats.rx_length_errors++;
}
UTI_596_ASSERT(pRfd != I596_NULL, "Supplying NULL RFD\n")
_ISR_Local_disable(level);
uti596_supplyFD ( pRfd ); /* Return RFD to RFA. */
_ISR_Local_enable(level);
pRfd = uti596_dequeue( (i596_rfd **)&sc->pInboundFrameQueue); /* grab next frame */
} /* end while */
} /* end for() */
#ifdef DBG_RX
printk (("uti596_rxDaemon: frames ... %d\n", frames))
#endif
}
/***********************************************************************
* Function: void uti596_resetDaemon
*
* Description:
***********************************************************************/
void uti596_resetDaemon(
void *arg
)
{
uti596_softc_ *sc = (uti596_softc_ *)arg;
rtems_event_set events;
rtems_time_of_day tm_struct;
/* struct ifnet *ifp = &sc->arpcom.ac_if; */
for (;;) {
/* Wait for reset event from ISR */
rtems_bsdnet_event_receive (NIC_RESET_EVENT,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT, &events);
rtems_clock_get_tod(&tm_struct);
printk(("reset daemon: Resetting NIC @ %" PRIu32 ":%" PRIu32 ":%" PRIu32 " \n",
tm_struct.hour, tm_struct.minute, tm_struct.second))
sc->stats.nic_reset_count++;
/* Reinitialize the LANC */
rtems_bsdnet_semaphore_obtain ();
uti596_reset();
rtems_bsdnet_semaphore_release ();
}
}
/***********************************************************************
* Function: uti596_DynamicInterruptHandler
*
* Description:
* This is the interrupt handler for the uti596 board
*
***********************************************************************/
/* static */ rtems_isr uti596_DynamicInterruptHandler(
rtems_vector_number irq
)
{
int fullStatus;
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: begins"))
#endif
uti596_wait (&uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT);
scbStatus = (fullStatus = uti596_softc.scb.status) & 0xf000;
if ( scbStatus ) {
/* acknowledge interrupts */
/* Write to the ICLR bit in the PCCchip2 control registers to clear
* the INT status bit. Clearing INT here *before* sending the CA signal
* to the 82596 should ensure that interrupts won't be lost.
*/
pccchip2->LANC_int_ctl |=0x08;
pccchip2->LANC_berr_ctl |=0x08;
/* printk(("***INFO: ACK %x\n", scbStatus))*/
/* Send the CA signal to acknowledge interrupt */
uti596_softc.scb.command = scbStatus;
uti596_issueCA ( &uti596_softc, UTI596_NO_WAIT );
if( uti596_softc.resetDone ) {
/* stack is attached */
uti596_wait ( &uti596_softc, UTI596_WAIT_FOR_CU_ACCEPT );
}
else {
printk(("***INFO: ACK'd w/o processing. status = %x\n", scbStatus))
return;
}
}
else {
#ifndef IGNORE_SPURIOUS_IRQ
printk(("\n***ERROR: Spurious interrupt (full status 0x%x). Resetting...\n", fullStatus))
uti596_softc.nic_reset = 1;
#endif
}
if ( (scbStatus & SCB_STAT_CX) && !(scbStatus & SCB_STAT_CNA) ) {
printk(("\n*****ERROR: Command Complete, and CNA available: 0x%x\nResetting...", scbStatus))
uti596_softc.nic_reset = 1;
return;
}
if ( !(scbStatus & SCB_STAT_CX) && (scbStatus & SCB_STAT_CNA) ) {
printk(("\n*****ERROR: CNA, NO CX:0x%x\nResetting...",scbStatus))
uti596_softc.nic_reset = 1;
return;
}
if ( scbStatus & SCB_CUS_SUSPENDED ) {
printk(("\n*****ERROR: Command unit suspended!:0x%x\nResetting...",scbStatus))
uti596_softc.nic_reset = 1;
return;
}
if ( scbStatus & RU_SUSPENDED ) {
printk(("\n*****ERROR: Receive unit suspended!:0x%x\nResetting...",scbStatus))
uti596_softc.nic_reset = 1;
return;
}
if ( scbStatus & SCB_STAT_RNR ) {
printk(("\n*****WARNING: RNR %x\n",scbStatus))
if (uti596_softc.pBeginRFA != I596_NULL) {
printk(("*****INFO: RFD cmd: %x status:%x\n", uti596_softc.pBeginRFA->cmd,
uti596_softc.pBeginRFA->stat))
}
else {
printk(("*****WARNING: RNR condition with NULL BeginRFA\n"))
}
}
/*
* Receive Unit Control
* a frame is received
*/
if ( scbStatus & SCB_STAT_FR ) {
uti596_softc.rxInterrupts++;
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: Frame received\n"))
#endif
if ( uti596_softc.pBeginRFA == I596_NULL ||
!( uti596_softc.pBeginRFA -> stat & STAT_C)) {
#ifndef IGNORE_NO_RFA
uti596_dump_scb();
uti596_softc.nic_reset = 1;
#endif
}
else {
while ( uti596_softc.pBeginRFA != I596_NULL &&
( uti596_softc.pBeginRFA -> stat & STAT_C)) {
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: pBeginRFA != NULL\n"))
#endif
count_rx ++;
#ifndef IGNORE_MULTIPLE_RF
if ( count_rx > 1) {
printk(("****WARNING: Received %i frames on 1 interrupt \n", count_rx))
}
#endif
/* Give Received Frame to the ULCS */
uti596_softc.countRFD--;
if ( uti596_softc.countRFD < 0 ) {
printk(("ISR: Count < 0 !!! count == %d, beginRFA = %p\n",
uti596_softc.countRFD, uti596_softc.pBeginRFA))
}
uti596_softc.stats.rx_packets++;
/* the rfd next link is stored with upper and lower words swapped so read it that way */
pIsrRfd = (i596_rfd *) word_swap ((unsigned long)uti596_softc.pBeginRFA->next);
/* the append destroys the link */
uti596_append( (i596_rfd **)&uti596_softc.pInboundFrameQueue , uti596_softc.pBeginRFA );
/*
* if we have just received the a frame in the last unknown RFD,
* then it is certain that the RFA is empty.
*/
if ( uti596_softc.pLastUnkRFD == uti596_softc.pBeginRFA ) {
UTI_596_ASSERT(uti596_softc.pLastUnkRFD != I596_NULL,"****ERROR:LastUnk is NULL, begin ptr @ end!\n")
uti596_softc.pEndRFA = uti596_softc.pLastUnkRFD = I596_NULL;
}
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: Wake %#x\n",uti596_softc.rxDaemonTid))
#endif
sc = rtems_bsdnet_event_send(uti596_softc.rxDaemonTid, INTERRUPT_EVENT);
if ( sc != RTEMS_SUCCESSFUL ) {
rtems_panic("Can't notify rxDaemon: %s\n",
rtems_status_text (sc));
}
#ifdef DBG_ISR
else {
printk(("uti596_DynamicInterruptHandler: Rx Wake: %#x\n",uti596_softc.rxDaemonTid))
}
#endif
uti596_softc.pBeginRFA = pIsrRfd;
} /* end while */
} /* end if */
if ( uti596_softc.pBeginRFA == I596_NULL ) {
/* adjust the pEndRFA to reflect an empty list */
if ( uti596_softc.pLastUnkRFD == I596_NULL && uti596_softc.countRFD != 0 ) {
printk(("Last Unk is NULL, BeginRFA is null, and count == %d\n",
uti596_softc.countRFD))
}
uti596_softc.pEndRFA = I596_NULL;
if ( uti596_softc.countRFD != 0 ) {
printk(("****ERROR:Count is %d, but begin ptr is NULL\n",
uti596_softc.countRFD ))
}
}
} /* end if ( scbStatus & SCB_STAT_FR ) */
/*
* Command Unit Control
* a command is completed
*/
if ( scbStatus & SCB_STAT_CX ) {
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: CU\n"))
#endif
pIsrCmd = uti596_softc.pCmdHead;
/* For ALL completed commands */
if ( pIsrCmd != I596_NULL && pIsrCmd->status & STAT_C ) {
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: pIsrCmd != NULL\n"))
#endif
/* Adjust the command block list */
uti596_softc.pCmdHead = (i596_cmd *) word_swap ((unsigned long)pIsrCmd->next);
/*
* If there are MORE commands to process,
* the serialization in the raw routine has failed.
* ( Perhaps AddCmd is bad? )
*/
UTI_596_ASSERT(uti596_softc.pCmdHead == I596_NULL, "****ERROR: command serialization failed\n")
/* What if the command did not complete OK? */
switch ( pIsrCmd->command & 0x7) {
case CmdConfigure:
uti596_softc.cmdOk = 1;
break;
case CmdDump:
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: dump!\n"))
#endif
uti596_softc.cmdOk = 1;
break;
case CmdDiagnose:
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: diagnose!\n"))
#endif
uti596_softc.cmdOk = 1;
break;
case CmdSASetup:
/* printk(("****INFO:Set address interrupt\n")) */
if ( pIsrCmd -> status & STAT_OK ) {
uti596_softc.cmdOk = 1;
}
else {
printk(("****ERROR:SET ADD FAILED\n"))
}
break;
case CmdTx:
UTI_596_ASSERT(uti596_softc.txDaemonTid, "****ERROR:Null txDaemonTid\n")
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: wake TX:0x%x\n",uti596_softc.txDaemonTid))
#endif
if ( uti596_softc.txDaemonTid ) {
/* Ensure that the transmitter is present */
sc = rtems_bsdnet_event_send (uti596_softc.txDaemonTid,
INTERRUPT_EVENT);
if ( sc != RTEMS_SUCCESSFUL ) {
printk(("****ERROR:Could NOT send event to tid 0x%" PRIu32 " : %s\n",
uti596_softc.txDaemonTid, rtems_status_text (sc) ))
}
#ifdef DBG_ISR
else {
printk(("****INFO:Tx wake: %#x\n",uti596_softc.txDaemonTid))
}
#endif
}
break;
case CmdMulticastList:
printk(("***ERROR:Multicast?!\n"))
pIsrCmd->next = I596_NULL;
break;
case CmdTDR: {
unsigned long status = *( (unsigned long *)pIsrCmd)+1;
printk(("****ERROR:TDR?!\n"))
if (status & STAT_C) {
/* mark the TDR command successful */
uti596_softc.cmdOk = 1;
}
else {
if (status & 0x4000) {
printk(("****WARNING:Transceiver problem.\n"))
}
if (status & 0x2000) {
printk(("****WARNING:Termination problem.\n"))
}
if (status & 0x1000) {
printk(("****WARNING:Short circuit.\n"))
/* printk(("****INFO:Time %ld.\n", status & 0x07ff)) */
}
}
}
break;
default: {
/*
* This should never be reached
*/
printk(("CX but NO known command\n"))
}
} /* end switch */
pIsrCmd = uti596_softc.pCmdHead; /* next command */
if ( pIsrCmd != I596_NULL ) {
printk(("****WARNING: more commands in list, but no start to NIC\n"))
}
} /* end if pIsrCmd != NULL && pIsrCmd->stat & STAT_C */
else {
if ( pIsrCmd != I596_NULL ) {
/* The command MAY be NULL from a RESET */
/* Reset the ethernet card, and wake the transmitter (if necessary) */
printk(("****INFO: Request board reset ( tx )\n"))
uti596_softc.nic_reset = 1;
if ( uti596_softc.txDaemonTid) {
/* Ensure that a transmitter is present */
sc = rtems_bsdnet_event_send (uti596_softc.txDaemonTid,
INTERRUPT_EVENT);
if ( sc != RTEMS_SUCCESSFUL ) {
printk(("****ERROR:Could NOT send event to tid 0x%" PRIu32 " : %s\n",
uti596_softc.txDaemonTid, rtems_status_text (sc) ))
}
#ifdef DBG_ISR
else {
printk(("uti596_DynamicInterruptHandler: ****INFO:Tx wake: %#x\n",
uti596_softc.txDaemonTid))
}
#endif
}
}
}
} /* end if command complete */
/*
* If the receiver has stopped,
* check if this is a No Resources scenario,
* Try to add more RFD's ( no RBDs are used )
*/
if ( uti596_softc.started ) {
if ( scbStatus & SCB_STAT_RNR ) {
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: INFO:RNR: status %#x \n",
uti596_softc.scb.status ))
#endif
/*
* THE RECEIVER IS OFF!
*/
if ( uti596_softc.pLastUnkRFD != I596_NULL ) {
/* We have an unknown RFD, it is not inbound */
if ( uti596_softc.pLastUnkRFD -> stat & (STAT_C | STAT_B )) { /* in use */
uti596_softc.pEndRFA = uti596_softc.pLastUnkRFD; /* update end */
}
else {
/*
* It is NOT in use, and since RNR, we know EL bit of pEndRFA was read!
* So, unlink it from the RFA and move it to the saved queue.
* But pBegin can equal LastUnk!
*/
if ( uti596_softc.pEndRFA != I596_NULL ) {
/* check added feb24. */
#ifdef DBG_ISR
if ((i596_rfd *)word_swap((unsigned long)uti596_softc.pEndRFA->next) != uti596_softc.pLastUnkRFD) {
printk(("***ERROR:UNK: %p not end->next: %p, end: %p\n",
uti596_softc.pLastUnkRFD,
uti596_softc.pEndRFA -> next,
uti596_softc.pEndRFA))
printk(("***INFO:countRFD now %d\n",
uti596_softc.countRFD))
printk(("\n\n"))
}
#endif
uti596_softc.pEndRFA -> next = I596_NULL; /* added feb 16 */
}
uti596_append( (i596_rfd **)&uti596_softc.pSavedRfdQueue, uti596_softc.pLastUnkRFD );
uti596_softc.savedCount++;
uti596_softc.pEndSavedQueue = uti596_softc.pLastUnkRFD;
uti596_softc.countRFD--; /* It was not in the RFA */
/*
* The Begin pointer CAN advance this far. We must resynch the CPU side
* with the chip.
*/
if ( uti596_softc.pBeginRFA == uti596_softc.pLastUnkRFD ) {
#ifdef DBG_ISR
if ( uti596_softc.countRFD != 0 ) {
printk(("****INFO:About to set begin to NULL, with count == %d\n\n",
uti596_softc.countRFD ))
}
#endif
uti596_softc.pBeginRFA = I596_NULL;
UTI_596_ASSERT(uti596_softc.countRFD == 0, "****ERROR:Count must be zero here!\n")
}
}
uti596_softc.pLastUnkRFD = I596_NULL;
} /* end if exists UnkRFD */
/*
* Append the saved queue to the RFA.
* Any further RFD's being supplied will be added to
* this new list.
*/
if ( uti596_softc.pSavedRfdQueue != I596_NULL ) {
/* entries to add */
if ( uti596_softc.pBeginRFA == I596_NULL ) {
/* add at beginning to list */
#ifdef DBG_ISR
if(uti596_softc.countRFD != 0) {
printk(("****ERROR:Begin pointer is NULL, but count == %d\n",
uti596_softc.countRFD))
}
#endif
uti596_softc.pBeginRFA = uti596_softc.pSavedRfdQueue;
uti596_softc.pEndRFA = uti596_softc.pEndSavedQueue;
uti596_softc.pSavedRfdQueue = uti596_softc.pEndSavedQueue = I596_NULL; /* Reset the End */
}
else {
#ifdef DBG_ISR
if ( uti596_softc.countRFD <= 0) {
printk(("****ERROR:Begin pointer is not NULL, but count == %d\n",
uti596_softc.countRFD))
}
#endif
UTI_596_ASSERT( uti596_softc.pEndRFA != I596_NULL, "****WARNING: END RFA IS NULL\n")
UTI_596_ASSERT( uti596_softc.pEndRFA->next == I596_NULL, "****ERROR:END RFA -> next must be NULL\n")
uti596_softc.pEndRFA->next = (i596_rfd *)word_swap((unsigned long)uti596_softc.pSavedRfdQueue);
uti596_softc.pEndRFA->cmd &= ~CMD_EOL; /* clear the end of list */
uti596_softc.pEndRFA = uti596_softc.pEndSavedQueue;
uti596_softc.pSavedRfdQueue = uti596_softc.pEndSavedQueue = I596_NULL; /* Reset the End */
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: count... %d, saved ... %d \n",
uti596_softc.countRFD,
uti596_softc.savedCount))
#endif
}
/* printk(("Isr: countRFD = %d\n",uti596_softc.countRFD)) */
uti596_softc.countRFD += uti596_softc.savedCount;
/* printk(("Isr: after countRFD = %d\n",uti596_softc.countRFD)) */
uti596_softc.savedCount = 0;
}
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: The list starts here %p\n",uti596_softc.pBeginRFA ))
#endif
if ( uti596_softc.countRFD > 1) {
printk(("****INFO: pBeginRFA -> stat = 0x%x\n",uti596_softc.pBeginRFA -> stat))
printk(("****INFO: pBeginRFA -> cmd = 0x%x\n",uti596_softc.pBeginRFA -> cmd))
uti596_softc.pBeginRFA -> stat = 0;
UTI_596_ASSERT(uti596_softc.scb.command == 0, "****ERROR:scb command must be zero\n")
uti596_softc.scb.pRfd = uti596_softc.pBeginRFA;
uti596_softc.scb.rfd_pointer = word_swap((unsigned long)uti596_softc.pBeginRFA);
/* start RX here */
printk(("****INFO: ISR Starting receiver\n"))
uti596_softc.scb.command = RX_START; /* should this also be CU start? */
i82596->chan_attn = 0x00000000;
}
} /* end stat_rnr */
} /* end if receiver started */
#ifdef DBG_ISR
printk(("uti596_DynamicInterruptHandler: X\n"))
#endif
count_rx=0;
/* Do this last, to ensure that the reset is called at the right time. */
if ( uti596_softc.nic_reset ) {
uti596_softc.nic_reset = 0;
sc = rtems_bsdnet_event_send(uti596_softc.resetDaemonTid, NIC_RESET_EVENT);
if ( sc != RTEMS_SUCCESSFUL )
rtems_panic ("Can't notify resetDaemon: %s\n", rtems_status_text (sc));
}
return;
}
/***********************************************************************
* Function: uti596_ioctl
*
* Description:
* driver ioctl function
* handles SIOCGIFADDR, SIOCSIFADDR, SIOCSIFFLAGS
*
***********************************************************************/
static int uti596_ioctl(
struct ifnet *ifp,
u_long command,
caddr_t data
)
{
uti596_softc_ *sc = ifp->if_softc;
int error = 0;
#ifdef DBG_IOCTL
printk(("uti596_ioctl: begins\n", sc->pScp))
#endif
switch (command) {
case SIOCGIFADDR:
case SIOCSIFADDR:
printk(("SIOCSIFADDR\n"))
ether_ioctl (ifp, command, data);
break;
case SIOCSIFFLAGS:
printk(("SIOCSIFFLAGS\n"))
switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
case IFF_RUNNING:
printk(("IFF_RUNNING\n"))
uti596_stop (sc);
break;
case IFF_UP:
printk(("IFF_UP\n"))
uti596_init ( (void *)sc);
break;
case IFF_UP | IFF_RUNNING:
printk(("IFF_UP and RUNNING\n"))
uti596_stop (sc);
uti596_init ( (void *)sc);
break;
default:
printk(("default\n"))
break;
}
break;
case SIO_RTEMS_SHOW_STATS:
printk(("show stats\n"))
uti596_stats (sc);
break;
/* FIXME: All sorts of multicast commands need to be added here! */
default:
printk(("default: EINVAL\n"))
error = EINVAL;
break;
}
return error;
}
/***********************************************************************
* Function: uti596_stats
*
* Description:
* print out the collected data
*
* Algorithm:
* use printf
*
***********************************************************************/
void uti596_stats(
uti596_softc_ *sc
)
{
printf ("CPU Reports:\n");
printf (" Tx raw send count:%-8lu", sc->rawsndcnt);
printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
printf (" Tx Interrupts:%-8lu\n", sc->txInterrupts);
printf (" Rx Packets:%-8u", sc->stats.rx_packets);
printf (" Tx Attempts:%-u\n", sc->stats.tx_packets);
printf (" Rx Dropped:%-8u", sc->stats.rx_dropped);
printf (" Rx IP Packets:%-8u", sc->stats.rx_packets);
printf (" Tx Errors:%-8u\n", sc->stats.tx_errors);
printf (" Tx aborted:%-8u", sc->stats.tx_aborted_errors);
printf (" Tx Dropped:%-8u\n", sc->stats.tx_dropped);
printf (" Tx IP packets:%-8u", sc->stats.tx_packets);
printf (" Collisions Detected:%-8u\n", sc->stats.collisions);
printf (" Tx Heartbeat Errors:%-8u", sc->stats.tx_heartbeat_errors);
printf (" Tx Carrier Errors:%-8u\n", sc->stats.tx_carrier_errors);
printf (" Tx Aborted Errors:%-8u", sc->stats.tx_aborted_errors);
printf (" Rx Length Errors:%-8u\n", sc->stats.rx_length_errors);
printf (" Rx Overrun Errors:%-8u", sc->stats.rx_over_errors);
printf (" Rx Fifo Errors:%-8u\n", sc->stats.rx_fifo_errors);
printf (" Rx Framing Errors:%-8u", sc->stats.rx_frame_errors);
printf (" Rx crc errors:%-8u\n", sc->stats.rx_crc_errors);
printf (" TX WAITS: %-8lu\n", sc->txRawWait);
printf (" NIC resets: %-8u\n", sc->stats.nic_reset_count);
printf (" NIC reports\n");
#ifdef DBG_STAT
uti596_dump_scb();
#endif
}
/************************ PACKET DEBUG ROUTINES ************************/
#ifdef DBG_PACKETS
/*
* dumpQ
*
* Dumps frame queues for debugging
*/
static void dumpQ( void )
{
i596_rfd *pRfd;
printk(("savedQ:\n"))
for( pRfd = uti596_softc.pSavedRfdQueue;
pRfd != I596_NULL;
pRfd = (i596_rfd*)word_swap((uint32_t)pRfd -> next)) {
printk(("pRfd: %p, stat: 0x%x cmd: 0x%x\n",pRfd,pRfd -> stat,pRfd -> cmd))
}
printk(("Inbound:\n"))
for( pRfd = uti596_softc.pInboundFrameQueue;
pRfd != I596_NULL;
pRfd = (i596_rfd*)word_swap((uint32_t)pRfd -> next)) {
printk(("pRfd: %p, stat: 0x%x cmd: 0x%x\n",pRfd,pRfd -> stat,pRfd -> cmd))
}
printk(("Last Unk: %p\n", uti596_softc.pLastUnkRFD ))
printk(("RFA:\n"))
for( pRfd = uti596_softc.pBeginRFA;
pRfd != I596_NULL;
pRfd = (i596_rfd*)word_swap((uint32_t)pRfd -> next)) {
printk(("pRfd: %p, stat: 0x%x cmd: 0x%x\n",pRfd,pRfd -> stat,pRfd -> cmd))
}
}
/*
* show_buffers
*
* Print out the RFA and frame queues
*/
static void show_buffers (void)
{
i596_rfd *pRfd;
printk(("82596 cmd: 0x%x, status: 0x%x RFA len: %d\n",
uti596_softc.scb.command,
uti596_softc.scb.status,
uti596_softc.countRFD))
printk(("\nRFA: \n"))
for ( pRfd = uti596_softc.pBeginRFA;
pRfd != I596_NULL;
pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) {
printk(("Frame @ %p, status: %2.2x, cmd: %2.2x\n",
pRfd, pRfd->stat, pRfd->cmd))
}
printk(("\nInbound: \n"))
for ( pRfd = uti596_softc.pInboundFrameQueue;
pRfd != I596_NULL;
pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) {
printk(("Frame @ %p, status: %2.2x, cmd: %2.2x\n",
pRfd, pRfd->stat, pRfd->cmd))
}
printk(("\nSaved: \n"))
for ( pRfd = uti596_softc.pSavedRfdQueue;
pRfd != I596_NULL;
pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) {
printk(("Frame @ %p, status: %2.2x, cmd: %2.2x\n",
pRfd, pRfd->stat, pRfd->cmd))
}
printk(("\nUnknown: %p\n",uti596_softc.pLastUnkRFD))
}
/*
* show_queues
*
* Print out the saved frame queue and the RFA
*/
static void show_queues(void)
{
i596_rfd *pRfd;
printk(("CMD: 0x%x, Status: 0x%x\n",
uti596_softc.scb.command,
uti596_softc.scb.status))
printk(("saved Q\n"))
for ( pRfd = uti596_softc.pSavedRfdQueue;
pRfd != I596_NULL &&
pRfd != NULL;
pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) {
printk(("0x%p\n", pRfd))
}
printk(("End saved Q 0x%p\n", uti596_softc.pEndSavedQueue))
printk(("\nRFA:\n"))
for ( pRfd = uti596_softc.pBeginRFA;
pRfd != I596_NULL &&
pRfd != NULL;
pRfd = (i596_rfd *)word_swap((uint32_t)pRfd->next) ) {
printk(("0x%p\n", pRfd))
}
printk(("uti596_softc.pEndRFA: %p\n",uti596_softc.pEndRFA))
}
/*
* print_eth
*
* Print the contents of an ethernet packet
* CANNOT BE CALLED FROM ISR
*/
static void print_eth(
unsigned char *add
)
{
int i;
short int length;
printk (("Packet Location %p\n", add))
printk (("Dest "))
for (i = 0; i < 6; i++) {
printk ((" %2.2X", add[i]))
}
printk (("\n"))
printk (("Source"))
for (i = 6; i < 12; i++) {
printk ((" %2.2X", add[i]))
}
printk (("\n"))
printk (("frame type %2.2X%2.2X\n", add[12], add[13]))
if ( add[12] == 0x08 && add[13] == 0x06 ) {
/* an ARP */
printk (("Hardware type : %2.2X%2.2X\n", add[14],add[15]))
printk (("Protocol type : %2.2X%2.2X\n", add[16],add[17]))
printk (("Hardware size : %2.2X\n", add[18]))
printk (("Protocol size : %2.2X\n", add[19]))
printk (("op : %2.2X%2.2X\n", add[20],add[21]))
printk (("Sender Enet addr: "))
for ( i=0; i< 5 ; i++) {
printk (("%x:", add[22 + i]))
}
printk (("%x\n", add[27]))
printk (("Sender IP addr: "))
for ( i=0; i< 3 ; i++) {
printk (("%u.", add[28 + i]))
}
printk (("%u\n", add[31]))
printk (("Target Enet addr: "))
for ( i=0; i< 5 ; i++) {
printk (( "%x:", add[32 + i]))
}
printk (("%x\n", add[37]))
printk (("Target IP addr: "))
for ( i=0; i< 3 ; i++) {
printk (( "%u.", add[38 + i]))
}
printk (("%u\n", add[41]))
}
if ( add[12] == 0x08 && add[13] == 0x00 ) {
/* an IP packet */
printk (("*********************IP HEADER******************\n"))
printk (("IP version/IPhdr length: %2.2X TOS: %2.2X\n", add[14] , add[15]))
printk (("IP total length: %2.2X %2.2X, decimal %d\n", add[16], add[17], length = (add[16]<<8 | add[17] )))
printk (("IP identification: %2.2X %2.2X, 3-bit flags and offset %2.2X %2.2X\n",
add[18],add[19], add[20], add[21]))
printk (("IP TTL: %2.2X, protocol: %2.2X, checksum: %2.2X %2.2X \n",
add[22],add[23],add[24],add[25]))
printk (("IP packet type: %2.2X code %2.2X\n", add[34],add[35]))
printk (("Source IP address: "))
for ( i=0; i< 3 ; i++) {
printk (("%u.", add[26 + i]))
}
printk (("%u\n", add[29]))
printk (("Destination IP address: "))
for ( i=0; i< 3 ; i++) {
printk (("%u.", add[30 + i]))
}
printk (("%u\n", add[33]))
}
}
/*
* print_hdr
*
* Print the contents of an ethernet packet header
* CANNOT BE CALLED FROM ISR
*/
static void print_hdr(
unsigned char *add
)
{
int i;
printk (("print_hdr: begins\n"))
printk (("Header Location %p\n", add))
printk (("Dest "))
for (i = 0; i < 6; i++) {
printk ((" %2.2X", add[i]))
}
printk (("\nSource"))
for (i = 6; i < 12; i++) {
printk ((" %2.2X", add[i]))
}
printk (("\nframe type %2.2X%2.2X\n", add[12], add[13]))
printk (("print_hdr: completed"))
}
/*
* Function: print_pkt
*
* Print the contents of an ethernet packet & data
* CANNOT BE CALLED FROM ISR
*/
static void print_pkt(
unsigned char *add
)
{
int i;
short int length;
printk (("print_pkt: begins"))
printk (("Data Location %p\n", add))
if ( add[0] == 0x08 && add[1] == 0x06 ) {
/* an ARP */
printk (("Hardware type : %2.2X%2.2X\n", add[14],add[15]))
printk (("Protocol type : %2.2X%2.2X\n", add[16],add[17]))
printk (("Hardware size : %2.2X\n", add[18]))
printk (("Protocol size : %2.2X\n", add[19]))
printk (("op : %2.2X%2.2X\n", add[20],add[21]))
printk (("Sender Enet addr: "))
for ( i=0; i< 5 ; i++) {
printk (( "%x:", add[22 + i]))
}
printk (("%x\n", add[27]))
printk (("Sender IP addr: "))
for ( i=0; i< 3 ; i++) {
printk (("%u.", add[28 + i]))
}
printk (("%u\n", add[31]))
printk (("Target Enet addr: "))
for ( i=0; i< 5 ; i++) {
printk (( "%x:", add[32 + i]))
}
printk (("%x\n", add[37]))
printk (("Target IP addr: "))
for ( i=0; i< 3 ; i++) {
printk (( "%u.", add[38 + i]))
}
printk (("%u\n", add[41]))
}
if ( add[0] == 0x08 && add[1] == 0x00 ) {
/* an IP packet */
printk (("*********************IP HEADER******************\n"))
printk (("IP version/IPhdr length: %2.2X TOS: %2.2X\n", add[14] , add[15]))
printk (("IP total length: %2.2X %2.2X, decimal %d\n", add[16], add[17], length = (add[16]<<8 | add[17] )))
printk (("IP identification: %2.2X %2.2X, 3-bit flags and offset %2.2X %2.2X\n",
add[18],add[19], add[20], add[21]))
printk (("IP TTL: %2.2X, protocol: %2.2X, checksum: %2.2X %2.2X \n",
add[22],add[23],add[24],add[25]))
printk (("IP packet type: %2.2X code %2.2X\n", add[34],add[35]))
printk (("Source IP address: "))
for ( i=0; i< 3 ; i++) {
printk(( "%u.", add[26 + i]))
}
printk(("%u\n", add[29]))
printk(("Destination IP address: "))
for ( i=0; i< 3 ; i++) {
printk(( "%u.", add[30 + i]))
}
printk(("%u\n", add[33]))
printk(("********************IP Packet Data*******************\n"))
length -=20;
for ( i=0; i < length ; i++) {
printk(("0x%2.2x ", add[34+i]))
}
printk(("\n"))
printk(("ICMP checksum: %2.2x %2.2x\n", add[36], add[37]))
printk(("ICMP identifier: %2.2x %2.2x\n", add[38], add[39]))
printk(("ICMP sequence nbr: %2.2x %2.2x\n", add[40], add[41]))
printk(("print_pkt: completed"))
}
}
/*
* print_echo
*
* Print the contents of an echo packet
* CANNOT BE CALLED FROM ISR
*/
static void print_echo(
unsigned char *add
)
{
int i;
short int length;
printk (("print_echo: begins"))
if ( add[12] == 0x08 && add[13] == 0x00 ) {
/* an IP packet */
printk (("Packet Location %p\n", add))
printk (("Dest "))
for (i = 0; i < 6; i++) {
printk ((" %2.2X", add[i]))
}
printk (("\n"))
printk (("Source"))
for (i = 6; i < 12; i++) {
printk ((" %2.2X", add[i]))
}
printk (("\n"))
printk (("frame type %2.2X%2.2X\n", add[12], add[13]))
printk (("*********************IP HEADER******************\n"))
printk (("IP version/IPhdr length: %2.2X TOS: %2.2X\n", add[14] , add[15]))
printk (("IP total length: %2.2X %2.2X, decimal %d\n", add[16], add[17], length = (add[16]<<8 | add[17] )))
printk (("IP identification: %2.2X %2.2X, 3-bit flags and offset %2.2X %2.2X\n",
add[18],add[19], add[20], add[21]))
printk (("IP TTL: %2.2X, protocol: %2.2X, checksum: %2.2X %2.2X \n",
add[22],add[23],add[24],add[25]))
printk (("IP packet type: %2.2X code %2.2X\n", add[34],add[35]))
printk (("Source IP address: "))
for ( i=0; i< 3 ; i++) {
printk (("%u.", add[26 + i]))
}
printk (("%u\n", add[29]))
printk (("Destination IP address: "))
for ( i=0; i< 3 ; i++) {
printk (("%u.", add[30 + i]))
}
printk (("%u\n", add[33]))
printk(("********************IP Packet Data*******************\n"))
length -=20;
for ( i=0; i < length ; i++) {
printk(("0x%2.2x ", add[34+i]))
}
printk(("\n"))
printk(("ICMP checksum: %2.2x %2.2x\n", add[36], add[37]))
printk(("ICMP identifier: %2.2x %2.2x\n", add[38], add[39]))
printk(("ICMP sequence nbr: %2.2x %2.2x\n", add[40], add[41]))
printk(("print_echo: completed"))
}
}
#endif