forked from Imagelibrary/rtems
Add and use <machine/rtems-bsd-kernel-space.h> and <machine/rtems-bsd-user-space.h> similar to the libbsd to avoid command line defines and defines scattered throught the code base. Simplify cpukit/libnetworking/Makefile.am. Update #3375.
1686 lines
44 KiB
C
1686 lines
44 KiB
C
/*
|
|
* RTEMS NETWORK DRIVER FOR NATIONAL DP83932 `SONIC'
|
|
* SYSTEMS-ORIENTED NETWORK INTERFACE CONTROLLER
|
|
*
|
|
* REUSABLE CHIP DRIVER
|
|
*
|
|
* References:
|
|
*
|
|
* 1) DP83932C-20/25/33 MHz SONIC(TM) Systems-Oriented Network Interface
|
|
* Controller data sheet. TL/F/10492, RRD-B30M105, National Semiconductor,
|
|
* 1995.
|
|
*
|
|
* 2) Software Driver Programmer's Guide for the DP83932 SONIC(TM),
|
|
* Application Note 746, Wesley Lee and Mike Lui, TL/F/11140,
|
|
* RRD-B30M75, National Semiconductor, March, 1991.
|
|
*
|
|
* COPYRIGHT (c) 1989-1997.
|
|
* On-Line Applications Research Corporation (OAR).
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rtems.org/license/LICENSE.
|
|
*
|
|
* This driver was originally written and tested on a DY-4 DMV177,
|
|
* which had a 100 Mhz PPC603e.
|
|
*
|
|
* This driver also works with DP83934CVUL-20/25 MHz, tested on
|
|
* Tharsys ERC32 VME board.
|
|
*
|
|
* Rehaul to fix lost interrupts and buffers, and to use to use
|
|
* interrupt-free transmission by Jiri, 22/03/1999.
|
|
*/
|
|
|
|
#include <machine/rtems-bsd-kernel-space.h>
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/rtems_bsdnet.h>
|
|
#include <libchip/sonic.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
#include <rtems/error.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/sockio.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
|
|
extern rtems_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int );
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_MBUFS)
|
|
#include <rtems/dumpbuf.h>
|
|
#endif
|
|
|
|
/*
|
|
* Use the top line if you want more symbols.
|
|
*/
|
|
|
|
#define SONIC_STATIC static
|
|
|
|
/*
|
|
* Number of devices supported by this driver
|
|
*/
|
|
#ifndef NSONIC
|
|
# define NSONIC 1
|
|
#endif
|
|
|
|
/*
|
|
*
|
|
* As suggested by National Application Note 746, make the
|
|
* receive resource area bigger than the receive descriptor area.
|
|
*
|
|
* NOTE: Changing this may break this driver since it currently
|
|
* assumes a 1<->1 mapping.
|
|
*/
|
|
#define RRA_EXTRA_COUNT 0
|
|
|
|
/*
|
|
* RTEMS event used by interrupt handler to signal daemons.
|
|
*/
|
|
#define INTERRUPT_EVENT RTEMS_EVENT_1
|
|
|
|
/*
|
|
* RTEMS event used to start transmit daemon.
|
|
* This must not be the same as INTERRUPT_EVENT.
|
|
*/
|
|
#define START_TRANSMIT_EVENT RTEMS_EVENT_2
|
|
|
|
/*
|
|
* Largest Ethernet frame.
|
|
*/
|
|
#define MAXIMUM_FRAME_SIZE 1518
|
|
|
|
/*
|
|
* Receive buffer size.
|
|
* Allow for a pointer, plus a full ethernet frame (including Frame
|
|
* Check Sequence) rounded up to a 4-byte boundary.
|
|
*/
|
|
#define RBUF_SIZE ((sizeof (void *) + (MAXIMUM_FRAME_SIZE) + 3) & ~3)
|
|
/* #define RBUF_WC ((((MAXIMUM_FRAME_SIZE) + 3) & ~3) / 2) */
|
|
#define RBUF_WC (RBUF_SIZE / 2)
|
|
|
|
/*
|
|
* Macros for manipulating 32-bit pointers as 16-bit fragments
|
|
*/
|
|
#define LSW(p) ((uint16_t)((uintptr_t)(p)))
|
|
#define MSW(p) ((uint16_t)((uintptr_t)(p) >> 16))
|
|
#define PTR(m,l) ((void*)(((uint16_t)(m)<<16)|(uint16_t)(l)))
|
|
|
|
/*
|
|
* Hardware-specific storage
|
|
*/
|
|
struct sonic_softc {
|
|
/*
|
|
* Connection to networking code
|
|
* This entry *must* be the first in the sonic_softc structure.
|
|
*/
|
|
struct arpcom arpcom;
|
|
|
|
/*
|
|
* Default location of device registers
|
|
* ===CACHE===
|
|
* This area must be non-cacheable, guarded.
|
|
*/
|
|
void *sonic;
|
|
|
|
/*
|
|
* Register access routines
|
|
*/
|
|
sonic_write_register_t write_register;
|
|
sonic_read_register_t read_register;
|
|
|
|
/*
|
|
* Interrupt vector
|
|
*/
|
|
rtems_vector_number vector;
|
|
|
|
/*
|
|
* Data Configuration Register values
|
|
*/
|
|
uint32_t dcr_value;
|
|
uint32_t dc2_value;
|
|
|
|
/*
|
|
* Indicates configuration
|
|
*/
|
|
int acceptBroadcast;
|
|
|
|
/*
|
|
* Task waiting for interrupts
|
|
*/
|
|
rtems_id rxDaemonTid;
|
|
rtems_id txDaemonTid;
|
|
|
|
/*
|
|
* Receive resource area
|
|
*/
|
|
int rdaCount;
|
|
ReceiveResourcePointer_t rsa;
|
|
ReceiveResourcePointer_t rea;
|
|
CamDescriptorPointer_t cdp;
|
|
ReceiveDescriptorPointer_t rda;
|
|
ReceiveDescriptorPointer_t rdp_last;
|
|
|
|
/*
|
|
* Transmit descriptors
|
|
*/
|
|
int tdaCount;
|
|
TransmitDescriptorPointer_t tdaHead; /* Last filled */
|
|
TransmitDescriptorPointer_t tdaTail; /* Next to retire */
|
|
|
|
/*
|
|
* Statistics
|
|
*/
|
|
unsigned long Interrupts;
|
|
unsigned long rxInterrupts;
|
|
unsigned long rxMissed;
|
|
unsigned long rxGiant;
|
|
unsigned long rxNonOctet;
|
|
unsigned long rxBadCRC;
|
|
unsigned long rxCollision;
|
|
|
|
unsigned long txInterrupts;
|
|
unsigned long txSingleCollision;
|
|
unsigned long txMultipleCollision;
|
|
unsigned long txCollision;
|
|
unsigned long txDeferred;
|
|
unsigned long txUnderrun;
|
|
unsigned long txLateCollision;
|
|
unsigned long txExcessiveCollision;
|
|
unsigned long txExcessiveDeferral;
|
|
unsigned long txLostCarrier;
|
|
unsigned long txRawWait;
|
|
};
|
|
SONIC_STATIC struct sonic_softc sonic_softc[NSONIC];
|
|
|
|
|
|
/*
|
|
******************************************************************
|
|
* *
|
|
* Debug Routines *
|
|
* *
|
|
******************************************************************
|
|
*/
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
|
|
void sonic_print_tx_descriptor(
|
|
TransmitDescriptorPointer_t tdp
|
|
)
|
|
{
|
|
printf( "TXD ==> %p", tdp );
|
|
printf( " pkt_config = 0x%04x", tdp->pkt_config & 0xffff);
|
|
printf( " pkt_size = 0x%04x\n", tdp->pkt_size & 0xffff );
|
|
printf( " frag_count = %d", tdp->frag_count & 0xffff );
|
|
/* could print all the fragments */
|
|
printf( " next = %p", tdp->next );
|
|
printf( " linkp = %p\n", tdp->linkp );
|
|
printf( " mbufp = %p", tdp->mbufp );
|
|
if ( tdp->mbufp )
|
|
printf( " mbufp->data = %p", mtod ( tdp->mbufp, void *) );
|
|
puts("");
|
|
}
|
|
|
|
void sonic_print_rx_descriptor(
|
|
ReceiveDescriptorPointer_t rdp
|
|
)
|
|
{
|
|
printf( "RXD ==> %p\n", rdp );
|
|
printf( " status = 0x%04x", rdp->status & 0xffff );
|
|
printf( " byte_count = 0x%04x\n", rdp->byte_count & 0xffff );
|
|
printf( " pkt = 0x%04x%04x", rdp->pkt_msw, rdp->pkt_lsw );
|
|
printf( " seq_no = %d", rdp->seq_no );
|
|
printf( " link = %d\n", rdp->link );
|
|
printf( " in_use = %d", rdp->in_use );
|
|
printf( " next = %p", rdp->next );
|
|
printf( " mbufp = %p", rdp->mbufp );
|
|
if ( rdp->mbufp )
|
|
printf( " mbufp->data = %p", mtod ( rdp->mbufp, void *) );
|
|
puts("");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
******************************************************************
|
|
* *
|
|
* Support Routines *
|
|
* *
|
|
******************************************************************
|
|
*/
|
|
|
|
static void sonic_enable_interrupts(
|
|
struct sonic_softc *sc,
|
|
uint32_t mask
|
|
)
|
|
{
|
|
void *rp = sc->sonic;
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable( level );
|
|
(*sc->write_register)(
|
|
rp,
|
|
SONIC_REG_IMR,
|
|
(*sc->read_register)(rp, SONIC_REG_IMR) | mask
|
|
);
|
|
rtems_interrupt_enable( level );
|
|
}
|
|
|
|
static void sonic_disable_interrupts(
|
|
struct sonic_softc *sc,
|
|
uint32_t mask
|
|
)
|
|
{
|
|
void *rp = sc->sonic;
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable( level );
|
|
(*sc->write_register)(
|
|
rp,
|
|
SONIC_REG_IMR,
|
|
(*sc->read_register)(rp, SONIC_REG_IMR) & ~mask
|
|
);
|
|
rtems_interrupt_enable( level );
|
|
}
|
|
|
|
static void sonic_clear_interrupts(
|
|
struct sonic_softc *sc,
|
|
uint32_t mask
|
|
)
|
|
{
|
|
void *rp = sc->sonic;
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable( level );
|
|
(*sc->write_register)( rp, SONIC_REG_ISR, mask);
|
|
rtems_interrupt_enable( level );
|
|
}
|
|
|
|
static void sonic_command(
|
|
struct sonic_softc *sc,
|
|
uint32_t mask
|
|
)
|
|
{
|
|
void *rp = sc->sonic;
|
|
rtems_interrupt_level level;
|
|
|
|
rtems_interrupt_disable( level );
|
|
(*sc->write_register)( rp, SONIC_REG_CR, mask);
|
|
rtems_interrupt_enable( level );
|
|
}
|
|
|
|
/*
|
|
* Allocate non-cacheable memory on a single 64k page.
|
|
* Very simple minded -- just keeps trying till the memory is on a single page.
|
|
*/
|
|
SONIC_STATIC void * sonic_allocate(unsigned int nbytes)
|
|
{
|
|
void *p;
|
|
unsigned long a1, a2;
|
|
|
|
for (;;) {
|
|
/*
|
|
* ===CACHE===
|
|
* Change malloc to malloc_noncacheable_guarded.
|
|
*/
|
|
p = malloc( nbytes, M_MBUF, M_NOWAIT );
|
|
if (p == NULL)
|
|
rtems_panic ("No memory!");
|
|
memset (p, '\0', nbytes);
|
|
a1 = (unsigned long)p;
|
|
a2 = a1 + nbytes - 1;
|
|
if ((a1 >> 16) == (a2 >> 16))
|
|
break;
|
|
}
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_ALLOCATE)
|
|
printf( "sonic_allocate %d bytes at %p\n", nbytes, p );
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* Shut down the interface.
|
|
*/
|
|
|
|
SONIC_STATIC void sonic_stop (struct sonic_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
|
|
/*
|
|
* Stop the transmitter and receiver.
|
|
*/
|
|
sonic_command(sc, CR_HTX | CR_RXDIS );
|
|
}
|
|
|
|
/*
|
|
* Show interface statistics
|
|
*/
|
|
SONIC_STATIC void sonic_stats (struct sonic_softc *sc)
|
|
{
|
|
printf (" Total Interrupts:%-8lu", sc->Interrupts);
|
|
printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
|
|
printf (" Giant:%-8lu", sc->rxGiant);
|
|
printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
|
|
printf (" Bad CRC:%-8lu", sc->rxBadCRC);
|
|
printf (" Collision:%-8lu", sc->rxCollision);
|
|
printf (" Missed:%-8lu\n", sc->rxMissed);
|
|
|
|
printf ( " Tx Interrupts:%-8lu", sc->txInterrupts);
|
|
printf ( " Deferred:%-8lu", sc->txDeferred);
|
|
printf (" Lost Carrier:%-8lu\n", sc->txLostCarrier);
|
|
printf ( "Single Collisions:%-8lu", sc->txSingleCollision);
|
|
printf ( "Multiple Collisions:%-8lu", sc->txMultipleCollision);
|
|
printf ("Excessive Collisions:%-8lu\n", sc->txExcessiveCollision);
|
|
printf ( " Total Collisions:%-8lu", sc->txCollision);
|
|
printf ( " Late Collision:%-8lu", sc->txLateCollision);
|
|
printf (" Underrun:%-8lu\n", sc->txUnderrun);
|
|
printf ( " Raw output wait:%-8lu\n", sc->txRawWait);
|
|
}
|
|
|
|
/*
|
|
******************************************************************
|
|
* *
|
|
* Interrupt Handler *
|
|
* *
|
|
******************************************************************
|
|
*/
|
|
|
|
SONIC_STATIC rtems_isr sonic_interrupt_handler (rtems_vector_number v)
|
|
{
|
|
struct sonic_softc *sc = sonic_softc;
|
|
uint32_t isr, imr;
|
|
void *rp;
|
|
|
|
#if (NSONIC > 1)
|
|
/*
|
|
* Find the device which requires service
|
|
*/
|
|
for (;;) {
|
|
if (sc->vector == v)
|
|
break;
|
|
if (++sc == &sonic[NSONIC])
|
|
return; /* Spurious interrupt? */
|
|
}
|
|
#endif /* NSONIC > 1 */
|
|
|
|
/*
|
|
* Get pointer to SONIC registers
|
|
*/
|
|
rp = sc->sonic;
|
|
|
|
sc->Interrupts++;
|
|
|
|
isr = (*sc->read_register)( rp, SONIC_REG_ISR );
|
|
imr = (*sc->read_register)( rp, SONIC_REG_IMR );
|
|
|
|
/*
|
|
* Packet received or receive buffer area exceeded?
|
|
*/
|
|
if (imr & isr & (IMR_PRXEN | IMR_RBAEEN)) {
|
|
imr &= ~(IMR_PRXEN | IMR_RBAEEN);
|
|
sc->rxInterrupts++;
|
|
rtems_bsdnet_event_send (sc->rxDaemonTid, INTERRUPT_EVENT);
|
|
(*sc->write_register)( rp, SONIC_REG_IMR, imr );
|
|
(*sc->write_register)( rp, SONIC_REG_ISR, isr & ISR_PKTRX );
|
|
}
|
|
|
|
/*
|
|
* Packet started, transmitter done or transmitter error?
|
|
* TX interrupts only occur after an error or when all TDA's are
|
|
* exhausted and we are waiting for buffer to come free.
|
|
*/
|
|
if (imr & isr & (IMR_PINTEN | IMR_TXEREN)) {
|
|
sc->txInterrupts++;
|
|
rtems_bsdnet_event_send (sc->txDaemonTid, INTERRUPT_EVENT);
|
|
(*sc->write_register)( rp, SONIC_REG_ISR, ISR_PINT | ISR_TXDN | ISR_TXER );
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
******************************************************************
|
|
* *
|
|
* Transmitter Routines *
|
|
* *
|
|
******************************************************************
|
|
*/
|
|
|
|
/*
|
|
* Soak up transmit descriptors that have been sent.
|
|
*/
|
|
|
|
SONIC_STATIC void sonic_retire_tda (struct sonic_softc *sc)
|
|
{
|
|
uint16_t status;
|
|
unsigned int collisions;
|
|
struct mbuf *m, *n;
|
|
|
|
/*
|
|
* Repeat for all completed transmit descriptors.
|
|
*/
|
|
while ((status = sc->tdaTail->status) != 0) {
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
|
|
printf( "retire TDA %p (0x%04x)\n", sc->tdaTail, status );
|
|
#endif
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
|
|
/*
|
|
* If there is an error that was not a collision,
|
|
* then someone may want to see it.
|
|
*/
|
|
|
|
if ( (status & ~(TDA_STATUS_COLLISION_MASK|TDA_STATUS_DEF)) != 0x0001 )
|
|
printf( "ERROR: retire TDA %p (0x%08x)\n",
|
|
sc->tdaTail, sc->tdaTail->status );
|
|
#endif
|
|
|
|
/*
|
|
* Check for errors which stop the transmitter.
|
|
*/
|
|
if (status & (TDA_STATUS_EXD |
|
|
TDA_STATUS_EXC |
|
|
TDA_STATUS_FU |
|
|
TDA_STATUS_BCM)) {
|
|
/*
|
|
* Restart the transmitter if there are
|
|
* packets waiting to go.
|
|
*/
|
|
uint16_t link;
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
|
|
printf("restarting sonic after error\n");
|
|
#endif
|
|
|
|
link = *(sc->tdaTail->linkp);
|
|
|
|
if ((link & TDA_LINK_EOL) == 0) {
|
|
void *rp = sc->sonic;
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_CTDA, link );
|
|
sonic_command(sc, CR_TXP );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update network statistics
|
|
*/
|
|
collisions = (status & TDA_STATUS_COLLISION_MASK) >> TDA_STATUS_COLLISION_SHIFT;
|
|
if (collisions) {
|
|
if (collisions == 1)
|
|
sc->txSingleCollision++;
|
|
else
|
|
sc->txMultipleCollision++;
|
|
sc->txCollision += collisions;
|
|
}
|
|
if (status & TDA_STATUS_EXC)
|
|
sc->txExcessiveCollision++;
|
|
if (status & TDA_STATUS_OWC)
|
|
sc->txLateCollision++;
|
|
if (status & TDA_STATUS_EXD)
|
|
sc->txExcessiveDeferral++;
|
|
if (status & TDA_STATUS_DEF)
|
|
sc->txDeferred++;
|
|
if (status & TDA_STATUS_FU)
|
|
sc->txUnderrun++;
|
|
if (status & TDA_STATUS_CRSL)
|
|
sc->txLostCarrier++;
|
|
|
|
/*
|
|
* Free the packet and reset a couple of fields
|
|
*/
|
|
m = sc->tdaTail->mbufp;
|
|
while ( m ) {
|
|
MFREE(m, n);
|
|
m = n;
|
|
}
|
|
|
|
/*
|
|
sc->tdaTail->frag[0].frag_link = LSW(sc->tdaTail->link_pad);
|
|
sc->tdaTail->frag_count = 0;
|
|
*/
|
|
sc->tdaTail->status = 0;
|
|
|
|
/*
|
|
* Move to the next transmit descriptor
|
|
*/
|
|
sc->tdaTail = sc->tdaTail->next;
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
|
|
printf( "next TDA %p\n", sc->tdaTail );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send packet
|
|
*/
|
|
SONIC_STATIC void sonic_sendpacket (struct ifnet *ifp, struct mbuf *m)
|
|
{
|
|
struct sonic_softc *sc = ifp->if_softc;
|
|
struct mbuf *l = NULL;
|
|
TransmitDescriptorPointer_t tdp;
|
|
volatile struct TransmitDescriptorFragLink *fp;
|
|
unsigned int packetSize;
|
|
int i;
|
|
rtems_event_set events;
|
|
static char padBuf[64];
|
|
|
|
/* printf( "sonic_sendpacket %p\n", m ); */
|
|
|
|
|
|
/*
|
|
* Wait for transmit descriptor to become available. Only retire TDA's
|
|
* if there are no more free buffers to minimize TX latency. Retire TDA'a
|
|
* on the way out.
|
|
*/
|
|
|
|
while (sc->tdaHead->next->status != 0) {
|
|
|
|
/*
|
|
* Free up transmit descriptors
|
|
*/
|
|
sonic_retire_tda (sc);
|
|
|
|
if (sc->tdaHead->next->status == 0)
|
|
break;
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
|
|
printf("blocking until TDAs are available\n");
|
|
#endif
|
|
/*
|
|
* Enable PINT interrupts.
|
|
sonic_clear_interrupts( sc, ISR_PINT );
|
|
sonic_enable_interrupts( sc, IMR_PINTEN );
|
|
*/
|
|
|
|
/*
|
|
* Wait for PINT TX interrupt. Every fourth TX buffer will raise PINT.
|
|
*/
|
|
rtems_bsdnet_event_receive (INTERRUPT_EVENT,
|
|
RTEMS_WAIT|RTEMS_EVENT_ANY,
|
|
RTEMS_NO_TIMEOUT,
|
|
&events);
|
|
sonic_disable_interrupts( sc, IMR_PINTEN );
|
|
sonic_retire_tda (sc);
|
|
}
|
|
|
|
/*
|
|
* Fill in the transmit descriptor fragment descriptors.
|
|
* ===CACHE===
|
|
* If data cache is operating in write-back mode, flush cached
|
|
* data to memory.
|
|
*/
|
|
tdp = sc->tdaHead->next;
|
|
tdp->mbufp = m;
|
|
packetSize = 0;
|
|
fp = tdp->frag;
|
|
for (i = 0 ; i < MAXIMUM_FRAGS_PER_DESCRIPTOR ; i++, fp++) {
|
|
/*
|
|
* Throw away empty mbufs
|
|
*/
|
|
if (m->m_len) {
|
|
void *p = mtod (m, void *);
|
|
fp->frag_lsw = LSW(p);
|
|
fp->frag_msw = MSW(p);
|
|
fp->frag_size = m->m_len;
|
|
packetSize += m->m_len;
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
|
|
printf( "fp %p 0x%04x%04x %d=%d .. %d\n",
|
|
fp, fp->frag_msw, fp->frag_lsw, fp->frag_size, m->m_len, packetSize );
|
|
#endif
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_TX_MBUFS)
|
|
rtems_print_buffer(
|
|
p,
|
|
(fp->frag_size > MAXIMUM_FRAME_SIZE) ? MAXIMUM_FRAME_SIZE : fp->frag_size
|
|
);
|
|
#endif
|
|
l = m;
|
|
m = m->m_next;
|
|
}
|
|
else {
|
|
struct mbuf *n;
|
|
MFREE (m, n);
|
|
m = n;
|
|
if (l != NULL)
|
|
l->m_next = m;
|
|
}
|
|
/*
|
|
* Break out of the loop if this mbuf is the last in the frame.
|
|
*/
|
|
if (m == NULL)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Pad short packets.
|
|
*/
|
|
if ((packetSize < 64) && (i < MAXIMUM_FRAGS_PER_DESCRIPTOR)) {
|
|
int padSize = 64 - packetSize;
|
|
fp++;
|
|
fp->frag_lsw = LSW(padBuf);
|
|
fp->frag_msw = MSW(padBuf);
|
|
fp->frag_size = padSize;
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
|
|
printf( "PAD fp %p 0x%04x%04x %d\n",
|
|
fp, fp->frag_msw, fp->frag_lsw, fp->frag_size );
|
|
#endif
|
|
packetSize += padSize;
|
|
i++;
|
|
}
|
|
|
|
/*
|
|
* Fill Transmit Descriptor
|
|
*/
|
|
tdp->pkt_size = packetSize;
|
|
tdp->frag_count = i + 1;
|
|
tdp->status = 0;
|
|
|
|
/*
|
|
* Chain onto list and start transmission.
|
|
*/
|
|
|
|
tdp->linkp = &(fp+1)->frag_link;
|
|
*tdp->linkp = LSW(tdp->next) | TDA_LINK_EOL;
|
|
if ( sc->tdaHead->frag_count )
|
|
*sc->tdaHead->linkp &= ~TDA_LINK_EOL;
|
|
sc->tdaHead = tdp;
|
|
|
|
/* Start transmission */
|
|
|
|
sonic_command(sc, CR_TXP );
|
|
|
|
/*
|
|
* Free up transmit descriptors on the way out.
|
|
*/
|
|
sonic_retire_tda (sc);
|
|
}
|
|
|
|
/*
|
|
* Driver transmit daemon
|
|
*/
|
|
SONIC_STATIC void sonic_txDaemon (void *arg)
|
|
{
|
|
struct sonic_softc *sc = (struct sonic_softc *)arg;
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
struct mbuf *m;
|
|
rtems_event_set events;
|
|
|
|
for (;;) {
|
|
/*
|
|
* Wait for packet
|
|
*/
|
|
rtems_bsdnet_event_receive (
|
|
START_TRANSMIT_EVENT,
|
|
RTEMS_EVENT_ANY | RTEMS_WAIT,
|
|
RTEMS_NO_TIMEOUT,
|
|
&events
|
|
);
|
|
|
|
/*
|
|
* Send packets till queue is empty
|
|
*/
|
|
for (;;) {
|
|
/*
|
|
* Get the next mbuf chain to transmit.
|
|
*/
|
|
IF_DEQUEUE(&ifp->if_snd, m);
|
|
if (!m)
|
|
break;
|
|
sonic_sendpacket (ifp, m);
|
|
}
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
******************************************************************
|
|
* *
|
|
* Receiver Routines *
|
|
* *
|
|
******************************************************************
|
|
*/
|
|
|
|
/*
|
|
* Wait for SONIC to hand over a Receive Descriptor.
|
|
*/
|
|
|
|
SONIC_STATIC void sonic_rda_wait(
|
|
struct sonic_softc *sc,
|
|
ReceiveDescriptorPointer_t rdp
|
|
)
|
|
{
|
|
int i;
|
|
void *rp = sc->sonic;
|
|
rtems_event_set events;
|
|
|
|
/*
|
|
* Wait for Receive Descriptor.
|
|
* The order of the tests is very important.
|
|
* The RDA is checked after RBAE is detected. This ensures that
|
|
* the driver processes all RDA entries before reusing the RRA
|
|
* entry holding the giant packet.
|
|
* The event wait is done after the RDA and RBAE checks. This
|
|
* catches the possibility that a Receive Descriptor became ready
|
|
* between the call to this function and the clearing of the
|
|
* interrupt status register bit.
|
|
*/
|
|
for (;;) {
|
|
/*
|
|
* Has a giant packet arrived?
|
|
* The National DP83932C data sheet is very vague on what
|
|
* happens under this condition. The description of the
|
|
* Interrupt Status Register (Section 4.3.6) states,
|
|
* ``Reception is aborted and the SONIC fetches the next
|
|
* available resource descriptors in the RRA. The buffer
|
|
* space is not re-used and an RDA is not setup for the
|
|
* truncated packet.''
|
|
* I take ``Reception is aborted'' to mean that the RXEN
|
|
* bit in the Command Register is cleared and must be set
|
|
* by the driver to begin reception again.
|
|
* Unfortunately, an alternative interpretation could be
|
|
* that only reception of the current packet is aborted.
|
|
* This would be more difficult to recover from....
|
|
*/
|
|
if ((*sc->read_register)( rp, SONIC_REG_ISR ) & ISR_RBAE) {
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
|
|
printf( "ERROR: looks like a giant packet -- RBAE\n" );
|
|
#endif
|
|
|
|
/*
|
|
* One more check to soak up any Receive Descriptors
|
|
* that may already have been handed back to the driver.
|
|
*/
|
|
if (rdp->in_use == RDA_IN_USE) {
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
|
|
printf( "ERROR: nope just an RBAE\n" );
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Check my interpretation of the SONIC manual.
|
|
*/
|
|
if ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_RXEN)
|
|
rtems_panic ("SONIC RBAE/RXEN");
|
|
|
|
/*
|
|
* Update statistics
|
|
*/
|
|
sc->rxGiant++;
|
|
|
|
/*
|
|
* Reuse receive buffer.
|
|
* Again, the manual is subject to interpretation. The
|
|
* RRP register is described as, `the lower address of
|
|
* the next descriptor the SONIC will read.''
|
|
* Since, acording to the ISR/RBAE notes, the SONIC has
|
|
* ``fetched the next available resource descriptor in
|
|
* the RRA'', I interpret this to mean that that the
|
|
* driver has to move the RRP back *two* entries to
|
|
* reuse the receive buffer holding the giant packet.
|
|
*/
|
|
for (i = 0; i < 2; ++i) {
|
|
uint32_t rrp = (*sc->read_register)( rp, SONIC_REG_RRP );
|
|
const uint32_t rsa = (*sc->read_register)( rp, SONIC_REG_RSA );
|
|
|
|
if (rrp == rsa) {
|
|
const uint32_t rea = (*sc->read_register)( rp, SONIC_REG_REA );
|
|
(*sc->write_register)( rp, SONIC_REG_RRP, rea );
|
|
}
|
|
|
|
rrp = (*sc->read_register)( rp, SONIC_REG_RRP );
|
|
(*sc->write_register)( rp, SONIC_REG_RRP, rrp - sizeof(ReceiveResource_t) );
|
|
}
|
|
|
|
/*
|
|
* Restart reception
|
|
*/
|
|
sonic_clear_interrupts( sc, ISR_RBAE );
|
|
sonic_command( sc, CR_RXEN );
|
|
}
|
|
|
|
/*
|
|
* Has Receive Descriptor become available?
|
|
*/
|
|
if (rdp->in_use == RDA_IN_USE)
|
|
break;
|
|
|
|
/*
|
|
* Enable interrupts.
|
|
*/
|
|
sonic_enable_interrupts( sc, (IMR_PRXEN | IMR_RBAEEN) );
|
|
|
|
/*
|
|
* Wait for interrupt.
|
|
*/
|
|
rtems_bsdnet_event_receive(
|
|
INTERRUPT_EVENT,
|
|
RTEMS_WAIT|RTEMS_EVENT_ANY,
|
|
RTEMS_NO_TIMEOUT,
|
|
&events
|
|
);
|
|
}
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
|
|
printf( "RDA %p\n", rdp );
|
|
#endif
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
|
|
if (rdp->status & 0x000E)
|
|
printf( "ERROR: RDA %p (0x%04x)\n", rdp, rdp->status );
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef CPU_U32_FIX
|
|
|
|
/*
|
|
* Routine to align the received packet so that the ip header
|
|
* is on a 32-bit boundary. Necessary for cpu's that do not
|
|
* allow unaligned loads and stores and when the 32-bit DMA
|
|
* mode is used.
|
|
*
|
|
* Transfers are done on word basis to avoid possibly slow byte
|
|
* and half-word writes.
|
|
*/
|
|
|
|
void ipalign(struct mbuf *m)
|
|
{
|
|
unsigned int *first, *last, data;
|
|
unsigned int tmp = 0;
|
|
|
|
if ((((int) m->m_data) & 2) && (m->m_len)) {
|
|
last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3);
|
|
first = (unsigned int *) (((int) m->m_data) & ~3);
|
|
tmp = *first << 16;
|
|
first++;
|
|
do {
|
|
data = *first;
|
|
*first = tmp | (data >> 16);
|
|
tmp = data << 16;
|
|
first++;
|
|
} while (first <= last);
|
|
|
|
m->m_data = (caddr_t)(((int) m->m_data) + 2);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* SONIC reader task
|
|
*/
|
|
SONIC_STATIC void sonic_rxDaemon (void *arg)
|
|
{
|
|
struct sonic_softc *sc = (struct sonic_softc *)arg;
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
void *rp = sc->sonic;
|
|
struct mbuf *m;
|
|
uint16_t status;
|
|
ReceiveDescriptorPointer_t rdp;
|
|
ReceiveResourcePointer_t rwp, rea;
|
|
uint16_t newMissedTally, oldMissedTally;
|
|
|
|
rwp = sc->rsa;
|
|
rea = sc->rea;
|
|
rdp = sc->rda;
|
|
|
|
/*
|
|
* Start the receiver
|
|
*/
|
|
oldMissedTally = (*sc->read_register)( rp, SONIC_REG_MPT );
|
|
|
|
/*
|
|
* Input packet handling loop
|
|
*/
|
|
for (;;) {
|
|
/*
|
|
* Wait till SONIC supplies a Receive Descriptor.
|
|
*/
|
|
if (rdp->in_use == RDA_FREE) {
|
|
sonic_rda_wait (sc, rdp);
|
|
}
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
|
|
printf( "Incoming packet %p status=0x%04x\n", rdp, rdp->status );
|
|
#endif
|
|
|
|
/*
|
|
* Check that packet is valid
|
|
*/
|
|
status = rdp->status;
|
|
if (status & RDA_STATUS_PRX) {
|
|
struct ether_header *eh;
|
|
void *p;
|
|
|
|
/*
|
|
* Pass the packet up the chain.
|
|
* The mbuf count is reduced to remove
|
|
* the frame check sequence at the end
|
|
* of the packet.
|
|
* ===CACHE===
|
|
* Invalidate cache entries for this memory.
|
|
*/
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
|
|
sonic_print_rx_descriptor( rdp );
|
|
if ((LSW(rdp->mbufp->m_data) != rdp->pkt_lsw)
|
|
|| (MSW(rdp->mbufp->m_data) != rdp->pkt_msw))
|
|
printf ("SONIC RDA/RRA %p, %08x\n",rdp->mbufp->m_data,(rdp->pkt_msw << 16) |
|
|
(rdp->pkt_lsw & 0x0ffff));
|
|
#endif
|
|
rdp->byte_count &= 0x0ffff; /* ERC32 pollutes msb of byte_count */
|
|
m = rdp->mbufp;
|
|
m->m_len = m->m_pkthdr.len = rdp->byte_count -
|
|
sizeof(uint32_t) -
|
|
sizeof(struct ether_header);
|
|
eh = mtod (m, struct ether_header *);
|
|
m->m_data += sizeof(struct ether_header);
|
|
|
|
#ifdef CPU_U32_FIX
|
|
ipalign(m); /* Align packet on 32-bit boundary */
|
|
#endif
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_RX_MBUFS)
|
|
rtems_print_buffer( (void *) eh, sizeof(struct ether_header) );
|
|
rtems_print_buffer( (void *) m, 96 /* m->m_len*/ );
|
|
#endif
|
|
|
|
/* printf( "ether_input %p\n", m ); */
|
|
/*
|
|
printf( "pkt %p, seq %04x, mbuf %p, m_data %p\n", rdp, rdp->seq_no, m, m->m_data );
|
|
printf( "%u, %u\n", ((int*)m->m_data)[6], ((int*)m->m_data)[7]);
|
|
*/
|
|
ether_input (ifp, eh, m);
|
|
/*
|
|
*/
|
|
|
|
/*
|
|
* Sanity check that Receive Resource Area is
|
|
* still in sync with Receive Descriptor Area
|
|
* The buffer reported in the Receive Descriptor
|
|
* should be the same as the buffer in the Receive
|
|
* Resource we are about to reuse.
|
|
*/
|
|
/* XXX figure out whether this is valid or not */
|
|
#if 0
|
|
if ((LSW(p) != rwp->buff_ptr_lsw)
|
|
|| (MSW(p) != rwp->buff_ptr_msw))
|
|
rtems_panic ("SONIC RDA/RRA");
|
|
#endif
|
|
|
|
/*
|
|
* Allocate a new mbuf.
|
|
*/
|
|
|
|
MGETHDR (m, M_WAIT, MT_DATA);
|
|
MCLGET (m, M_WAIT);
|
|
m->m_pkthdr.rcvif = ifp;
|
|
rdp->mbufp = m;
|
|
p = mtod (m, void *);
|
|
|
|
/*
|
|
* Reuse Receive Resource.
|
|
*/
|
|
|
|
rwp->buff_ptr_lsw = LSW(p);
|
|
rwp->buff_ptr_msw = MSW(p);
|
|
rwp->buff_wc_lsw = RBUF_WC;
|
|
rwp->buff_wc_msw = 0;
|
|
rwp++;
|
|
|
|
if (rwp == rea) {
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
|
|
printf( "Wrapping RWP from %p to %p\n", rwp, sc->rsa );
|
|
#endif
|
|
rwp = sc->rsa;
|
|
}
|
|
(*sc->write_register)( rp, SONIC_REG_RWP , LSW(rwp) );
|
|
|
|
/*
|
|
* Tell the SONIC to reread the RRA.
|
|
*/
|
|
if ((*sc->read_register)( rp, SONIC_REG_ISR ) & ISR_RBE)
|
|
sonic_clear_interrupts( sc, ISR_RBE );
|
|
}
|
|
else {
|
|
if (status & RDA_STATUS_COL)
|
|
sc->rxCollision++;
|
|
if (status & RDA_STATUS_FAER)
|
|
sc->rxNonOctet++;
|
|
else if (status & RDA_STATUS_CRCR)
|
|
sc->rxBadCRC++;
|
|
}
|
|
|
|
/*
|
|
* Count missed packets
|
|
*/
|
|
newMissedTally = (*sc->read_register)( rp, SONIC_REG_MPT );
|
|
if (newMissedTally != oldMissedTally) {
|
|
sc->rxMissed += (newMissedTally - oldMissedTally) & 0xFFFF;
|
|
newMissedTally = oldMissedTally;
|
|
}
|
|
|
|
/*
|
|
* Move to next receive descriptor and update EOL
|
|
*/
|
|
|
|
rdp->link |= RDA_LINK_EOL;
|
|
rdp->in_use = RDA_FREE;
|
|
sc->rdp_last->link &= ~RDA_LINK_EOL;
|
|
sc->rdp_last = rdp;
|
|
rdp = rdp->next;
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
******************************************************************
|
|
* *
|
|
* Initialization Routines *
|
|
* *
|
|
******************************************************************
|
|
*/
|
|
|
|
/*
|
|
* Initialize the SONIC hardware
|
|
*/
|
|
SONIC_STATIC void sonic_initialize_hardware(struct sonic_softc *sc)
|
|
{
|
|
void *rp = sc->sonic;
|
|
int i;
|
|
unsigned char *hwaddr;
|
|
TransmitDescriptorPointer_t tdp;
|
|
ReceiveDescriptorPointer_t ordp, rdp;
|
|
ReceiveResourcePointer_t rwp;
|
|
struct mbuf *m;
|
|
void *p;
|
|
CamDescriptorPointer_t cdp;
|
|
|
|
/*
|
|
* The Revision B SONIC has a horrible bug known as the "Zero
|
|
* Length Packet bug". The initial board used to develop this
|
|
* driver had a newer revision of the SONIC so there was no reason
|
|
* to check for this. If you have the Revision B SONIC chip, then
|
|
* you need to add some code to the RX path to handle this weirdness.
|
|
*/
|
|
|
|
if ( (*sc->read_register)( rp, SONIC_REG_SR ) <= SONIC_REVISION_B ) {
|
|
rtems_fatal_error_occurred( 0x0BADF00D ); /* don't eat this part :) */
|
|
}
|
|
|
|
/*
|
|
* Set up circular linked list in Transmit Descriptor Area.
|
|
* Use the PINT bit in the transmit configuration field to
|
|
* request an interrupt on every other transmitted packet.
|
|
*
|
|
* NOTE: sonic_allocate() zeroes all of the memory allocated.
|
|
*/
|
|
|
|
sc->tdaTail = sonic_allocate(sc->tdaCount * sizeof *tdp);
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
|
|
printf( "tdaTail = %p\n", sc->tdaTail );
|
|
#endif
|
|
tdp = sc->tdaTail;
|
|
for (i = 0 ; i < sc->tdaCount ; i++) {
|
|
/*
|
|
* Start off with the table of outstanding mbuf's
|
|
*/
|
|
|
|
/*
|
|
* status, pkt_config, pkt_size, and all fragment fields
|
|
* are set to zero by sonic_allocate.
|
|
*/
|
|
|
|
/* XXX not used by the BSD drivers
|
|
tdp->frag[0].frag_link = LSW(tdp + 1);
|
|
*/
|
|
if (i & 3)
|
|
tdp->pkt_config = TDA_CONFIG_PINT;
|
|
|
|
tdp->status = 0;
|
|
tdp->frag_count = 0;
|
|
tdp->link_pad = LSW(tdp + 1) | TDA_LINK_EOL;
|
|
tdp->linkp = &((tdp + 1)->frag[0].frag_link);
|
|
tdp->next = (TransmitDescriptor_t *)(tdp + 1);
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
|
|
sonic_print_tx_descriptor( tdp );
|
|
#endif
|
|
tdp++;
|
|
}
|
|
tdp--;
|
|
sc->tdaHead = tdp;
|
|
tdp->link_pad = LSW(sc->tdaTail) | TDA_LINK_EOL;
|
|
tdp->next = (TransmitDescriptor_t *)sc->tdaTail;
|
|
tdp->linkp = &sc->tdaTail->frag[0].frag_link;
|
|
|
|
/*
|
|
* Set up circular linked list in Receive Descriptor Area.
|
|
* Leaves sc->rda pointing at the `beginning' of the list.
|
|
*
|
|
* NOTE: The RDA and CDP must have the same MSW for their addresses.
|
|
*/
|
|
|
|
sc->rda = sonic_allocate(
|
|
(sc->rdaCount * sizeof(ReceiveDescriptor_t)) +
|
|
sizeof(CamDescriptor_t) );
|
|
sc->cdp = (CamDescriptorPointer_t) ((unsigned char *)sc->rda +
|
|
(sc->rdaCount * sizeof(ReceiveDescriptor_t)));
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
|
|
printf( "rda area = %p\n", sc->rda );
|
|
printf( "cdp area = %p\n", sc->cdp );
|
|
#endif
|
|
|
|
ordp = rdp = sc->rda;
|
|
for (i = 0 ; i < sc->rdaCount ; i++) {
|
|
/*
|
|
* status, byte_count, pkt_ptr0, pkt_ptr1, and seq_no are set
|
|
* to zero by sonic_allocate.
|
|
*/
|
|
rdp->link = LSW(rdp + 1);
|
|
rdp->in_use = RDA_FREE;
|
|
rdp->next = (ReceiveDescriptor_t *)(rdp + 1);
|
|
ordp = rdp;
|
|
rdp++;
|
|
}
|
|
/*
|
|
* Link the last desriptor to the 1st one and mark it as the end
|
|
* of the list.
|
|
*/
|
|
ordp->next = sc->rda;
|
|
ordp->link = LSW(sc->rda) | RDA_LINK_EOL;
|
|
sc->rdp_last = ordp;
|
|
|
|
/*
|
|
* Allocate the receive resource area.
|
|
* In accordance with National Application Note 746, make the
|
|
* receive resource area bigger than the receive descriptor area.
|
|
* This has the useful side effect of making the receive resource
|
|
* area big enough to hold the CAM descriptor area.
|
|
*/
|
|
|
|
sc->rsa = sonic_allocate((sc->rdaCount + RRA_EXTRA_COUNT) * sizeof *sc->rsa);
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
|
|
printf( "rsa area = %p\n", sc->rsa );
|
|
#endif
|
|
|
|
/*
|
|
* Set up list in Receive Resource Area.
|
|
* Allocate space for incoming packets.
|
|
*/
|
|
|
|
rwp = sc->rsa;
|
|
for (i = 0 ; i < (sc->rdaCount + RRA_EXTRA_COUNT) ; i++, rwp++) {
|
|
|
|
/*
|
|
* Allocate memory for buffer.
|
|
* Place a pointer to the mbuf at the beginning of the buffer
|
|
* so we can find the mbuf when the SONIC returns the buffer
|
|
* to the driver.
|
|
*/
|
|
|
|
MGETHDR (m, M_WAIT, MT_DATA);
|
|
MCLGET (m, M_WAIT);
|
|
m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
|
|
sc->rda[i].mbufp = m;
|
|
|
|
p = mtod (m, void *);
|
|
|
|
/*
|
|
* Set up RRA entry
|
|
*/
|
|
rwp->buff_ptr_lsw = LSW(p);
|
|
rwp->buff_ptr_msw = MSW(p);
|
|
rwp->buff_wc_lsw = RBUF_WC;
|
|
rwp->buff_wc_msw = 0;
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
|
|
sonic_print_rx_descriptor( &sc->rda[i] );
|
|
#endif
|
|
}
|
|
sc->rea = rwp;
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
|
|
printf( "rea area = %p\n", sc->rea );
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Issue a software reset.
|
|
*/
|
|
(*sc->write_register)( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );
|
|
|
|
/*
|
|
* Set up data configuration registers.
|
|
*/
|
|
(*sc->write_register)( rp, SONIC_REG_DCR, sc->dcr_value );
|
|
(*sc->write_register)( rp, SONIC_REG_DCR2, sc->dc2_value );
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_CR, CR_STP | CR_RXDIS | CR_HTX );
|
|
|
|
/*
|
|
* Mask all interrupts
|
|
*/
|
|
(*sc->write_register)( rp, SONIC_REG_IMR, 0x0 ); /* XXX was backwards */
|
|
|
|
/*
|
|
* Clear outstanding interrupts.
|
|
*/
|
|
(*sc->write_register)( rp, SONIC_REG_ISR, 0x7FFF );
|
|
|
|
/*
|
|
* Clear the tally counters
|
|
*/
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_CRCT, 0xFFFF );
|
|
(*sc->write_register)( rp, SONIC_REG_FAET, 0xFFFF );
|
|
(*sc->write_register)( rp, SONIC_REG_MPT, 0xFFFF );
|
|
(*sc->write_register)( rp, SONIC_REG_RSC, 0 );
|
|
|
|
/*
|
|
* Set the Receiver mode
|
|
*
|
|
* Enable/disable reception of broadcast packets
|
|
*/
|
|
|
|
if (sc->acceptBroadcast)
|
|
(*sc->write_register)( rp, SONIC_REG_RCR, RCR_BRD );
|
|
else
|
|
(*sc->write_register)( rp, SONIC_REG_RCR, 0 );
|
|
|
|
/*
|
|
* Set up Resource Area pointers
|
|
*/
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_URRA, MSW(sc->rsa) );
|
|
(*sc->write_register)( rp, SONIC_REG_RSA, LSW(sc->rsa) );
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_REA, LSW(sc->rea) );
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_RRP, LSW(sc->rsa) );
|
|
(*sc->write_register)( rp, SONIC_REG_RWP, LSW(sc->rsa) ); /* XXX was rea */
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_URDA, MSW(sc->rda) );
|
|
(*sc->write_register)( rp, SONIC_REG_CRDA, LSW(sc->rda) );
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_UTDA, MSW(sc->tdaTail) );
|
|
(*sc->write_register)( rp, SONIC_REG_CTDA, LSW(sc->tdaTail) );
|
|
|
|
/*
|
|
* Set End Of Buffer Count register to the value recommended
|
|
* in Note 1 of Section 3.4.4.4 of the SONIC data sheet.
|
|
*/
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_EOBC, RBUF_WC - 2 );
|
|
|
|
/*
|
|
* Issue the load RRA command
|
|
*/
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_CR, CR_RRRA );
|
|
while ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_RRRA)
|
|
continue;
|
|
|
|
/*
|
|
* Remove device reset
|
|
*/
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_CR, 0 );
|
|
|
|
/*
|
|
* Set up the SONIC CAM with our hardware address.
|
|
*/
|
|
|
|
hwaddr = sc->arpcom.ac_enaddr;
|
|
cdp = sc->cdp;
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
|
|
printf( "hwaddr: %2x:%2x:%2x:%2x:%2x:%2x\n",
|
|
hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] );
|
|
#endif
|
|
|
|
cdp->cep = 0; /* Fill first and only entry in CAM */
|
|
cdp->cap0 = hwaddr[1] << 8 | hwaddr[0];
|
|
cdp->cap1 = hwaddr[3] << 8 | hwaddr[2];
|
|
cdp->cap2 = hwaddr[5] << 8 | hwaddr[4];
|
|
cdp->ce = 0x0001; /* Enable first entry in CAM */
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_CDC, 1 ); /* 1 entry in CDA */
|
|
(*sc->write_register)( rp, SONIC_REG_CDP, LSW(cdp) );
|
|
(*sc->write_register)( rp, SONIC_REG_CR, CR_LCAM ); /* Load the CAM */
|
|
|
|
while ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_LCAM)
|
|
continue;
|
|
|
|
/*
|
|
* Verify that CAM was properly loaded.
|
|
*/
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
|
|
(*sc->write_register)( rp, SONIC_REG_CEP, 0 ); /* Select first entry in CAM */
|
|
printf ("Loaded Ethernet address into SONIC CAM.\n"
|
|
" Wrote %04x%04x%04x - %#x\n"
|
|
" Read %04x%04x%04x - %#x\n",
|
|
cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
|
|
(*sc->read_register)( rp, SONIC_REG_CAP2 ),
|
|
(*sc->read_register)( rp, SONIC_REG_CAP1 ),
|
|
(*sc->read_register)( rp, SONIC_REG_CAP0 ),
|
|
(*sc->read_register)( rp, SONIC_REG_CE ));
|
|
|
|
(*sc->write_register)( rp, SONIC_REG_CEP, 0 ); /* Select first entry in CAM */
|
|
if (((*sc->read_register)( rp, SONIC_REG_CAP2 ) != cdp->cap2)
|
|
|| ((*sc->read_register)( rp, SONIC_REG_CAP1 ) != cdp->cap1)
|
|
|| ((*sc->read_register)( rp, SONIC_REG_CAP0 ) != cdp->cap0)
|
|
|| ((*sc->read_register)( rp, SONIC_REG_CE ) != cdp->ce)) {
|
|
printf ("Failed to load Ethernet address into SONIC CAM.\n"
|
|
" Wrote %04x%04x%04x - %#x\n"
|
|
" Read %04x%04x%04x - %#x\n",
|
|
cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
|
|
(*sc->read_register)( rp, SONIC_REG_CAP2 ),
|
|
(*sc->read_register)( rp, SONIC_REG_CAP1 ),
|
|
(*sc->read_register)( rp, SONIC_REG_CAP0 ),
|
|
(*sc->read_register)( rp, SONIC_REG_CE ));
|
|
rtems_panic ("SONIC LCAM");
|
|
}
|
|
#endif
|
|
|
|
(*sc->write_register)(rp, SONIC_REG_CR, /* CR_TXP | */CR_RXEN | CR_STP);
|
|
|
|
/*
|
|
* Attach SONIC interrupt handler
|
|
*/
|
|
/* XXX
|
|
(*sc->write_register)( rp, SONIC_REG_IMR, 0 );
|
|
*/
|
|
|
|
/* Ignore returned old handler */
|
|
(void) set_vector(sonic_interrupt_handler, sc->vector, 1);
|
|
|
|
/*
|
|
* Remainder of hardware initialization is
|
|
* done by the receive and transmit daemons.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Send packet (caller provides header).
|
|
*/
|
|
|
|
SONIC_STATIC void sonic_start(struct ifnet *ifp)
|
|
{
|
|
struct sonic_softc *sc = ifp->if_softc;
|
|
|
|
rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT);
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
}
|
|
|
|
/*
|
|
* Initialize and start the device
|
|
*/
|
|
|
|
SONIC_STATIC void sonic_init (void *arg)
|
|
{
|
|
struct sonic_softc *sc = arg;
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
void *rp = sc->sonic;
|
|
int rcr;
|
|
|
|
if (sc->txDaemonTid == 0) {
|
|
|
|
/*
|
|
* Set up SONIC hardware
|
|
*/
|
|
sonic_initialize_hardware (sc);
|
|
|
|
/*
|
|
* Start driver tasks
|
|
*/
|
|
sc->rxDaemonTid = rtems_bsdnet_newproc ("SNrx", 4096, sonic_rxDaemon, sc);
|
|
sc->txDaemonTid = rtems_bsdnet_newproc ("SNtx", 4096, sonic_txDaemon, sc);
|
|
}
|
|
|
|
/*
|
|
* Set flags appropriately
|
|
*/
|
|
rcr = (*sc->read_register)( rp, SONIC_REG_RCR );
|
|
if (ifp->if_flags & IFF_PROMISC)
|
|
rcr |= RCR_PRO;
|
|
else
|
|
rcr &= ~RCR_PRO;
|
|
(*sc->write_register)( rp, SONIC_REG_RCR, rcr);
|
|
|
|
/*
|
|
* Tell the world that we're running.
|
|
*/
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
/*
|
|
* Enable receiver and transmitter
|
|
*/
|
|
sonic_enable_interrupts( sc, IMR_TXEREN | (IMR_PRXEN | IMR_RBAEEN) );
|
|
sonic_command( sc, CR_RXEN );
|
|
}
|
|
|
|
/*
|
|
* Driver ioctl handler
|
|
*/
|
|
static int
|
|
sonic_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
|
|
{
|
|
struct sonic_softc *sc = ifp->if_softc;
|
|
int error = 0;
|
|
|
|
switch (command) {
|
|
case SIOCGIFADDR:
|
|
case SIOCSIFADDR:
|
|
ether_ioctl (ifp, command, data);
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
|
switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
|
|
case IFF_RUNNING:
|
|
sonic_stop (sc);
|
|
break;
|
|
|
|
case IFF_UP:
|
|
sonic_init (sc);
|
|
break;
|
|
|
|
case IFF_UP | IFF_RUNNING:
|
|
sonic_stop (sc);
|
|
sonic_init (sc);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SIO_RTEMS_SHOW_STATS:
|
|
sonic_stats (sc);
|
|
break;
|
|
|
|
/*
|
|
* FIXME: All sorts of multicast commands need to be added here!
|
|
*/
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Attach an SONIC driver to the system
|
|
* This is the only `extern' function in the driver.
|
|
*/
|
|
|
|
int
|
|
rtems_sonic_driver_attach (
|
|
struct rtems_bsdnet_ifconfig *config,
|
|
sonic_configuration_t *chip
|
|
)
|
|
{
|
|
struct sonic_softc *sc;
|
|
struct ifnet *ifp;
|
|
int mtu;
|
|
int unitNumber;
|
|
char *unitName;
|
|
|
|
/*
|
|
* Parse driver name
|
|
*/
|
|
if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
|
|
return 0;
|
|
|
|
/*
|
|
* Is driver free?
|
|
*/
|
|
if ((unitNumber <= 0) || (unitNumber > NSONIC)) {
|
|
printf ("Bad SONIC unit number.\n");
|
|
return 0;
|
|
}
|
|
sc = &sonic_softc[unitNumber - 1];
|
|
ifp = &sc->arpcom.ac_if;
|
|
if (ifp->if_softc != NULL) {
|
|
printf ("Driver already in use.\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* zero out the control structure
|
|
*/
|
|
|
|
memset( sc, 0, sizeof(*sc) );
|
|
|
|
|
|
/*
|
|
* Process options
|
|
*/
|
|
if (config->hardware_address) {
|
|
memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
|
|
}
|
|
else {
|
|
memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
|
|
}
|
|
if (config->mtu)
|
|
mtu = config->mtu;
|
|
else
|
|
mtu = ETHERMTU;
|
|
if (config->rbuf_count)
|
|
sc->rdaCount = config->rbuf_count;
|
|
else
|
|
sc->rdaCount = chip->rda_count;
|
|
if (config->xbuf_count)
|
|
sc->tdaCount = config->xbuf_count;
|
|
else
|
|
sc->tdaCount = chip->tda_count;
|
|
sc->acceptBroadcast = !config->ignore_broadcast;
|
|
|
|
sc->sonic = chip->base_address;
|
|
sc->vector = chip->vector;
|
|
sc->dcr_value = chip->dcr_value;
|
|
sc->dc2_value = chip->dc2_value;
|
|
sc->write_register = chip->write_register;
|
|
sc->read_register = chip->read_register;
|
|
|
|
/*
|
|
* Set up network interface values
|
|
*/
|
|
ifp->if_softc = sc;
|
|
ifp->if_unit = unitNumber;
|
|
ifp->if_name = unitName;
|
|
ifp->if_mtu = mtu;
|
|
ifp->if_init = sonic_init;
|
|
ifp->if_ioctl = sonic_ioctl;
|
|
ifp->if_start = sonic_start;
|
|
ifp->if_output = ether_output;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
|
|
if (ifp->if_snd.ifq_maxlen == 0)
|
|
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
|
|
|
/*
|
|
* Attach the interface
|
|
*/
|
|
if_attach (ifp);
|
|
ether_ifattach (ifp);
|
|
return 1;
|
|
}
|
|
|
|
#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
|
|
#include <stdio.h>
|
|
|
|
char SONIC_Reg_name[64][6]= {
|
|
"CR", /* 0x00 */
|
|
"DCR", /* 0x01 */
|
|
"RCR", /* 0x02 */
|
|
"TCR", /* 0x03 */
|
|
"IMR", /* 0x04 */
|
|
"ISR", /* 0x05 */
|
|
"UTDA", /* 0x06 */
|
|
"CTDA", /* 0x07 */
|
|
"0x08", /* 0x08 */
|
|
"0x09", /* 0x09 */
|
|
"0x0A", /* 0x0A */
|
|
"0x0B", /* 0x0B */
|
|
"0x0C", /* 0x0C */
|
|
"URDA", /* 0x0D */
|
|
"CRDA", /* 0x0E */
|
|
"0x0F", /* 0x0F */
|
|
"0x10", /* 0x10 */
|
|
"0x11", /* 0x11 */
|
|
"0x12", /* 0x12 */
|
|
"EOBC", /* 0x13 */
|
|
"URRA", /* 0x14 */
|
|
"RSA", /* 0x15 */
|
|
"REA", /* 0x16 */
|
|
"RRP", /* 0x17 */
|
|
"RWP", /* 0x18 */
|
|
"0x19", /* 0x19 */
|
|
"0x1A", /* 0x1A */
|
|
"0x1B", /* 0x1B */
|
|
"0x1C", /* 0x1C */
|
|
"0x0D", /* 0x1D */
|
|
"0x1E", /* 0x1E */
|
|
"0x1F", /* 0x1F */
|
|
"0x20", /* 0x20 */
|
|
"CEP", /* 0x21 */
|
|
"CAP2", /* 0x22 */
|
|
"CAP1", /* 0x23 */
|
|
"CAP0", /* 0x24 */
|
|
"CE", /* 0x25 */
|
|
"CDP", /* 0x26 */
|
|
"CDC", /* 0x27 */
|
|
"SR", /* 0x28 */
|
|
"WT0", /* 0x29 */
|
|
"WT1", /* 0x2A */
|
|
"RSC", /* 0x2B */
|
|
"CRCT", /* 0x2C */
|
|
"FAET", /* 0x2D */
|
|
"MPT", /* 0x2E */
|
|
"MDT", /* 0x2F */
|
|
"0x30", /* 0x30 */
|
|
"0x31", /* 0x31 */
|
|
"0x32", /* 0x32 */
|
|
"0x33", /* 0x33 */
|
|
"0x34", /* 0x34 */
|
|
"0x35", /* 0x35 */
|
|
"0x36", /* 0x36 */
|
|
"0x37", /* 0x37 */
|
|
"0x38", /* 0x38 */
|
|
"0x39", /* 0x39 */
|
|
"0x3A", /* 0x3A */
|
|
"0x3B", /* 0x3B */
|
|
"0x3C", /* 0x3C */
|
|
"0x3D", /* 0x3D */
|
|
"0x3E", /* 0x3E */
|
|
"DCR2" /* 0x3F */
|
|
};
|
|
#endif
|