|
|
|
|
@@ -1,92 +1,115 @@
|
|
|
|
|
/*
|
|
|
|
|
* XXX This driver needs to be reworked to support the new BSD stack
|
|
|
|
|
*
|
|
|
|
|
* RTEMS/KA9Q driver for WD8003 Ethernet Controller
|
|
|
|
|
* RTEMS driver for M68360 WD1 Ethernet
|
|
|
|
|
*
|
|
|
|
|
* W. Eric Norum
|
|
|
|
|
* Saskatchewan Accelerator Laboratory
|
|
|
|
|
* University of Saskatchewan
|
|
|
|
|
* Saskatoon, Saskatchewan, CANADA
|
|
|
|
|
* eric@skatter.usask.ca
|
|
|
|
|
*
|
|
|
|
|
* $Id$
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <bsp.h>
|
|
|
|
|
#include <wd80x3.h>
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <rtems/error.h>
|
|
|
|
|
#include <ka9q/rtems_ka9q.h>
|
|
|
|
|
#include <ka9q/global.h>
|
|
|
|
|
#include <ka9q/enet.h>
|
|
|
|
|
#include <ka9q/iface.h>
|
|
|
|
|
#include <ka9q/netuser.h>
|
|
|
|
|
#include <ka9q/trace.h>
|
|
|
|
|
#include <ka9q/commands.h>
|
|
|
|
|
#include <ka9q/domain.h>
|
|
|
|
|
#include <rtems/rtems_bsdnet.h>
|
|
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <netinet/if_ether.h>
|
|
|
|
|
|
|
|
|
|
#include <irq.h>
|
|
|
|
|
|
|
|
|
|
#define ET_MINLEN 60 /* minimum message length */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Number of SCCs supported by this driver
|
|
|
|
|
* Number of WDs supported by this driver
|
|
|
|
|
*/
|
|
|
|
|
#define NSCCDRIVER 1
|
|
|
|
|
#define NWDDRIVER 1
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Default number of buffer descriptors set aside for this driver.
|
|
|
|
|
* The number of transmit buffer descriptors has to be quite large
|
|
|
|
|
* since a single frame often uses four or more buffer descriptors.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define RX_BUF_COUNT 15
|
|
|
|
|
#define TX_BUF_COUNT 4
|
|
|
|
|
#define TX_BD_PER_BUF 4
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* RTEMS event used by interrupt handler to signal daemons.
|
|
|
|
|
* This must *not* be the same event used by the KA9Q task synchronization.
|
|
|
|
|
* RTEMS event used by interrupt handler to signal driver tasks.
|
|
|
|
|
* This must not be any of the events used by the network task synchronization.
|
|
|
|
|
*/
|
|
|
|
|
#define INTERRUPT_EVENT RTEMS_EVENT_1
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Receive buffer size -- Allow for a full ethernet packet plus a pointer
|
|
|
|
|
* RTEMS event used to start transmit daemon.
|
|
|
|
|
* This must not be the same as INTERRUPT_EVENT.
|
|
|
|
|
*/
|
|
|
|
|
#define RBUF_SIZE (1520 + sizeof (struct iface *))
|
|
|
|
|
#define START_TRANSMIT_EVENT RTEMS_EVENT_2
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Hardware-specific storage
|
|
|
|
|
* Receive buffer size -- Allow for a full ethernet packet including CRC
|
|
|
|
|
*/
|
|
|
|
|
typedef struct {
|
|
|
|
|
#define RBUF_SIZE 1520
|
|
|
|
|
|
|
|
|
|
#if (MCLBYTES < RBUF_SIZE)
|
|
|
|
|
# error "Driver must have MCLBYTES > RBUF_SIZE"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Per-device data
|
|
|
|
|
*/
|
|
|
|
|
struct wd_softc {
|
|
|
|
|
struct arpcom arpcom;
|
|
|
|
|
rtems_irq_connect_data irqInfo;
|
|
|
|
|
struct mbuf **rxMbuf;
|
|
|
|
|
struct mbuf **txMbuf;
|
|
|
|
|
unsigned int port;
|
|
|
|
|
unsigned char *base;
|
|
|
|
|
unsigned long bpar;
|
|
|
|
|
int acceptBroadcast;
|
|
|
|
|
int rxBdCount;
|
|
|
|
|
int txBdCount;
|
|
|
|
|
int txBdHead;
|
|
|
|
|
int txBdTail;
|
|
|
|
|
int txBdActiveCount;
|
|
|
|
|
struct iface *iface;
|
|
|
|
|
rtems_id txWaitTid;
|
|
|
|
|
rtems_id rxDaemonTid;
|
|
|
|
|
rtems_id txDaemonTid;
|
|
|
|
|
|
|
|
|
|
unsigned int port;
|
|
|
|
|
unsigned char *base;
|
|
|
|
|
unsigned long bpar;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Statistics
|
|
|
|
|
*/
|
|
|
|
|
unsigned long rxInterrupts;
|
|
|
|
|
unsigned long rxNotFirst;
|
|
|
|
|
unsigned long rxNotLast;
|
|
|
|
|
unsigned long rxGiant;
|
|
|
|
|
unsigned long rxNonOctet;
|
|
|
|
|
unsigned long rxRunt;
|
|
|
|
|
unsigned long rxBadCRC;
|
|
|
|
|
unsigned long rxOverrun;
|
|
|
|
|
unsigned long rxCollision;
|
|
|
|
|
unsigned long rxInterrupts;
|
|
|
|
|
unsigned long rxNotFirst;
|
|
|
|
|
unsigned long rxNotLast;
|
|
|
|
|
unsigned long rxGiant;
|
|
|
|
|
unsigned long rxNonOctet;
|
|
|
|
|
unsigned long rxRunt;
|
|
|
|
|
unsigned long rxBadCRC;
|
|
|
|
|
unsigned long rxOverrun;
|
|
|
|
|
unsigned long rxCollision;
|
|
|
|
|
|
|
|
|
|
unsigned long txInterrupts;
|
|
|
|
|
unsigned long txDeferred;
|
|
|
|
|
unsigned long txHeartbeat;
|
|
|
|
|
unsigned long txLateCollision;
|
|
|
|
|
unsigned long txRetryLimit;
|
|
|
|
|
unsigned long txUnderrun;
|
|
|
|
|
unsigned long txLostCarrier;
|
|
|
|
|
unsigned long txRawWait;
|
|
|
|
|
}wd80x3EnetDriver;
|
|
|
|
|
unsigned long txInterrupts;
|
|
|
|
|
unsigned long txDeferred;
|
|
|
|
|
unsigned long txHeartbeat;
|
|
|
|
|
unsigned long txLateCollision;
|
|
|
|
|
unsigned long txRetryLimit;
|
|
|
|
|
unsigned long txUnderrun;
|
|
|
|
|
unsigned long txLostCarrier;
|
|
|
|
|
unsigned long txRawWait;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define RO 0x10
|
|
|
|
|
|
|
|
|
|
@@ -96,39 +119,26 @@ typedef struct {
|
|
|
|
|
#define OUTPAGE ((SHATOT-(MAXSIZ+SHAPAGE-1))/SHAPAGE)
|
|
|
|
|
|
|
|
|
|
static unsigned long loopc;
|
|
|
|
|
|
|
|
|
|
static wd80x3EnetDriver wd8003EnetDriver[NSCCDRIVER];
|
|
|
|
|
static volatile unsigned long overrun;
|
|
|
|
|
static volatile unsigned long resend;
|
|
|
|
|
static struct wd_softc wd_softc[NWDDRIVER];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* WD8003 interrupt handler. The code as it is cleraly showes that
|
|
|
|
|
* only one driver is connected. In order to change this a table
|
|
|
|
|
* making the correspondance between the current irq number and
|
|
|
|
|
* the corresponding wd8003EnetDriver structure could be used...
|
|
|
|
|
* WD interrupt handler
|
|
|
|
|
*/
|
|
|
|
|
static void wd8003Enet_interrupt_handler ()
|
|
|
|
|
static rtems_isr
|
|
|
|
|
wd8003Enet_interrupt_handler (rtems_vector_number v)
|
|
|
|
|
{
|
|
|
|
|
unsigned int tport, nowTicks, bootTicks;
|
|
|
|
|
unsigned char status, status2;
|
|
|
|
|
|
|
|
|
|
struct iface *iface = (struct iface *)(wd8003EnetDriver[0].iface);
|
|
|
|
|
wd80x3EnetDriver *dp = (wd80x3EnetDriver *)&wd8003EnetDriver[0];
|
|
|
|
|
struct mbuf *bp;
|
|
|
|
|
unsigned int i2;
|
|
|
|
|
unsigned int len;
|
|
|
|
|
unsigned char start, next, current;
|
|
|
|
|
char *shp, *temp;
|
|
|
|
|
|
|
|
|
|
tport = wd8003EnetDriver[0].port ;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Drop chips interrupt
|
|
|
|
|
*/
|
|
|
|
|
outport_byte(tport+IMR, 0x00);
|
|
|
|
|
tport = wd_softc[0].port ;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Read status
|
|
|
|
|
*/
|
|
|
|
|
inport_byte(tport+ISR, status);
|
|
|
|
|
outport_byte(tport+IMR, 0x00);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Ring overwrite
|
|
|
|
|
@@ -145,56 +155,20 @@ static void wd8003Enet_interrupt_handler ()
|
|
|
|
|
status |= (status2 & (MSK_PTX+MSK_TXE)) ; /* TX status */
|
|
|
|
|
outport_byte(tport+TCR, MSK_LOOP); /* loopback mode */
|
|
|
|
|
outport_byte(tport+CMDR, MSK_STA + MSK_RD2); /* start */
|
|
|
|
|
overrun = 1 ;
|
|
|
|
|
if ((status & (MSK_PTX+MSK_TXE)) == 0)
|
|
|
|
|
resend = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Frame received?
|
|
|
|
|
*/
|
|
|
|
|
while (status & (MSK_PRX+MSK_RXE)) {
|
|
|
|
|
*/
|
|
|
|
|
if (status & (MSK_PRX+MSK_RXE)) {
|
|
|
|
|
outport_byte(tport+ISR, status & (MSK_PRX+MSK_RXE));
|
|
|
|
|
inport_byte(tport+BNRY, start);
|
|
|
|
|
start += 1;
|
|
|
|
|
shp = dp->base + 1 + (SHAPAGE * start);
|
|
|
|
|
next = *shp++;
|
|
|
|
|
len = *((short *)shp)++ - 4;
|
|
|
|
|
if (start >= OUTPAGE || next >= OUTPAGE)
|
|
|
|
|
break;
|
|
|
|
|
bp = ambufw (RBUF_SIZE);
|
|
|
|
|
bp->data += sizeof (struct iface *);
|
|
|
|
|
temp = bp->data;
|
|
|
|
|
bp->cnt = len;
|
|
|
|
|
|
|
|
|
|
if ((i2 = (OUTPAGE - start) * SHAPAGE - 4) < len){
|
|
|
|
|
memcpy(temp, shp, i2);
|
|
|
|
|
len -= i2;
|
|
|
|
|
temp += i2;
|
|
|
|
|
shp = dp->base;
|
|
|
|
|
}
|
|
|
|
|
memcpy(temp, shp, len);
|
|
|
|
|
|
|
|
|
|
net_route (iface, &bp);
|
|
|
|
|
outport_byte(tport+BNRY, next-1);
|
|
|
|
|
outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
|
|
|
|
|
inport_byte(tport+CURR, current);
|
|
|
|
|
outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
|
|
|
|
|
if (current == next)
|
|
|
|
|
break;
|
|
|
|
|
wd_softc[0].rxInterrupts++;
|
|
|
|
|
rtems_event_send (wd_softc[0].rxDaemonTid, INTERRUPT_EVENT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Ring overwrite
|
|
|
|
|
*/
|
|
|
|
|
if (status & MSK_OVW) {
|
|
|
|
|
outport_byte(tport+ISR, MSK_OVW); /* reset IR */
|
|
|
|
|
outport_byte(tport+TCR, 0); /* out of loopback */
|
|
|
|
|
if ((status & (MSK_PTX+MSK_TXE)) == 0)
|
|
|
|
|
outport_byte(tport+CMDR, MSK_TXP + MSK_RD2); /* resend */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Enable chip interrupts
|
|
|
|
|
*/
|
|
|
|
|
outport_byte(tport+IMR, 0x15);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void nopOn(const rtems_irq_connect_data* notUsed)
|
|
|
|
|
@@ -214,32 +188,34 @@ static int wdIsOn(const rtems_irq_connect_data* irq)
|
|
|
|
|
* Initialize the ethernet hardware
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
wd8003Enet_initialize_hardware (wd80x3EnetDriver *dp, int broadcastFlag)
|
|
|
|
|
wd8003Enet_initialize_hardware (struct wd_softc *sc)
|
|
|
|
|
{
|
|
|
|
|
int i1, ultra;
|
|
|
|
|
char cc1, cc2;
|
|
|
|
|
unsigned char temp;
|
|
|
|
|
rtems_status_code sc;
|
|
|
|
|
rtems_status_code st;
|
|
|
|
|
unsigned int tport;
|
|
|
|
|
unsigned char *hwaddr;
|
|
|
|
|
|
|
|
|
|
tport = dp->port;
|
|
|
|
|
tport = sc->port;
|
|
|
|
|
|
|
|
|
|
/* address from board ROM */
|
|
|
|
|
inport_byte(tport+0x04, temp);
|
|
|
|
|
outport_byte(tport+0x04, temp & 0x7f);
|
|
|
|
|
|
|
|
|
|
hwaddr = sc->arpcom.ac_enaddr;
|
|
|
|
|
for (i1=cc2=0; i1<8; i1++) {
|
|
|
|
|
inport_byte(tport + ADDROM + i1, cc1);
|
|
|
|
|
cc2 += cc1;
|
|
|
|
|
if (i1 < 6)
|
|
|
|
|
dp->iface->hwaddr[i1] = cc1;
|
|
|
|
|
hwaddr[i1] = cc1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inport_byte(tport+0x04, temp);
|
|
|
|
|
outport_byte(tport+0x04, temp | 0x80); /* alternate registers */
|
|
|
|
|
outport_byte(tport+W83CREG, MSK_RESET); /* reset board, set buffer */
|
|
|
|
|
outport_byte(tport+W83CREG, 0);
|
|
|
|
|
outport_byte(tport+W83CREG, MSK_ENASH + (int)((dp->bpar>>13)&0x3f));
|
|
|
|
|
outport_byte(tport+W83CREG, MSK_ENASH + (int)((sc->bpar>>13)&0x3f));
|
|
|
|
|
|
|
|
|
|
outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
|
|
|
|
|
cc1 = MSK_BMS + MSK_FT10; /* configure 8 or 16 bits */
|
|
|
|
|
@@ -258,12 +234,12 @@ wd8003Enet_initialize_hardware (wd80x3EnetDriver *dp, int broadcastFlag)
|
|
|
|
|
outport_byte(tport+PSTART, 0); /* init PSTART */
|
|
|
|
|
outport_byte(tport+BNRY, -1); /* init BNRY */
|
|
|
|
|
outport_byte(tport+ISR, -1); /* clear IR's */
|
|
|
|
|
outport_byte(tport+IMR, 0x15); /* 0x17 enable interrupt */
|
|
|
|
|
outport_byte(tport+IMR, 0x15); /* enable interrupt */
|
|
|
|
|
|
|
|
|
|
outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
|
|
|
|
|
|
|
|
|
|
for (i1=0; i1<6; i1++) /* initial physical addr */
|
|
|
|
|
outport_byte(tport+PAR+i1, dp->iface->hwaddr[i1]);
|
|
|
|
|
outport_byte(tport+PAR+i1, hwaddr[i1]);
|
|
|
|
|
|
|
|
|
|
for (i1=0; i1<MARsize; i1++) /* clear multicast */
|
|
|
|
|
outport_byte(tport+MAR+i1, 0);
|
|
|
|
|
@@ -283,287 +259,400 @@ wd8003Enet_initialize_hardware (wd80x3EnetDriver *dp, int broadcastFlag)
|
|
|
|
|
/*
|
|
|
|
|
* Set up interrupts
|
|
|
|
|
*/
|
|
|
|
|
dp->irqInfo.hdl = wd8003Enet_interrupt_handler;
|
|
|
|
|
dp->irqInfo.on = nopOn;
|
|
|
|
|
dp->irqInfo.off = nopOn;
|
|
|
|
|
dp->irqInfo.isOn = wdIsOn;
|
|
|
|
|
sc->irqInfo.hdl = wd8003Enet_interrupt_handler;
|
|
|
|
|
sc->irqInfo.on = nopOn;
|
|
|
|
|
sc->irqInfo.off = nopOn;
|
|
|
|
|
sc->irqInfo.isOn = wdIsOn;
|
|
|
|
|
|
|
|
|
|
sc = pc386_install_rtems_irq_handler (&dp->irqInfo);
|
|
|
|
|
if (!sc)
|
|
|
|
|
st = pc386_install_rtems_irq_handler (&sc->irqInfo);
|
|
|
|
|
if (!st)
|
|
|
|
|
rtems_panic ("Can't attach WD interrupt handler for irq %d\n",
|
|
|
|
|
dp->irqInfo.name);
|
|
|
|
|
sc->irqInfo.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Send raw packet (caller provides header).
|
|
|
|
|
* This code runs in the context of the interface transmit
|
|
|
|
|
* task or in the context of the network task.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
wd8003Enet_raw (struct iface *iface, struct mbuf **bpp)
|
|
|
|
|
static void
|
|
|
|
|
wd_rxDaemon (void *arg)
|
|
|
|
|
{
|
|
|
|
|
wd80x3EnetDriver *dp = &wd8003EnetDriver[iface->dev];
|
|
|
|
|
struct mbuf *bp;
|
|
|
|
|
unsigned int len, tport;
|
|
|
|
|
char *shp;
|
|
|
|
|
unsigned int tport;
|
|
|
|
|
struct ether_header *eh;
|
|
|
|
|
struct wd_softc *dp = (struct wd_softc *)&wd_softc[0];
|
|
|
|
|
struct ifnet *ifp = &dp->arpcom.ac_if;
|
|
|
|
|
struct mbuf *m;
|
|
|
|
|
unsigned int i2;
|
|
|
|
|
unsigned int len;
|
|
|
|
|
volatile unsigned char start, next, current;
|
|
|
|
|
char *shp, *temp;
|
|
|
|
|
rtems_event_set events;
|
|
|
|
|
|
|
|
|
|
tport = dp->port;
|
|
|
|
|
|
|
|
|
|
tport = wd_softc[0].port ;
|
|
|
|
|
|
|
|
|
|
for (;;){
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rtems_bsdnet_event_receive (INTERRUPT_EVENT,
|
|
|
|
|
RTEMS_WAIT|RTEMS_EVENT_ANY,
|
|
|
|
|
RTEMS_NO_TIMEOUT,
|
|
|
|
|
&events);
|
|
|
|
|
|
|
|
|
|
for (;;){
|
|
|
|
|
inport_byte(tport+BNRY, start);
|
|
|
|
|
|
|
|
|
|
outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
|
|
|
|
|
inport_byte(tport+CURR, current);
|
|
|
|
|
outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
|
|
|
|
|
|
|
|
|
|
start += 1;
|
|
|
|
|
if (start >= OUTPAGE){
|
|
|
|
|
start = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current == start)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
shp = dp->base + 1 + (SHAPAGE * start);
|
|
|
|
|
next = *shp++;
|
|
|
|
|
len = *((short *)shp)++ - 4;
|
|
|
|
|
|
|
|
|
|
if (next >= OUTPAGE){
|
|
|
|
|
next = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MGETHDR (m, M_WAIT, MT_DATA);
|
|
|
|
|
MCLGET (m, M_WAIT);
|
|
|
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
|
|
|
|
|
|
temp = m->m_data;
|
|
|
|
|
m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
|
|
|
|
|
|
|
|
|
|
if ((i2 = (OUTPAGE - start) * SHAPAGE - 4) < len){
|
|
|
|
|
memcpy(temp, shp, i2);
|
|
|
|
|
len -= i2;
|
|
|
|
|
temp += i2;
|
|
|
|
|
shp = dp->base;
|
|
|
|
|
}
|
|
|
|
|
memcpy(temp, shp, len);
|
|
|
|
|
|
|
|
|
|
eh = mtod (m, struct ether_header *);
|
|
|
|
|
m->m_data += sizeof(struct ether_header);
|
|
|
|
|
ether_input (ifp, eh, m);
|
|
|
|
|
|
|
|
|
|
outport_byte(tport+BNRY, next-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Fill in some logging data
|
|
|
|
|
*/
|
|
|
|
|
iface->rawsndcnt++;
|
|
|
|
|
iface->lastsent = secclock ();
|
|
|
|
|
dump (iface, IF_TRACE_OUT, *bpp);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* It would not do to have two tasks active in the transmit
|
|
|
|
|
* loop at the same time.
|
|
|
|
|
* The blocking is simple-minded since the odds of two tasks
|
|
|
|
|
* simultaneously attempting to use this code are low. The only
|
|
|
|
|
* way that two tasks can try to run here is:
|
|
|
|
|
* 1) Task A enters this code and ends up having to
|
|
|
|
|
* wait for a transmit buffer descriptor.
|
|
|
|
|
* 2) Task B gains control and tries to transmit a packet.
|
|
|
|
|
* The RTEMS/KA9Q scheduling semaphore ensures that there
|
|
|
|
|
* are no race conditions associated with manipulating the
|
|
|
|
|
* txWaitTid variable.
|
|
|
|
|
* Ring overwrite
|
|
|
|
|
*/
|
|
|
|
|
if (overrun){
|
|
|
|
|
outport_byte(tport+ISR, MSK_OVW); /* reset IR */
|
|
|
|
|
outport_byte(tport+TCR, 0); /* out of loopback */
|
|
|
|
|
if (resend == 1)
|
|
|
|
|
outport_byte(tport+CMDR, MSK_TXP + MSK_RD2); /* resend */
|
|
|
|
|
resend = 0;
|
|
|
|
|
overrun = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outport_byte(tport+IMR, 0x15); /* re-enable IT rx */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
sendpacket (struct ifnet *ifp, struct mbuf *m)
|
|
|
|
|
{
|
|
|
|
|
struct wd_softc *dp = ifp->if_softc;
|
|
|
|
|
struct mbuf *n;
|
|
|
|
|
unsigned int len, tport;
|
|
|
|
|
char *shp;
|
|
|
|
|
|
|
|
|
|
tport = dp->port;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dp->txWaitTid) {
|
|
|
|
|
dp->txRawWait++;
|
|
|
|
|
while (dp->txWaitTid)
|
|
|
|
|
rtems_ka9q_ppause (10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dp->txWaitTid == 0)
|
|
|
|
|
rtems_task_ident (0, 0, &dp->txWaitTid);
|
|
|
|
|
|
|
|
|
|
bp = *bpp;
|
|
|
|
|
len = 0;
|
|
|
|
|
shp = dp->base + (SHAPAGE * OUTPAGE);
|
|
|
|
|
|
|
|
|
|
/*rtems_interrupt_disable(level);*/
|
|
|
|
|
n = m;
|
|
|
|
|
|
|
|
|
|
for (;;){
|
|
|
|
|
len += bp->cnt;
|
|
|
|
|
memcpy(shp, (char *)bp->data, bp->cnt);
|
|
|
|
|
shp += bp->cnt ;
|
|
|
|
|
if ((bp = bp->next) == NULL)
|
|
|
|
|
len += m->m_len;
|
|
|
|
|
memcpy(shp, (char *)m->m_data, m->m_len);
|
|
|
|
|
shp += m->m_len ;
|
|
|
|
|
if ((m = m->m_next) == NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free_p(bpp);
|
|
|
|
|
|
|
|
|
|
m_freem(n);
|
|
|
|
|
|
|
|
|
|
if (len < ET_MINLEN) len = ET_MINLEN;
|
|
|
|
|
outport_byte(tport+TBCR0, len);
|
|
|
|
|
outport_byte(tport+TBCR1, (len >> 8) );
|
|
|
|
|
outport_byte(tport+TPSR, OUTPAGE);
|
|
|
|
|
outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Show that we've finished with the packet
|
|
|
|
|
*/
|
|
|
|
|
dp->txWaitTid = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Driver transmit daemon
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
wd_txDaemon (void *arg)
|
|
|
|
|
{
|
|
|
|
|
struct wd_softc *sc = (struct wd_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;
|
|
|
|
|
sendpacket (ifp, m);
|
|
|
|
|
}
|
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Shut down the interface
|
|
|
|
|
* FIXME: This is a pretty simple-minded routine. It doesn't worry
|
|
|
|
|
* about cleaning up mbufs, shutting down daemons, etc.
|
|
|
|
|
* Send packet (caller provides header).
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
wd8003Enet_stop (struct iface *iface)
|
|
|
|
|
static void
|
|
|
|
|
wd_start (struct ifnet *ifp)
|
|
|
|
|
{
|
|
|
|
|
struct wd_softc *sc = ifp->if_softc;
|
|
|
|
|
|
|
|
|
|
rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
|
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Initialize and start the device
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
wd_init (void *arg)
|
|
|
|
|
{
|
|
|
|
|
struct wd_softc *sc = arg;
|
|
|
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
|
|
|
|
|
|
|
|
if (sc->txDaemonTid == 0) {
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set up WD hardware
|
|
|
|
|
*/
|
|
|
|
|
wd8003Enet_initialize_hardware (sc);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Start driver tasks
|
|
|
|
|
*/
|
|
|
|
|
sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, wd_txDaemon, sc);
|
|
|
|
|
sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, wd_rxDaemon, sc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Tell the world that we're running.
|
|
|
|
|
*/
|
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Stop the device
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
wd_stop (struct wd_softc *sc)
|
|
|
|
|
{
|
|
|
|
|
unsigned int tport;
|
|
|
|
|
unsigned char temp;
|
|
|
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
|
|
|
|
|
|
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Stop the transmitter
|
|
|
|
|
*/
|
|
|
|
|
tport=wd8003EnetDriver[0].port ;
|
|
|
|
|
tport=wd_softc[0].port ;
|
|
|
|
|
inport_byte(tport+0x04,temp);
|
|
|
|
|
outport_byte(tport+0x04, temp & 0x7f);
|
|
|
|
|
outport_byte(tport + CMDR, MSK_STP + MSK_RD2);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Show interface statistics
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
wd8003Enet_show (struct iface *iface)
|
|
|
|
|
wd_stats (struct wd_softc *sc)
|
|
|
|
|
{
|
|
|
|
|
printf (" Rx Interrupts:%-8lu", wd8003EnetDriver[0].rxInterrupts);
|
|
|
|
|
printf (" Not First:%-8lu", wd8003EnetDriver[0].rxNotFirst);
|
|
|
|
|
printf (" Not Last:%-8lu\n", wd8003EnetDriver[0].rxNotLast);
|
|
|
|
|
printf (" Giant:%-8lu", wd8003EnetDriver[0].rxGiant);
|
|
|
|
|
printf (" Runt:%-8lu", wd8003EnetDriver[0].rxRunt);
|
|
|
|
|
printf (" Non-octet:%-8lu\n", wd8003EnetDriver[0].rxNonOctet);
|
|
|
|
|
printf (" Bad CRC:%-8lu", wd8003EnetDriver[0].rxBadCRC);
|
|
|
|
|
printf (" Overrun:%-8lu", wd8003EnetDriver[0].rxOverrun);
|
|
|
|
|
printf (" Collision:%-8lu\n", wd8003EnetDriver[0].rxCollision);
|
|
|
|
|
printf (" Tx Interrupts:%-8lu", wd8003EnetDriver[0].txInterrupts);
|
|
|
|
|
printf (" Deferred:%-8lu", wd8003EnetDriver[0].txDeferred);
|
|
|
|
|
printf (" Missed Hearbeat:%-8lu\n", wd8003EnetDriver[0].txHeartbeat);
|
|
|
|
|
printf (" No Carrier:%-8lu", wd8003EnetDriver[0].txLostCarrier);
|
|
|
|
|
printf ("Retransmit Limit:%-8lu", wd8003EnetDriver[0].txRetryLimit);
|
|
|
|
|
printf (" Late Collision:%-8lu\n", wd8003EnetDriver[0].txLateCollision);
|
|
|
|
|
printf (" Underrun:%-8lu", wd8003EnetDriver[0].txUnderrun);
|
|
|
|
|
printf (" Raw output wait:%-8lu\n", wd8003EnetDriver[0].txRawWait);
|
|
|
|
|
printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
|
|
|
|
|
printf (" Not First:%-8lu", sc->rxNotFirst);
|
|
|
|
|
printf (" Not Last:%-8lu\n", sc->rxNotLast);
|
|
|
|
|
printf (" Giant:%-8lu", sc->rxGiant);
|
|
|
|
|
printf (" Runt:%-8lu", sc->rxRunt);
|
|
|
|
|
printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
|
|
|
|
|
printf (" Bad CRC:%-8lu", sc->rxBadCRC);
|
|
|
|
|
printf (" Overrun:%-8lu", sc->rxOverrun);
|
|
|
|
|
printf (" Collision:%-8lu\n", sc->rxCollision);
|
|
|
|
|
|
|
|
|
|
printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
|
|
|
|
|
printf (" Deferred:%-8lu", sc->txDeferred);
|
|
|
|
|
printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
|
|
|
|
|
printf (" No Carrier:%-8lu", sc->txLostCarrier);
|
|
|
|
|
printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
|
|
|
|
|
printf (" Late Collision:%-8lu\n", sc->txLateCollision);
|
|
|
|
|
printf (" Underrun:%-8lu", sc->txUnderrun);
|
|
|
|
|
printf (" Raw output wait:%-8lu\n", sc->txRawWait);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Attach an WD8003 driver to the system
|
|
|
|
|
* This is the only `extern' function in the driver.
|
|
|
|
|
*
|
|
|
|
|
* argv[0]: interface label, e.g., "rtems"
|
|
|
|
|
* The remainder of the arguemnts are key/value pairs:
|
|
|
|
|
* mtu ## -- maximum transmission unit, default 1500
|
|
|
|
|
* broadcast y/n -- accept or ignore broadcast packets, default yes
|
|
|
|
|
* rbuf ## -- Set number of receive buffer descriptors
|
|
|
|
|
* rbuf ## -- Set number of transmit buffer descriptors
|
|
|
|
|
* ip ###.###.###.### -- IP address
|
|
|
|
|
* ether ##:##:##:##:##:## -- Ethernet address
|
|
|
|
|
* irno -- Set controller irq
|
|
|
|
|
* port -- Set io port
|
|
|
|
|
* bpar -- Set RAM address
|
|
|
|
|
* Driver ioctl handler
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
wd_ioctl (struct ifnet *ifp, int command, caddr_t data)
|
|
|
|
|
{
|
|
|
|
|
struct wd_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:
|
|
|
|
|
wd_stop (sc);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFF_UP:
|
|
|
|
|
wd_init (sc);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IFF_UP | IFF_RUNNING:
|
|
|
|
|
wd_stop (sc);
|
|
|
|
|
wd_init (sc);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SIO_RTEMS_SHOW_STATS:
|
|
|
|
|
wd_stats (sc);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* FIXME: All sorts of multicast commands need to be added here!
|
|
|
|
|
*/
|
|
|
|
|
default:
|
|
|
|
|
error = EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Attach an WD driver to the system
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
rtems_ka9q_driver_attach (int argc, char *argv[], void *p)
|
|
|
|
|
rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *config)
|
|
|
|
|
{
|
|
|
|
|
struct iface *iface;
|
|
|
|
|
wd80x3EnetDriver *dp;
|
|
|
|
|
char *cp;
|
|
|
|
|
int i;
|
|
|
|
|
int argIndex;
|
|
|
|
|
int broadcastFlag;
|
|
|
|
|
char cbuf[30];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find a free driver
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0 ; i < NSCCDRIVER ; i++) {
|
|
|
|
|
if (wd8003EnetDriver[i].iface == NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (i >= NSCCDRIVER) {
|
|
|
|
|
printf ("Too many SCC drivers.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (if_lookup (argv[0]) != NULL) {
|
|
|
|
|
printf ("Interface %s already exists\n", argv[0]);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
dp = &wd8003EnetDriver[i];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create an inteface descriptor
|
|
|
|
|
*/
|
|
|
|
|
iface = callocw (1, sizeof *iface);
|
|
|
|
|
iface->name = strdup (argv[0]);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set default values
|
|
|
|
|
*/
|
|
|
|
|
broadcastFlag = 1;
|
|
|
|
|
dp->txWaitTid = 0;
|
|
|
|
|
dp->rxBdCount = RX_BUF_COUNT;
|
|
|
|
|
dp->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF;
|
|
|
|
|
dp->irqInfo.name = (rtems_irq_symbolic_name) 5;
|
|
|
|
|
dp->port = 0x240;
|
|
|
|
|
dp->base = (unsigned char*) 0xD0000;
|
|
|
|
|
dp->bpar = 0xD0000;
|
|
|
|
|
iface->mtu = 1500;
|
|
|
|
|
iface->addr = Ip_addr;
|
|
|
|
|
iface->hwaddr = mallocw (EADDR_LEN);
|
|
|
|
|
memset (iface->hwaddr, 0x08, EADDR_LEN);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Parse arguments
|
|
|
|
|
*/
|
|
|
|
|
for (argIndex = 1 ; argIndex < (argc - 1) ; argIndex++) {
|
|
|
|
|
if (strcmp ("mtu", argv[argIndex]) == 0) {
|
|
|
|
|
iface->mtu = atoi (argv[++argIndex]);
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp ("broadcast", argv[argIndex]) == 0) {
|
|
|
|
|
if (*argv[++argIndex] == 'n')
|
|
|
|
|
broadcastFlag = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp ("rbuf", argv[argIndex]) == 0) {
|
|
|
|
|
dp->rxBdCount = atoi (argv[++argIndex]);
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp ("tbuf", argv[argIndex]) == 0) {
|
|
|
|
|
dp->txBdCount = atoi (argv[++argIndex]) * TX_BD_PER_BUF;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp ("ip", argv[argIndex]) == 0) {
|
|
|
|
|
iface->addr = resolve (argv[++argIndex]);
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp ("ether", argv[argIndex]) == 0) {
|
|
|
|
|
argIndex++;
|
|
|
|
|
gether (iface->hwaddr, argv[argIndex]);
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp ("irno", argv[argIndex]) == 0) {
|
|
|
|
|
dp->irqInfo.name = (rtems_irq_symbolic_name) atoi (argv[++argIndex]);
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp ("port", argv[argIndex]) == 0) {
|
|
|
|
|
sscanf(argv[++argIndex], "%x", &(dp->port));
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp ("bpar", argv[argIndex]) == 0) {
|
|
|
|
|
sscanf(argv[++argIndex], "%x", (unsigned *) &(dp->bpar));
|
|
|
|
|
dp->base = (unsigned char *)(dp->bpar);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
printf ("Argument %d (%s) is invalid.\n", argIndex, argv[argIndex]);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf ("Ethernet address: %s\n", pether (cbuf, iface->hwaddr));
|
|
|
|
|
printf ("Internet address: %s\n", inet_ntoa(iface->addr));
|
|
|
|
|
printf ("Irno: %X, port: %X, bpar: %X, base: %X\n",dp->irqInfo.name, dp->port,
|
|
|
|
|
(unsigned) dp->bpar, (unsigned) dp->base);
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
/*
|
|
|
|
|
* Fill in remainder of interface configuration
|
|
|
|
|
*/
|
|
|
|
|
iface->dev = i;
|
|
|
|
|
iface->raw = wd8003Enet_raw;
|
|
|
|
|
iface->stop = wd8003Enet_stop;
|
|
|
|
|
iface->show = wd8003Enet_show;
|
|
|
|
|
dp->iface = iface;
|
|
|
|
|
setencap (iface, "Ethernet");
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set up SCC hardware
|
|
|
|
|
*/
|
|
|
|
|
wd8003Enet_initialize_hardware (dp, broadcastFlag);
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Chain onto list of interfaces
|
|
|
|
|
*/
|
|
|
|
|
iface->next = Ifaces;
|
|
|
|
|
Ifaces = iface;
|
|
|
|
|
|
|
|
|
|
/* calibrate a delay loop for 2 milliseconds */
|
|
|
|
|
rtems_clock_get(RTEMS_CLOCK_GET_TICKS_PER_SECOND, &loopc );
|
|
|
|
|
loopc /= 500;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Start I/O daemons
|
|
|
|
|
*/
|
|
|
|
|
cp = if_name (iface, " tx");
|
|
|
|
|
iface->txproc = newproc (cp, 1024, if_tx, iface->dev, iface, NULL, 0);
|
|
|
|
|
free (cp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
struct wd_softc *sc;
|
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
int mtu;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find a free driver
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0 ; i < NWDDRIVER ; i++) {
|
|
|
|
|
sc = &wd_softc[i];
|
|
|
|
|
ifp = &sc->arpcom.ac_if;
|
|
|
|
|
if (ifp->if_softc == NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (i >= NWDDRIVER) {
|
|
|
|
|
printf ("Too many WD drivers.\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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->irno)
|
|
|
|
|
sc->irqInfo.name = config->irno;
|
|
|
|
|
else
|
|
|
|
|
sc->irqInfo.name = 5;
|
|
|
|
|
|
|
|
|
|
if (config->port)
|
|
|
|
|
sc->port = config->port;
|
|
|
|
|
else
|
|
|
|
|
sc->port = 0x240;
|
|
|
|
|
|
|
|
|
|
if (config->bpar) {
|
|
|
|
|
sc->bpar = config->bpar;
|
|
|
|
|
sc->base = (unsigned char*) config->bpar;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sc->bpar = 0xD0000;
|
|
|
|
|
sc->base = (unsigned char*) 0xD0000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sc->acceptBroadcast = !config->ignore_broadcast;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set up network interface values
|
|
|
|
|
*/
|
|
|
|
|
ifp->if_softc = sc;
|
|
|
|
|
ifp->if_unit = i + 1;
|
|
|
|
|
ifp->if_name = "wd";
|
|
|
|
|
ifp->if_mtu = mtu;
|
|
|
|
|
ifp->if_init = wd_init;
|
|
|
|
|
ifp->if_ioctl = wd_ioctl;
|
|
|
|
|
ifp->if_start = wd_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;
|
|
|
|
|
|
|
|
|
|
/* calibrate a delay loop for 2 milliseconds */
|
|
|
|
|
rtems_clock_get(RTEMS_CLOCK_GET_TICKS_PER_SECOND, &loopc );
|
|
|
|
|
loopc /= 500;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* init some variables
|
|
|
|
|
*/
|
|
|
|
|
overrun = 0;
|
|
|
|
|
resend = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Attach the interface
|
|
|
|
|
*/
|
|
|
|
|
if_attach (ifp);
|
|
|
|
|
ether_ifattach (ifp);
|
|
|
|
|
return 1;
|
|
|
|
|
};
|
|
|
|
|
|