summaryrefslogtreecommitdiffstats
path: root/bsps/i386
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-23 09:53:31 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-23 15:18:44 +0200
commit031df3914990db0336a0d386fb53558b05de467e (patch)
tree4661e22f0cdb3f9d06879f0194b77c75f62bac79 /bsps/i386
parentbsps: Move interrupt controller support to bsps (diff)
downloadrtems-031df3914990db0336a0d386fb53558b05de467e.tar.bz2
bsps: Move legacy network drivers to bsps
This patch is a part of the BSP source reorganization. Update #3285.
Diffstat (limited to 'bsps/i386')
-rw-r--r--bsps/i386/pc386/net/3c509.c1538
-rw-r--r--bsps/i386/pc386/net/3c509.h436
-rw-r--r--bsps/i386/pc386/net/elink.c82
-rw-r--r--bsps/i386/pc386/net/elink.h49
-rw-r--r--bsps/i386/pc386/net/ne2000.c1310
-rw-r--r--bsps/i386/pc386/net/wd8003.c647
6 files changed, 4062 insertions, 0 deletions
diff --git a/bsps/i386/pc386/net/3c509.c b/bsps/i386/pc386/net/3c509.c
new file mode 100644
index 0000000000..3cf7ec5226
--- /dev/null
+++ b/bsps/i386/pc386/net/3c509.c
@@ -0,0 +1,1538 @@
+/*
+ * Ported by Rosimildo da Silva.
+ * ConnectTel,Inc.
+ * e-mail: rdasilva@connecttel.com
+ *
+ * MODULE DESCRIPTION:
+ * RTEMS driver for 3COM 3C509 Ethernet Card.
+ * The driver has been tested on PC with a single network card.
+ *
+ *
+ * This driver was based on the FreeBSD implementation( if_ep.c ) of the 3c5x9
+ * family and on the network framework of the RTEMS network driver.
+ * ( WD80x3 by Eric Norum ).
+ * See notes below:
+ *
+ ******************************************************************************
+ * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Herb Peyerl.
+ * 4. The name of Herb Peyerl may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *******************************************************************************
+ *
+ * RTEMS driver for M68360 WD1 Ethernet
+ *
+ * W. Eric Norum
+ * Saskatchewan Accelerator Laboratory
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric@skatter.usask.ca
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <bsp.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <assert.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/libkern.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <bsp/irq.h>
+
+/* Local includes */
+#include "3c509.h"
+#include "elink.h"
+
+/* #define ET_MINLEN 60 */ /* minimum message length */
+
+/*
+ * Number of WDs supported by this driver
+ */
+#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 driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#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
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+
+/*
+#define RBUF_SIZE 1520
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+*/
+
+/* network driver name */
+#define NET_DRIVER_NAME "ep"
+
+/*
+ * Per device structure.
+ *
+ * XXX Note: id_conflicts should either become an array of things we're
+ * specifically allowed to conflict with or be subsumed into some
+ * more powerful mechanism for detecting and dealing with multiple types
+ * of non-fatal conflict. -jkh XXX
+ */
+struct isa_device
+{
+ int id_id; /* device id */
+ int id_unit; /* unit number */
+ int id_iobase; /* base i/o address */
+ u_int id_irq; /* interrupt request */
+};
+
+struct ep_board
+{
+ int epb_addr; /* address of this board */
+ char epb_used; /* was this entry already used for configuring ? */
+ /* data from EEPROM for later use */
+ u_short eth_addr[3]; /* Ethernet address */
+ u_short prod_id; /* product ID */
+ u_short res_cfg; /* resource configuration */
+};
+
+/*
+ * Ethernet software status per interface.
+ */
+struct ep_softc
+{
+ struct arpcom arpcom; /* Ethernet common part */
+ int ep_io_addr; /* i/o bus address */
+ struct mbuf *top, *mcur;
+ short cur_len;
+ u_short ep_connectors; /* Connectors on this card. */
+ u_char ep_connector; /* Configured connector. */
+ int stat; /* some flags */
+ struct ep_board *epb;
+ int unit;
+
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+ rtems_vector_number name;
+
+ int acceptBroadcast;
+
+ short tx_underrun;
+ short rx_no_first;
+ short rx_no_mbuf;
+ short rx_bpf_disc;
+ short rx_overrunf;
+ short rx_overrunl;
+};
+
+/* static unsigned long loopc; */
+static volatile unsigned long overrun;
+static volatile unsigned long resend;
+static struct ep_softc ep_softc[ NWDDRIVER ];
+static struct isa_device isa_dev[ NWDDRIVER ] =
+{
+ { 0, /* device id */
+ 0, /* unit number */
+ -1, /* base i/o address ??? */
+ 0 /* interrupt request ??? */
+ }
+};
+
+static u_long ep_unit;
+static int ep_boards;
+struct ep_board ep_board[ EP_MAX_BOARDS + 1];
+static int ep_current_tag = EP_LAST_TAG + 1;
+static char *ep_conn_type[] = {"UTP", "AUI", "???", "BNC"};
+
+#define ep_ftst(f) (sc->stat&(f))
+#define ep_fset(f) (sc->stat|=(f))
+#define ep_frst(f) (sc->stat&=~(f))
+
+/* forward declarations for functions */
+static int ep_attach( struct ep_softc *sc );
+static int ep_isa_probe( struct isa_device *is );
+static void epinit( struct ep_softc *sc );
+static void epread( register struct ep_softc *sc );
+static void epstart( struct ifnet *ifp );
+static void epread( register struct ep_softc *sc );
+static int ep_isa_attach( struct isa_device *is );
+static int get_eeprom_data( int id_port, int offset );
+static void ep_intr( struct ep_softc *sc );
+
+/* external functions */
+extern void Wait_X_ms( unsigned int timeToWait ); /* timer.c ??? */
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Writes a buffer of data to the I/O port. The data is sent to the
+ * port as 32 bits units( 4 bytes ).
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static __inline void outsl( unsigned short io_addr, uint8_t *out_data, int len )
+{
+ u_long *pl = ( u_long *)out_data;
+ while( len-- )
+ {
+ outport_long( io_addr, *pl );
+ pl++;
+ }
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Writes a buffer of data to the I/O port. The data is sent to the
+ * port as 16 bits units( 2 bytes ).
+ *
+ * RETURNS:
+ *
+ **********************************************************************************/
+static __inline void outsw( unsigned short io_addr, uint8_t *out_data, int len )
+{
+ u_short *ps = ( u_short *)out_data;
+ while( len-- )
+ {
+ outport_word( io_addr, *ps );
+ ps++;
+ }
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Writes a buffer of data to the I/O port. The data is sent to the
+ * port as 8 bits units( 1 byte ).
+ *
+ * RETURNS: nothing
+ *
+ **********************************************************************************/
+static __inline void outsb( unsigned short io_addr, uint8_t *out_data, int len )
+{
+ while( len-- )
+ {
+ outport_byte( io_addr, *out_data );
+ out_data++;
+ }
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Read a buffer of data from an I/O port. The data is read as 16 bits
+ * units or 2 bytes.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static __inline void insw( unsigned short io_addr, uint8_t *in_data, int len )
+{
+ u_short *ps = ( u_short *)in_data;
+ while( len-- )
+ {
+ inport_word( io_addr, *ps );
+ ps++;
+ }
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Read a buffer of data from an I/O port. The data is read as 32 bits
+ * units or 4 bytes.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static __inline void insl( unsigned short io_addr, uint8_t *in_data, int len )
+{
+ u_long *pl = ( u_long *)in_data;
+ while( len-- )
+ {
+ inport_long( io_addr, *pl );
+ pl++;
+ }
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Read a buffer of data from an I/O port. The data is read as 8 bits
+ * units or 1 bytes.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static __inline void insb( unsigned short io_addr, uint8_t *in_data, int len )
+{
+ while( len-- )
+ {
+ inport_byte( io_addr, *in_data++ );
+ }
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Writes a word to the I/O port.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+/*
+ * Routine to output a word as defined in FreeBSD.
+ */
+static __inline void outw( unsigned short io_addr, unsigned short out_data )
+{
+ outport_word( io_addr, out_data );
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Routine to read a word as defined in FreeBSD.
+ *
+ * RETURNS: nothing
+ *
+ **********************************************************************************/
+static __inline unsigned short inw( unsigned short io_addr )
+{
+ unsigned short in_data;
+ inport_word( io_addr, in_data );
+ return in_data;
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Routine to output a word as defined in FreeBSD.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static __inline void outb( unsigned short io_addr, uint8_t out_data )
+{
+ outport_byte( io_addr, out_data );
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Routine to read a word as defined in FreeBSD.
+ *
+ * RETURNS: byte read.
+ *
+ **********************************************************************************/
+static __inline uint8_t inb( unsigned short io_addr )
+{
+ uint8_t in_data;
+ inport_byte( io_addr, in_data );
+ return in_data;
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * We get eeprom data from the id_port given an offset into the eeprom.
+ * Basically; after the ID_sequence is sent to all of the cards; they enter
+ * the ID_CMD state where they will accept command requests. 0x80-0xbf loads
+ * the eeprom data. We then read the port 16 times and with every read; the
+ * cards check for contention (ie: if one card writes a 0 bit and another
+ * writes a 1 bit then the host sees a 0. At the end of the cycle; each card
+ * compares the data on the bus; if there is a difference then that card goes
+ * into ID_WAIT state again). In the meantime; one bit of data is returned in
+ * the AX register which is conveniently returned to us by inb(). Hence; we
+ * read 16 times getting one bit of data with each read.
+ *
+ * RETURNS: 16 bit word from the EEPROM
+ *
+ **********************************************************************************/
+static int get_eeprom_data( int id_port, int offset )
+{
+ int i, data = 0;
+ outb(id_port, 0x80 + offset);
+ Wait_X_ms( 1 );
+ for (i = 0; i < 16; i++)
+ data = (data << 1) | (inw(id_port) & 1);
+ return( data );
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * Driver interrupt handler. This routine is called by the RTEMS kernel when this
+ * interrupt is raised.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static rtems_isr ap_interrupt_handler(void *arg)
+{
+ struct ep_softc *sc = (struct ep_softc *)arg;
+
+ /* de-activate any pending interrrupt, and sent and event to interrupt task
+ * to process all events required by this interrupt.
+ */
+ outw( BASE + EP_COMMAND, SET_INTR_MASK ); /* disable all Ints */
+ rtems_bsdnet_event_send( sc->rxDaemonTid, INTERRUPT_EVENT );
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * Initializes the ethernet hardware.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void _3c509_initialize_hardware (struct ep_softc *sc)
+{
+ rtems_status_code status;
+
+ epinit( sc );
+
+ /*
+ * Set up interrupts
+ */
+ printf ("3c509: IRQ with Kernel: %d\n", (int)sc->name );
+ status = rtems_interrupt_handler_install(
+ sc->name,
+ "3c509",
+ RTEMS_INTERRUPT_UNIQUE,
+ ap_interrupt_handler,
+ sc
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Driver interrupt daemon.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void _3c509_rxDaemon (void *arg)
+{
+ struct ep_softc *dp = (struct ep_softc *)&ep_softc[ 0 ];
+ rtems_event_set events;
+
+ printf ("3C509: RX Daemon is starting.\n");
+ for( ;; )
+ {
+ /* printk( "R-" ); */
+ rtems_bsdnet_event_receive( INTERRUPT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events );
+ /* printk( "R+" ); */
+ ep_intr( dp );
+ epstart( &dp->arpcom.ac_if );
+ }
+ printf ("3C509: RX Daemon is finishing.\n");
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Driver transmit daemon
+ *
+ * RETURNS:
+ *
+ **********************************************************************************/
+static void _3c509_txDaemon (void *arg)
+{
+ struct ep_softc *sc = (struct ep_softc *)&ep_softc[0];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ rtems_event_set events;
+
+ printf ("3C509: TX Daemon is starting.\n");
+ for( ;; )
+ {
+ /*
+ * Wait for packet
+ */
+ /* printk( "T-\n" ); */
+ rtems_bsdnet_event_receive( START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events );
+ /* printk( "T+\n" ); */
+ epstart( ifp );
+ while( ifp->if_flags & IFF_OACTIVE )
+ epstart( ifp );
+ }
+ printf ("3C509: TX Daemon is finishing.\n");
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Activates the trabsmitter task...
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void _3c509_start (struct ifnet *ifp)
+{
+ struct ep_softc *sc = ifp->if_softc;
+ /* printk ("S"); */
+ ifp->if_flags |= IFF_OACTIVE;
+ rtems_bsdnet_event_send( sc->txDaemonTid, START_TRANSMIT_EVENT );
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Initialize and start the device
+ *
+ * RETURNS:
+ *
+ **********************************************************************************/
+static void _3c509_init (void *arg)
+{
+ struct ep_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ printf ("3C509: Initialization called.\n");
+ if (sc->txDaemonTid == 0) {
+
+ /*
+ * Set up WD hardware
+ */
+ _3c509_initialize_hardware (sc);
+ printf ("3C509: starting network driver tasks..\n");
+ /*
+ * Start driver tasks
+ */
+ sc->txDaemonTid = rtems_bsdnet_newproc ("APtx", 4096, _3c509_txDaemon, sc);
+ sc->rxDaemonTid = rtems_bsdnet_newproc ("APrx", 4096, _3c509_rxDaemon, sc);
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Stop the device
+ *
+ * RETURNS:
+ *
+ **********************************************************************************/
+static void _3c509_stop (struct ep_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ printf ("3C509: stop() called.\n");
+ /*
+ * Stop the transmitter
+ */
+ outw(BASE + EP_COMMAND, RX_DISABLE);
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ outw(BASE + EP_COMMAND, TX_DISABLE);
+ outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK);
+ outw(BASE + EP_COMMAND, SET_INTR_MASK);
+ outw(BASE + EP_COMMAND, SET_RX_FILTER);
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Show interface statistics
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void _3c509_stats (struct ep_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ printf ("3C509: stats() called.\n");
+ printf("\tStat: %x\n", sc->stat);
+ printf("\tIpackets=%ld, Opackets=%ld\n", ifp->if_ipackets, ifp->if_opackets);
+ printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n",
+ sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf,
+ sc->rx_overrunl, sc->tx_underrun );
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Driver ioctl handler
+ *
+ * RETURNS:
+ *
+ **********************************************************************************/
+static int _3c509_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct ep_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ printf ("3C509: ioctl() called.\n");
+ 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:
+ _3c509_stop (sc);
+ break;
+
+ case IFF_UP:
+ _3c509_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ _3c509_stop (sc);
+ _3c509_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ _3c509_stats( sc );
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * Attaches this network driver to the system. This function is called by the network
+ * interface during the initialization of the system.
+ *
+ * RETURNS: - 1 - success; 0 - fail to initialize
+ *
+ **********************************************************************************/
+int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *config )
+{
+ struct ep_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int i;
+
+ printf ("3C509: attach() called.\n");
+
+ /*
+ * init some variables
+ */
+ overrun = 0;
+ resend = 0;
+ ep_unit = 0;
+ ep_boards = 0;
+
+ /*
+ * Find a free driver
+ */
+ for (i = 0 ; i < NWDDRIVER ; i++) {
+ sc = &ep_softc[i];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc == NULL)
+ break;
+ }
+ if (i >= NWDDRIVER)
+ {
+ printf ("Too many 3C509 drivers.\n");
+ return 0;
+ }
+
+ /*
+ * Process options
+ */
+ if( config->hardware_address )
+ {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ }
+ else
+ {
+ /* set it to something ... */
+ memset (sc->arpcom.ac_enaddr, 0x08,ETHER_ADDR_LEN);
+ }
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ if (config->irno)
+ sc->name = config->irno;
+ else
+ sc->name = 10;
+
+ if (config->port)
+ sc->ep_io_addr = config->port;
+ else
+ sc->ep_io_addr = 0x300;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ printf ("3C509: isa_probe() looking for a card...\n");
+ if( !ep_isa_probe( &isa_dev[ 0 ] ) )
+ {
+ printf ("3C509: isa_probe() fail to find a board.\n");
+ return 0;
+ }
+
+ /* A board has been found, so proceed with the installation of the driver */
+ ep_isa_attach( &isa_dev[ 0 ] );
+ /*
+ * Set up network interface values
+ */
+
+ ifp->if_softc = sc;
+ ifp->if_unit = i;
+ ifp->if_name = NET_DRIVER_NAME;
+ ifp->if_mtu = mtu;
+ ifp->if_init = _3c509_init;
+ ifp->if_ioctl = _3c509_ioctl;
+ ifp->if_start = _3c509_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ if( ifp->if_snd.ifq_maxlen == 0 )
+ {
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ }
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ printf ("3C509: attach() is complete.\n");
+ return 1;
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * This function looks for a 3COM card 3c5x9 in an isa bus. If a board is found, it
+ * returns a structure describing the caracteristics of the card. It returns zero when
+ * card can not be found.
+ *
+ * RETURNS: 0 - fail - could not find a card...
+ * <> description of the card.
+ *
+ **********************************************************************************/
+static struct ep_board *ep_look_for_board_at( struct isa_device *is )
+{
+ int data, i, j, id_port = ELINK_ID_PORT;
+ int count = 0;
+
+ if(ep_current_tag == (EP_LAST_TAG + 1) )
+ {
+ /* Come here just one time */
+ ep_current_tag--;
+
+ /* Look for the ISA boards. Init and leave them actived */
+ outb(id_port, 0);
+ outb(id_port, 0);
+
+ elink_idseq(0xCF);
+ elink_reset();
+ Wait_X_ms( 10 ); /* RPS: assuming delay in miliseconds */
+ for (i = 0; i < EP_MAX_BOARDS; i++)
+ {
+ outb(id_port, 0);
+ outb(id_port, 0);
+ elink_idseq(0xCF);
+
+ data = get_eeprom_data(id_port, EEPROM_MFG_ID);
+ if (data != MFG_ID)
+ break;
+
+ /* resolve contention using the Ethernet address */
+ for (j = 0; j < 3; j++)
+ get_eeprom_data(id_port, j);
+
+ /* and save this address for later use */
+
+ for (j = 0; j < 3; j++)
+ ep_board[ep_boards].eth_addr[j] = get_eeprom_data(id_port, j);
+
+ ep_board[ep_boards].res_cfg = get_eeprom_data(id_port, EEPROM_RESOURCE_CFG);
+ ep_board[ep_boards].prod_id = get_eeprom_data(id_port, EEPROM_PROD_ID);
+ ep_board[ep_boards].epb_used = 0;
+#ifdef PC98
+ ep_board[ep_boards].epb_addr =
+ (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x100 + 0x40d0;
+#else
+ ep_board[ep_boards].epb_addr =
+ (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200;
+ if (ep_board[ep_boards].epb_addr > 0x3E0)
+ /* Board in EISA configuration mode */
+ continue;
+#endif /* PC98 */
+
+ outb(id_port, ep_current_tag); /* tags board */
+ outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG);
+ ep_boards++;
+ count++;
+ ep_current_tag--;
+ }
+ ep_board[ep_boards].epb_addr = 0;
+ if( count )
+ {
+ printf("%d 3C5x9 board(s) on ISA found at", count);
+ for (j = 0; ep_board[j].epb_addr; j++)
+ if( ep_board[j].epb_addr <= 0x3E0 )
+ printf(" 0x%x", ep_board[j].epb_addr );
+ printf("\n");
+ }
+ }
+
+ /* we have two cases:
+ *
+ * 1. Device was configured with 'port ?'
+ * In this case we search for the first unused card in list
+ *
+ * 2. Device was configured with 'port xxx'
+ * In this case we search for the unused card with that address
+ *
+ */
+
+ if (IS_BASE == -1)
+ { /* port? */
+ for (i = 0; ep_board[i].epb_addr && ep_board[i].epb_used; i++) ;
+ if (ep_board[i].epb_addr == 0)
+ return 0;
+
+ IS_BASE = ep_board[i].epb_addr;
+ ep_board[i].epb_used = 1;
+ return &ep_board[ i ];
+ }
+ else
+ {
+ for (i = 0; ep_board[i].epb_addr && ep_board[i].epb_addr != IS_BASE; i++ ) ;
+ if (ep_board[i].epb_used || ep_board[i].epb_addr != IS_BASE)
+ return 0;
+ if (inw(IS_BASE + EP_W0_EEPROM_COMMAND) & EEPROM_TST_MODE)
+ {
+ printf("ep%d: 3c5x9 at 0x%x in PnP mode. Disable PnP mode!\n",
+ is->id_unit, IS_BASE );
+ }
+ ep_board[i].epb_used = 1;
+ return &ep_board[i];
+ }
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * This routine checks if there card installed on the machine.
+ *
+ * RETURNS: 0 - no card founded.
+ * 16 - size of the IO range for the card.
+ *
+ **********************************************************************************/
+static int ep_isa_probe( struct isa_device *is )
+{
+ struct ep_softc *sc;
+ struct ep_board *epb;
+ u_short k;
+
+ /* try to find a 3COM 3c5x9 .... */
+ if( (epb = ep_look_for_board_at(is)) == 0 )
+ return (0);
+
+ sc = &ep_softc[ 0 ];
+ sc->ep_io_addr = epb->epb_addr;
+ sc->epb = epb;
+
+ /*
+ * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be
+ * 0x9[0-f]50 (IBM-PC)
+ * 0x9[0-f]5[0-f] (PC-98)
+ */
+ GO_WINDOW(0);
+ k = sc->epb->prod_id;
+#ifdef PC98
+ if ((k & 0xf0f0) != (PROD_ID & 0xf0f0))
+ {
+#else
+ if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
+ {
+#endif
+ printf("ep_isa_probe: ignoring model %04x\n", k );
+/* ep_unit--; */
+ return (0);
+ }
+ k = sc->epb->res_cfg;
+ k >>= 12;
+
+ /* Now we have two cases again:
+ *
+ * 1. Device was configured with 'irq?'
+ * In this case we use irq read from the board
+ *
+ * 2. Device was configured with 'irq xxx'
+ * In this case we set up the board to use specified interrupt
+ *
+ */
+
+ if (is->id_irq == 0)
+ { /* irq? */
+ is->id_irq = ( k == 2 ) ? 9 : k;
+ }
+
+ sc->stat = 0; /* 16 bit access */
+
+ /* By now, the adapter is already activated */
+
+ return (EP_IOSIZE); /* 16 bytes of I/O space used. */
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * This routine attaches this network driver and the network interface routines.
+ *
+ * RETURNS: 0 - failed to attach
+ * 1 - success
+ *
+ **********************************************************************************/
+static int ep_isa_attach( struct isa_device *is )
+{
+ struct ep_softc *sc = &ep_softc[ 0 ];
+ u_short config;
+ int irq;
+
+ sc->ep_connectors = 0;
+ config = inw( IS_BASE + EP_W0_CONFIG_CTRL );
+ if (config & IS_AUI)
+ {
+ sc->ep_connectors |= AUI;
+ }
+ if (config & IS_BNC)
+ {
+ sc->ep_connectors |= BNC;
+ }
+ if (config & IS_UTP)
+ {
+ sc->ep_connectors |= UTP;
+ }
+ if( !(sc->ep_connectors & 7) )
+ printf( "no connectors!" );
+ sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS;
+
+ /*
+ * Write IRQ value to board
+ */
+
+ irq = is->id_irq;
+ /* update the interrupt line number to registered with kernel */
+ sc->name = irq;
+
+ GO_WINDOW( 0 );
+ SET_IRQ( BASE, irq );
+
+ printf( "3C509: I/O=0x%x, IRQ=%d, CONNECTOR=%s, ",
+ sc->ep_io_addr, (int)sc->name,ep_conn_type[ sc->ep_connector ] );
+
+ ep_attach( sc );
+ return 1;
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Completes the initialization/attachement of the driver.
+ *
+ * RETURNS: 0 - ok.
+ *
+ **********************************************************************************/
+static int ep_attach( struct ep_softc *sc )
+{
+ u_short *p;
+ int i;
+
+ /*
+ * Setup the station address
+ */
+ p = (u_short *) &sc->arpcom.ac_enaddr;
+ GO_WINDOW(2);
+ printf("ADDRESS=" );
+ for (i = 0; i < 3; i++)
+ {
+ p[i] = htons( sc->epb->eth_addr[i] );
+ outw( BASE + EP_W2_ADDR_0 + (i * 2), ntohs( p[i] ) );
+ printf("%04x ", (u_short)ntohs( p[i] ) );
+ }
+ printf("\n" );
+
+ sc->rx_no_first = sc->rx_no_mbuf =
+ sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl =
+ sc->tx_underrun = 0;
+
+ ep_fset( F_RX_FIRST );
+ sc->top = sc->mcur = 0;
+ return 0;
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * Initializes the card.
+ * The order in here seems important. Otherwise we may not receive interrupts. ?!
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void epinit( struct ep_softc *sc )
+{
+ register struct ifnet *ifp = &sc->arpcom.ac_if;
+ int i, j;
+
+ while( inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS ) ;
+ GO_WINDOW(0);
+ outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
+ GO_WINDOW(4);
+ outw(BASE + EP_W4_MEDIA_TYPE, DISABLE_UTP);
+ GO_WINDOW(0);
+
+ /* Disable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, 0);
+
+ /* Enable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
+
+ GO_WINDOW(2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < 6; i++)
+ outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]);
+
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+
+ /* Window 1 is operating window */
+ GO_WINDOW(1);
+ for (i = 0; i < 31; i++)
+ inb(BASE + EP_W1_TX_STATUS);
+
+ /* get rid of stray intr's */
+ outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
+
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS);
+
+ outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS);
+
+ if (ifp->if_flags & IFF_PROMISC)
+ outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
+ FIL_GROUP | FIL_BRDCST | FIL_ALL);
+ else
+ outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_GROUP | FIL_BRDCST);
+
+ /*
+ * S.B.
+ *
+ * Now behavior was slightly changed:
+ *
+ * if any of flags link[0-2] is used and its connector is
+ * physically present the following connectors are used:
+ *
+ * link0 - AUI * highest precedence
+ * link1 - BNC
+ * link2 - UTP * lowest precedence
+ *
+ * If none of them is specified then
+ * connector specified in the EEPROM is used
+ * (if present on card or AUI if not).
+ *
+ */
+
+ /* Set the xcvr. */
+ if (ifp->if_flags & IFF_LINK0 && sc->ep_connectors & AUI)
+ {
+ i = ACF_CONNECTOR_AUI;
+ }
+ else if (ifp->if_flags & IFF_LINK1 && sc->ep_connectors & BNC)
+ {
+ i = ACF_CONNECTOR_BNC;
+ }
+ else if (ifp->if_flags & IFF_LINK2 && sc->ep_connectors & UTP)
+ {
+ i = ACF_CONNECTOR_UTP;
+ }
+ else
+ {
+ i = sc->ep_connector;
+ }
+ GO_WINDOW(0);
+ j = inw(BASE + EP_W0_ADDRESS_CFG) & 0x3fff;
+ outw(BASE + EP_W0_ADDRESS_CFG, j | (i << ACF_CONNECTOR_BITS));
+
+ switch(i)
+ {
+ case ACF_CONNECTOR_UTP:
+ if (sc->ep_connectors & UTP)
+ {
+ GO_WINDOW(4);
+ outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
+ }
+ break;
+
+ case ACF_CONNECTOR_BNC:
+ if (sc->ep_connectors & BNC)
+ {
+ outw(BASE + EP_COMMAND, START_TRANSCEIVER);
+ Wait_X_ms( 1 );
+ }
+ break;
+
+ case ACF_CONNECTOR_AUI:
+ /* nothing to do */
+ break;
+
+ default:
+ printf("ep%d: strange connector type in EEPROM: assuming AUI\n", sc->unit);
+ break;
+ }
+
+ outw(BASE + EP_COMMAND, RX_ENABLE);
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE; /* just in case */
+
+ sc->rx_no_first = sc->rx_no_mbuf =
+ sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl =
+ sc->tx_underrun = 0;
+
+ ep_fset(F_RX_FIRST);
+ if( sc->top )
+ {
+ m_freem( sc->top );
+ sc->top = sc->mcur = 0;
+ }
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
+ outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16);
+
+ /*
+ * Store up a bunch of mbuf's for use later. (MAX_MBS). First we free up
+ * any that we had in case we're being called from intr or somewhere
+ * else.
+ */
+
+ GO_WINDOW(1);
+}
+
+static const char padmap[] = {0, 3, 2, 1};
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Routine to transmit frames to the card.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void epstart( struct ifnet *ifp )
+{
+ register struct ep_softc *sc = ifp->if_softc;
+ register u_int len;
+ register struct mbuf *m;
+ struct mbuf *top;
+ int pad;
+
+ while( inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS )
+ ;
+startagain:
+ /* printk( "S-" ); */
+
+ /* Sneak a peek at the next packet */
+ m = ifp->if_snd.ifq_head;
+ if (m == 0)
+ {
+ ifp->if_flags &= ~IFF_OACTIVE;
+ return;
+ }
+
+ for( len = 0, top = m; m; m = m->m_next )
+ len += m->m_len;
+
+ pad = padmap[ len & 3 ];
+
+ /*
+ * The 3c509 automatically pads short packets to minimum ethernet length,
+ * but we drop packets that are too large. Perhaps we should truncate
+ * them instead?
+ */
+ if( len + pad > ETHER_MAX_LEN )
+ {
+ /* packet is obviously too large: toss it */
+ ++ifp->if_oerrors;
+ IF_DEQUEUE( &ifp->if_snd, m );
+ m_freem( m );
+ goto readcheck;
+ }
+ if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4)
+ {
+ /* no room in FIFO */
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4));
+ /* make sure */
+ if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4)
+ {
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ }
+ }
+ IF_DEQUEUE( &ifp->if_snd, m );
+ outw(BASE + EP_W1_TX_PIO_WR_1, len);
+ outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */
+
+ for (top = m; m != 0; m = m->m_next)
+ {
+ if( ep_ftst(F_ACCESS_32_BITS ) )
+ {
+ outsl( BASE + EP_W1_TX_PIO_WR_1, mtod(m, uint8_t *), m->m_len / 4 );
+ if( m->m_len & 3 )
+ outsb(BASE + EP_W1_TX_PIO_WR_1, mtod(m, uint8_t *) + (m->m_len & (~3)), m->m_len & 3 );
+ }
+ else
+ {
+ outsw( BASE + EP_W1_TX_PIO_WR_1, mtod(m, uint8_t *), m->m_len / 2 );
+ if( m->m_len & 1 )
+ outb( BASE + EP_W1_TX_PIO_WR_1, *(mtod(m, uint8_t *) + m->m_len - 1) );
+ }
+ }
+ while( pad-- )
+ {
+ outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */
+ }
+ ifp->if_timer = 2;
+ ifp->if_opackets++;
+ m_freem(top);
+
+/* goto startagain; */
+ /*
+ * Is another packet coming in? We don't want to overflow the tiny RX
+ * fifo.
+ */
+readcheck:
+ if( inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK )
+ {
+ /*
+ * we check if we have packets left, in that case we prepare to come
+ * back later
+ */
+ if( ifp->if_snd.ifq_head )
+ {
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8);
+ }
+ return;
+ }
+ goto startagain;
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Routine to read frames from the card.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void epread( register struct ep_softc *sc )
+{
+ struct ether_header *eh;
+ struct mbuf *top, *mcur, *m;
+ struct ifnet *ifp;
+ int lenthisone;
+
+ short rx_fifo2, status;
+ register short rx_fifo;
+
+ ifp = &sc->arpcom.ac_if;
+ status = inw( BASE + EP_W1_RX_STATUS );
+
+read_again:
+
+ if (status & ERR_RX)
+ {
+ ++ifp->if_ierrors;
+ if( status & ERR_RX_OVERRUN )
+ {
+ /*
+ * we can think the rx latency is actually greather than we
+ * expect
+ */
+ if( ep_ftst(F_RX_FIRST) )
+ sc->rx_overrunf++;
+ else
+ sc->rx_overrunl++;
+
+ }
+ goto out;
+ }
+ rx_fifo = rx_fifo2 = status & RX_BYTES_MASK;
+
+ if( ep_ftst( F_RX_FIRST ) )
+ {
+ MGETHDR( m, M_DONTWAIT, MT_DATA );
+ if( !m )
+ goto out;
+ if( rx_fifo >= MINCLSIZE )
+ MCLGET( m, M_DONTWAIT );
+ sc->top = sc->mcur = top = m;
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+ top->m_data += EOFF;
+
+ /* Read what should be the header. */
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, uint8_t *), sizeof(struct ether_header) / 2);
+ top->m_len = sizeof(struct ether_header);
+ rx_fifo -= sizeof(struct ether_header);
+ sc->cur_len = rx_fifo2;
+ }
+ else
+ {
+ /* come here if we didn't have a complete packet last time */
+ top = sc->top;
+ m = sc->mcur;
+ sc->cur_len += rx_fifo2;
+ }
+
+ /* Reads what is left in the RX FIFO */
+ while (rx_fifo > 0)
+ {
+ lenthisone = min( rx_fifo, M_TRAILINGSPACE(m) );
+ if( lenthisone == 0 )
+ { /* no room in this one */
+ mcur = m;
+ MGET(m, M_WAIT, MT_DATA);
+ if (!m)
+ goto out;
+ if (rx_fifo >= MINCLSIZE)
+ MCLGET(m, M_WAIT);
+ m->m_len = 0;
+ mcur->m_next = m;
+ lenthisone = min(rx_fifo, M_TRAILINGSPACE(m));
+ }
+ if( ep_ftst( F_ACCESS_32_BITS ) )
+ { /* default for EISA configured cards*/
+ insl( BASE + EP_W1_RX_PIO_RD_1, mtod(m, uint8_t *) + m->m_len, lenthisone / 4);
+ m->m_len += (lenthisone & ~3);
+ if (lenthisone & 3)
+ insb(BASE + EP_W1_RX_PIO_RD_1, mtod(m, uint8_t *) + m->m_len, lenthisone & 3);
+ m->m_len += (lenthisone & 3);
+ }
+ else
+ {
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, uint8_t *) + m->m_len, lenthisone / 2);
+ m->m_len += lenthisone;
+ if( lenthisone & 1 )
+ *(mtod(m, uint8_t *) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1);
+ }
+ rx_fifo -= lenthisone;
+ }
+
+ if( status & ERR_RX_INCOMPLETE)
+ { /* we haven't received the complete packet */
+ sc->mcur = m;
+ sc->rx_no_first++; /* to know how often we come here */
+ ep_frst( F_RX_FIRST );
+ if( !((status = inw(BASE + EP_W1_RX_STATUS)) & ERR_RX_INCOMPLETE) )
+ {
+ /* we see if by now, the packet has completly arrived */
+ goto read_again;
+ }
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_NEXT_EARLY_THRESH);
+ return;
+ }
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ ++ifp->if_ipackets;
+ ep_fset(F_RX_FIRST);
+ top->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ top->m_pkthdr.len = sc->cur_len;
+
+ eh = mtod(top, struct ether_header *);
+ m_adj(top, sizeof(struct ether_header));
+ ether_input(ifp, eh, top);
+ sc->top = 0;
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
+ return;
+
+out:
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ if (sc->top)
+ {
+ m_freem(sc->top);
+ sc->top = 0;
+ sc->rx_no_mbuf++;
+ }
+ ep_fset(F_RX_FIRST);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ;
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * This routine handles interrupts. It is called from the "RX" task whenever
+ * the ISR post an event to the task.
+ * This is basically the "isr" from the FreeBSD driver.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void ep_intr( struct ep_softc *sc )
+{
+ register int status;
+ struct ifnet *ifp;
+ ifp = &sc->arpcom.ac_if;
+
+rescan:
+
+ /* printk( "I-" ); */
+ while( ( status = inw(BASE + EP_STATUS)) & S_5_INTS )
+ {
+ /* first acknowledge all interrupt sources */
+ outw( BASE + EP_COMMAND, ACK_INTR | ( status & S_MASK ) );
+
+ if( status & ( S_RX_COMPLETE | S_RX_EARLY ) )
+ {
+ epread( sc );
+ continue;
+ }
+ if (status & S_TX_AVAIL)
+ {
+ /* we need ACK */
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ GO_WINDOW(1);
+ inw(BASE + EP_W1_FREE_TX);
+ epstart(ifp);
+ }
+ if (status & S_CARD_FAILURE)
+ {
+ ifp->if_timer = 0;
+ printf("\nep%d:\n\tStatus: %x\n", sc->unit, status);
+ GO_WINDOW(4);
+ printf("\tFIFO Diagnostic: %x\n", inw(BASE + EP_W4_FIFO_DIAG));
+ printf("\tStat: %x\n", sc->stat);
+ printf("\tIpackets=%ld, Opackets=%ld\n", ifp->if_ipackets, ifp->if_opackets);
+ printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n",
+ sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf,
+ sc->rx_overrunl, sc->tx_underrun);
+
+ printf("ep%d: Status: %x (input buffer overflow)\n", sc->unit, status);
+ ++ifp->if_ierrors;
+ epinit(sc);
+ return;
+ }
+ if (status & S_TX_COMPLETE)
+ {
+ ifp->if_timer = 0;
+ /* we need ACK. we do it at the end */
+ /*
+ * We need to read TX_STATUS until we get a 0 status in order to
+ * turn off the interrupt flag.
+ */
+ while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE)
+ {
+ if (status & TXS_SUCCES_INTR_REQ)
+ ;
+ else if( status & (TXS_UNDERRUN | TXS_JABBER | TXS_MAX_COLLISION ) )
+ {
+ outw(BASE + EP_COMMAND, TX_RESET);
+ if (status & TXS_UNDERRUN)
+ {
+ sc->tx_underrun++;
+ }
+ else
+ {
+ if( status & TXS_JABBER )
+ ;
+ else /* TXS_MAX_COLLISION - we shouldn't get here */
+ ++ifp->if_collisions;
+ }
+ ++ifp->if_oerrors;
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+ /*
+ * To have a tx_avail_int but giving the chance to the
+ * Reception
+ */
+ if( ifp->if_snd.ifq_head )
+ {
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8);
+ }
+ }
+ outb( BASE + EP_W1_TX_STATUS, 0x0 ); /* pops up the next status */
+ } /* while */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ GO_WINDOW(1);
+ inw(BASE + EP_W1_FREE_TX);
+ epstart( ifp );
+ } /* end TX_COMPLETE */
+ }
+ outw(BASE + EP_COMMAND, C_INTR_LATCH); /* ACK int Latch */
+ if( (status = inw(BASE + EP_STATUS) ) & S_5_INTS )
+ goto rescan;
+
+ /* re-enable Ints */
+ outw( BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS );
+ /* printk( "I+" ); */
+}
diff --git a/bsps/i386/pc386/net/3c509.h b/bsps/i386/pc386/net/3c509.h
new file mode 100644
index 0000000000..975342970a
--- /dev/null
+++ b/bsps/i386/pc386/net/3c509.h
@@ -0,0 +1,436 @@
+/**
+ * @file
+ *
+ * @ingroup pc386_3c509
+ *
+ * @brief 3C509 PC card support.
+ */
+
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2. The name
+ * of the author may not be used to endorse or promote products derived from
+ * this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by:
+ *
+ October 2, 1994
+
+ Modified by: Andres Vega Garcia
+
+ INRIA - Sophia Antipolis, France
+ e-mail: avega@sophia.inria.fr
+ finger: avega@pax.inria.fr
+
+ */
+/*
+ * Promiscuous mode added and interrupt logic slightly changed
+ * to reduce the number of adapter failures. Transceiver select
+ * logic changed to use value from EEPROM. Autoconfiguration
+ * features added.
+ * Done by:
+ * Serge Babkin
+ * Chelindbank (Chelyabinsk, Russia)
+ * babkin@hq.icb.chel.su
+ */
+
+/*
+ * Pccard support for 3C589 by:
+ * HAMADA Naoki
+ * nao@tom-yam.or.jp
+ */
+
+/**
+ * @defgroup pc386_3c509 3C509 Support
+ *
+ * @ingroup i386_pc386
+ *
+ * @brief 3C509 support.
+ */
+
+/*
+typedef unsigned short u_short;
+typedef unsigned long u_long;
+typedef unsigned char u_char;
+*/
+
+/*
+ * Some global constants
+ */
+#define F_RX_FIRST 0x1
+#define F_PROMISC 0x8
+#define F_ACCESS_32_BITS 0x100
+
+#define TX_INIT_RATE 16
+#define TX_INIT_MAX_RATE 64
+#define RX_INIT_LATENCY 64
+#define RX_INIT_EARLY_THRESH 208 /* not less than MINCLSIZE */
+#define RX_NEXT_EARLY_THRESH 500
+
+#define EEPROMSIZE 0x40
+#define MAX_EEPROMBUSY 1000
+#define EP_LAST_TAG 0xd7
+#define EP_MAX_BOARDS 16
+/*
+ * This `ID' port is a mere hack. There's currently no chance to register
+ * it with config's idea of the ports that are in use.
+ *
+ * "After the automatic configuration is completed, the IDS is in its initial
+ * state (ID-WAIT), and it monitors all write access to I/O port 01x0h, where
+ * 'x' is any hex digit. If a zero is written to any one of these ports, then
+ * that address is remembered and becomes the ID port. A second zero written
+ * to that port resets the ID sequence to its initial state. The IDS watches
+ * for the ID sequence to be written to the ID port."
+ *
+ * We prefer 0x110 over 0x100 so to not conflict with the Plaque&Pray
+ * ports.
+ */
+#define EP_ID_PORT 0x110
+#define EP_IOSIZE 16 /* 16 bytes of I/O space used. */
+
+/*
+ * some macros to acces long named fields
+ */
+#define IS_BASE (is->id_iobase)
+#define BASE (sc->ep_io_addr)
+
+/*
+ * Commands to read/write EEPROM trough EEPROM command register (Window 0,
+ * Offset 0xa)
+ */
+#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
+#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
+#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
+#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
+
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+
+/*
+ * Some short functions, worth to let them be a macro
+ */
+#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY)
+#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x))
+
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existence of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a
+ * command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status
+ * reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window
+ * reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+/* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+/* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+/* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+/* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+/* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+/* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+/* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+/* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+/* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms
+ * after issuing */
+#define WINDOW_SELECT (u_short) (0x1<<11)
+#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to
+ * determine whether
+ * this is needed. If
+ * so; wait 800 uSec
+ * before using trans-
+ * ceiver. */
+#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on
+ * power-up */
+#define RX_ENABLE (u_short) (0x4<<11)
+#define RX_RESET (u_short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11)
+#define TX_ENABLE (u_short) (0x9<<11)
+#define TX_DISABLE (u_short) (0xa<<11)
+#define TX_RESET (u_short) (0xb<<11)
+#define REQ_INTR (u_short) (0xc<<11)
+#define SET_INTR_MASK (u_short) (0xe<<11)
+#define SET_RD_0_MASK (u_short) (0xf<<11)
+#define SET_RX_FILTER (u_short) (0x10<<11)
+#define FIL_INDIVIDUAL (u_short) (0x1)
+#define FIL_GROUP (u_short) (0x2)
+#define FIL_BRDCST (u_short) (0x4)
+#define FIL_ALL (u_short) (0x8)
+#define SET_RX_EARLY_THRESH (u_short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11)
+#define SET_TX_START_THRESH (u_short) (0x13<<11)
+#define STATS_ENABLE (u_short) (0x15<<11)
+#define STATS_DISABLE (u_short) (0x16<<11)
+#define STOP_TRANSCEIVER (u_short) (0x17<<11)
+/*
+ * The following C_* acknowledge the various interrupts. Some of them don't
+ * do anything. See the manual.
+ */
+#define ACK_INTR (u_short) (0x6800)
+#define C_INTR_LATCH (u_short) (ACK_INTR|0x1)
+#define C_CARD_FAILURE (u_short) (ACK_INTR|0x2)
+#define C_TX_COMPLETE (u_short) (ACK_INTR|0x4)
+#define C_TX_AVAIL (u_short) (ACK_INTR|0x8)
+#define C_RX_COMPLETE (u_short) (ACK_INTR|0x10)
+#define C_RX_EARLY (u_short) (ACK_INTR|0x20)
+#define C_INT_RQD (u_short) (ACK_INTR|0x40)
+#define C_UPD_STATS (u_short) (ACK_INTR|0x80)
+#define C_MASK (u_short) 0xFF /* mask of C_* */
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (u_short) (0x1)
+#define S_CARD_FAILURE (u_short) (0x2)
+#define S_TX_COMPLETE (u_short) (0x4)
+#define S_TX_AVAIL (u_short) (0x8)
+#define S_RX_COMPLETE (u_short) (0x10)
+#define S_RX_EARLY (u_short) (0x20)
+#define S_INT_RQD (u_short) (0x40)
+#define S_UPD_STATS (u_short) (0x80)
+#define S_MASK (u_short) 0xFF /* mask of S_* */
+#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\
+ S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY)
+#define S_COMMAND_IN_PROGRESS (u_short) (0x1000)
+
+/* Address Config. Register.
+ * Window 0/Port 06
+ */
+
+#define ACF_CONNECTOR_BITS 14
+#define ACF_CONNECTOR_UTP 0
+#define ACF_CONNECTOR_AUI 1
+#define ACF_CONNECTOR_BNC 3
+
+/* Resource configuration register.
+ * Window 0/Port 08
+ *
+ */
+
+#define SET_IRQ(base,irq) outw((base) + EP_W0_RESOURCE_CFG, \
+ ((inw((base) + EP_W0_RESOURCE_CFG) & 0x0fff) | \
+ ((u_short)(irq)<<12)) ) /* set IRQ i */
+
+/*
+ * FIFO Registers.
+ * RX Status. Window 1/Port 08
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_RX_INCOMPLETE (u_short) (0x1<<15)
+#define ERR_RX (u_short) (0x1<<14)
+#define ERR_RX_OVERRUN (u_short) (0x8<<11)
+#define ERR_RX_RUN_PKT (u_short) (0xb<<11)
+#define ERR_RX_ALIGN (u_short) (0xc<<11)
+#define ERR_RX_CRC (u_short) (0xd<<11)
+#define ERR_RX_OVERSIZE (u_short) (0x9<<11)
+#define ERR_RX_DRIBBLE (u_short) (0x2<<11)
+
+/*
+ * FIFO Registers.
+ * TX Status. Window 1/Port 0B
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_SUCCES_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Configuration control register.
+ * Window 0/Port 04
+ */
+/* Read */
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+/* Write */
+#define ENABLE_DRQ_IRQ 0x0001
+#define W0_P4_CMD_RESET_ADAPTER 0x4
+#define W0_P4_CMD_ENABLE_ADAPTER 0x1
+/*
+ * Media type and status.
+ * Window 4/Port 0A
+ */
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+
+/*
+ * Misc defines for various things.
+ */
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */
+#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */
+#define PROD_ID 0x9150
+
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+
+#define RX_BYTES_MASK (u_short) (0x07ff)
diff --git a/bsps/i386/pc386/net/elink.c b/bsps/i386/pc386/net/elink.c
new file mode 100644
index 0000000000..ca637fb0a8
--- /dev/null
+++ b/bsps/i386/pc386/net/elink.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1994 Charles Hannum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Common code for dealing with 3COM ethernet cards.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <sys/types.h>
+
+#include "elink.h"
+
+#include <bsp.h>
+
+static void outb( unsigned short io_addr, unsigned char out_data )
+{
+ outport_byte( io_addr, out_data );
+}
+
+/*
+ * Issue a `global reset' to all cards. We have to be careful to do this only
+ * once during autoconfig, to prevent resetting boards that have already been
+ * configured.
+ */
+void
+elink_reset()
+{
+ static int x = 0;
+
+ if (x == 0) {
+ x = 1;
+ outb(ELINK_ID_PORT, ELINK_RESET);
+ }
+}
+
+/*
+ * The `ID sequence' is really just snapshots of an 8-bit CRC register as 0
+ * bits are shifted in. Different board types use different polynomials.
+ */
+void
+elink_idseq(u_char p)
+{
+ register int i;
+ register u_char c;
+
+ c = 0xff;
+ for (i = 255; i; i--) {
+ outb(ELINK_ID_PORT, c);
+ if (c & 0x80) {
+ c <<= 1;
+ c ^= p;
+ } else
+ c <<= 1;
+ }
+}
diff --git a/bsps/i386/pc386/net/elink.h b/bsps/i386/pc386/net/elink.h
new file mode 100644
index 0000000000..bcb13a494a
--- /dev/null
+++ b/bsps/i386/pc386/net/elink.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ *
+ * @ingroup pc386_3c509
+ *
+ * @brief EtherLink definitions.
+ */
+
+/*
+ * Copyright (c) 1994 Charles Hannum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Charles Hannum.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef PC98
+#define ELINK_ID_PORT 0x71d0
+#else
+#define ELINK_ID_PORT 0x110
+#endif
+#define ELINK_RESET 0xc0
+
+#define ELINK_507_POLY 0xe7
+#define ELINK_509_POLY 0xcf
+
+extern void elink_reset ( void );
+extern void elink_idseq ( u_char p );
diff --git a/bsps/i386/pc386/net/ne2000.c b/bsps/i386/pc386/net/ne2000.c
new file mode 100644
index 0000000000..17861405a6
--- /dev/null
+++ b/bsps/i386/pc386/net/ne2000.c
@@ -0,0 +1,1310 @@
+/* ne2k.c -- RTEMS NE2000 Ethernet driver.
+ * Written by Ian Lance Taylor, Zembu Labs.
+ * October, 1998.
+ *
+ * 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.
+ *
+ * Both the ne2000 and the wd80x3 are based on the National Semiconductor
+ * 8390 chip, so there is a fair amount of overlap between the two
+ * drivers. It would be possible in principle to combine some code into
+ * a separate set of subroutines called by both. In fact, the drivers in
+ * both OpenBSD and Linux work this way. I didn't bother, because for
+ * the relatively simple drivers used by RTEMS, the overlap is not
+ * especially large, and any reasonable use of subroutines would lead to
+ * slightly less efficient code.
+
+ * This ne2000 driver uses two transmit buffers. While one packet is
+ * being transmitted over the Ethernet, RTEMS will upload another. Since
+ * uploading a packet to the ne2000 is rather slow, I don't think there
+ * is any point to having more than two transmit buffers. However, the
+ * code does make it possible, by changing NE_TX_BUFS, although that
+ * would of course reduce the number of receive buffers.
+ *
+ * I suspect that the wd80x3 driver would benefit slightly from copying
+ * the multiple transmit buffer code. However, I have no way to test
+ * that.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <bsp.h>
+#include <libchip/wd80x3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <rtems/error.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 <bsp/irq.h>
+
+/* Define this to force byte-wide data transfers with the NIC. This
+ is needed for boards like the TS-1325 386EX PC, which support only
+ an 8-bit PC/104 bus. Undefine this on a normal PC.*/
+
+/* #define NE2000_BYTE_TRANSFERS */
+
+/* Define this to print debugging messages with printk. */
+
+/* #define DEBUG_NE2000 */
+/* #define DEBUG_NE */
+
+/* We expect to be able to read a complete packet into an mbuf. */
+
+#if (MCLBYTES < 1520)
+# error "Driver must have MCLBYTES >= 1520"
+#endif
+
+/* The 8390 macro definitions in wd80x3.h expect RO to be defined. */
+#define RO 0
+
+/* Minimum size of Ethernet packet. */
+#define ET_MINLEN 60
+
+/* The number of NE2000 devices supported by this driver. */
+
+#define NNEDRIVER 1
+
+/* RTEMS event number used by the interrupt handler to signal the
+ driver task. This must not be any of the events used by the
+ network task synchronization. */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/* RTEMS event number used to start the transmit daemon. This must
+ not be the same as INTERRUPT_EVENT. */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+/* Interrupts we want to handle from the device. */
+
+#define NE_INTERRUPTS \
+ (MSK_PRX | MSK_PTX | MSK_RXE | MSK_TXE | MSK_OVW | MSK_CNT)
+
+/* The size of a page in device memory. */
+
+#define NE_PAGE_SIZE (256)
+
+/* The first page address in device memory. */
+
+#define NE_START_PAGE (0x40)
+
+/* The last page address, plus 1. */
+
+#define NE_STOP_PAGE (0x80)
+
+/* The number of pages used for a single transmit buffer. This is
+ 1536 bytes, enough for a full size packet. */
+
+#define NE_TX_PAGES (6)
+
+/* The number of transmit buffers. We use two, so we can load one
+ packet while the other is being sent. */
+
+#define NE_TX_BUFS (2)
+
+/* We use the first pages in memory as transmit buffers, and the
+ remaining ones as receive buffers. */
+
+#define NE_FIRST_TX_PAGE (NE_START_PAGE)
+
+#define NE_FIRST_RX_PAGE (NE_FIRST_TX_PAGE + NE_TX_PAGES * NE_TX_BUFS)
+
+/* Data we store for each NE2000 device. */
+
+struct ne_softc {
+ /* The bsdnet information structure. */
+ struct arpcom arpcom;
+
+ /* The interrupt request number. */
+ unsigned int irno;
+ /* The base IO port number. */
+ unsigned int port;
+
+ /* Whether we accept broadcasts. */
+ int accept_broadcasts;
+
+ /* The thread ID of the transmit task. */
+ rtems_id tx_daemon_tid;
+ /* The thread ID of the receive task. */
+ rtems_id rx_daemon_tid;
+
+ /* Whether we use byte-transfers with the device. */
+ bool byte_transfers;
+
+ /* The number of memory buffers which the transmit daemon has loaded
+ with data to be sent, but which have not yet been completely
+ sent. */
+ int inuse;
+ /* The index of the next available transmit memory buffer. */
+ int nextavail;
+ /* The index of the next transmit buffer to send. */
+ int nextsend;
+ /* Nonzero if the device is currently transmitting a packet. */
+ int transmitting;
+ /* The length of the data stored in each transmit buffer. */
+ int sendlen[NE_TX_BUFS];
+
+ /* Set if we have a packet overrun while receiving. */
+ int overrun;
+ /* Set if we should resend after an overrun. */
+ int resend;
+
+ /* Statistics. */
+ struct {
+ /* Number of packets received. */
+ unsigned long rx_packets;
+ /* Number of packets sent. */
+ unsigned long tx_packets;
+ /* Number of interrupts. */
+ unsigned long interrupts;
+ /* Number of receive acknowledgements. */
+ unsigned long rx_acks;
+ /* Number of transmit acknowledgements. */
+ unsigned long tx_acks;
+ /* Number of packet overruns. */
+ unsigned long overruns;
+ /* Number of frame errors. */
+ unsigned long rx_frame_errors;
+ /* Number of CRC errors. */
+ unsigned long rx_crc_errors;
+ /* Number of missed packets. */
+ unsigned long rx_missed_errors;
+ } stats;
+};
+
+/* The list of NE2000 devices on this system. */
+
+static struct ne_softc ne_softc[NNEDRIVER];
+
+/*
+ * receive ring descriptor
+ *
+ * The National Semiconductor DS8390 Network interface controller uses
+ * the following receive ring headers. The way this works is that the
+ * memory on the interface card is chopped up into 256 bytes blocks.
+ * A contiguous portion of those blocks are marked for receive packets
+ * by setting start and end block #'s in the NIC. For each packet that
+ * is put into the receive ring, one of these headers (4 bytes each) is
+ * tacked onto the front. The first byte is a copy of the receiver status
+ * register at the time the packet was received.
+ */
+struct ne_ring
+{
+ unsigned char rsr; /* receiver status */
+ unsigned char next; /* pointer to next packet */
+ unsigned char cnt_lo; /* bytes in packet (length + 4) */
+ unsigned char cnt_hi; /* 16-bit, little-endian value */
+};
+
+/* Forward declarations to avoid warnings */
+
+static void ne_init_irq_handler (int irno);
+static void ne_stop (struct ne_softc *sc);
+static void ne_stop_hardware (struct ne_softc *sc);
+static void ne_init (void *arg);
+static void ne_init_hardware (struct ne_softc *sc);
+
+static void ne_reset(struct ne_softc *sc);
+#ifdef DEBUG_NE
+static void ne_dump(struct ne_softc *sc);
+#endif
+
+/* Find the NE2000 device which is attached at a particular interrupt
+ vector. */
+
+static struct ne_softc *
+ne_device_for_irno (int irno)
+{
+ int i;
+
+ for (i = 0; i < NNEDRIVER; ++i)
+ {
+ if (ne_softc[i].irno == irno
+ && ne_softc[i].arpcom.ac_if.if_softc != NULL)
+ return &ne_softc[i];
+ }
+
+ return NULL;
+}
+
+/* Read data from an NE2000 device. Read LEN bytes at ADDR, storing
+ them into P. */
+
+static void
+ne_read_data (struct ne_softc *sc, int addr, int len, unsigned char *p)
+{
+ unsigned int port = sc->port;
+ unsigned int dport = port + DATAPORT;
+
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA);
+ outport_byte (port + RBCR0, len);
+ outport_byte (port + RBCR1, len >> 8);
+ outport_byte (port + RSAR0, addr);
+ outport_byte (port + RSAR1, addr >> 8);
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RRE | MSK_STA);
+
+ if (sc->byte_transfers)
+ {
+ unsigned char d;
+ while (len > 0)
+ {
+ inport_byte(dport, d);
+ *p++ = d;
+ len--;
+ }
+ }
+ else /* word transfers */
+ {
+ unsigned short d;
+ while (len > 1)
+ {
+ inport_word(dport, d);
+ *p++ = d;
+ *p++ = d >> 8;
+ len -= 2;
+ }
+ if (len)
+ {
+ inport_word(dport, d);
+ *p++ = d;
+ }
+ }
+
+ outport_byte (port + ISR, MSK_RDC);
+}
+
+/* Handle the current NE2000 status. This is called when the device
+ signals an interrupt. It is also called at other times while
+ NE2000 interrupts have been disabled. */
+
+static void
+ne_check_status (struct ne_softc *sc, int from_irq_handler)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ unsigned int port = sc->port;
+ unsigned char status;
+
+ /* It seems that we need to use a loop here, because if the NE2000
+ signals an interrupt because packet transmission is complete, and
+ then receives a packet while interrupts are disabled, it seems to
+ sometimes fail to signal the interrupt for the received packet
+ when interrupts are reenabled. (Based on the behaviour of the
+ Realtek 8019AS chip). */
+
+ /* int count = 0; */
+ while (1)
+ {
+ inport_byte (port + ISR, status);
+ if (status == 0)
+ break;
+
+ /* ack */
+ outport_byte (port + ISR, status);
+
+#ifdef DEBUG_NE2000
+ printk ("NE2000 status 0x%x (8259 enabled: %s; mask: %x)\n", status,
+ i8259s_cache & (1 << sc->irno) ? "no" : "yes",
+ i8259s_cache);
+#endif
+
+ /* Check for incoming packet overwrite. */
+ if (status & MSK_OVW)
+ {
+ ifp->if_timer = 0;
+#ifdef DEBUG_NE
+ printk("^");
+#endif
+ ++sc->stats.overruns;
+ ne_reset(sc);
+ /* Reenable device interrupts. */
+ if (from_irq_handler)
+ outport_byte(port + IMR, NE_INTERRUPTS);
+ return;
+ }
+
+ /* Check for transmitted packet. The transmit daemon may now be
+ able to send another packet to the device. */
+ if ((status & (MSK_PTX | MSK_TXE)) != 0)
+ {
+ ifp->if_timer = 0;
+ ++sc->stats.tx_acks;
+ --sc->inuse;
+ sc->transmitting = 0;
+ if (sc->inuse > 0 || (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) != 0)
+ rtems_bsdnet_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT);
+ }
+
+ /* Check for received packet. */
+ if ((status & (MSK_PRX | MSK_RXE)) != 0)
+ {
+ ++sc->stats.rx_acks;
+ rtems_bsdnet_event_send (sc->rx_daemon_tid, INTERRUPT_EVENT);
+ }
+
+ /* Check for counter change. */
+ if ((status & MSK_CNT) != 0)
+ {
+ unsigned char add;
+ inport_byte (port + CNTR0, add);
+ sc->stats.rx_frame_errors += add;
+ inport_byte (port + CNTR1, add);
+ sc->stats.rx_crc_errors += add;
+ inport_byte (port + CNTR2, add);
+ sc->stats.rx_missed_errors += add;
+ }
+
+ break;
+ /* if (++count >= 1000)
+ {
+ printk("status: %x\n", status);
+ ne_reset(sc);
+ if (from_irq_handler)
+ outport_byte(port + IMR, NE_INTERRUPTS);
+ return;
+ } */
+ }
+
+ outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2);
+}
+
+/* Handle an NE2000 interrupt. */
+
+static void
+ne_interrupt_handler (rtems_irq_hdl_param cdata)
+{
+ rtems_vector_number v = (rtems_vector_number) cdata;
+ struct ne_softc *sc;
+
+ sc = ne_device_for_irno (v);
+ if (sc == NULL)
+ return;
+
+ ++sc->stats.interrupts;
+
+#ifdef DEBUG_NE
+ printk("!");
+#endif
+ ne_check_status(sc, 1);
+}
+
+/* Turn NE2000 interrupts on. */
+
+static void
+ne_interrupt_on (const rtems_irq_connect_data *irq)
+{
+ struct ne_softc *sc;
+
+#ifdef DEBUG_NE
+ printk ("ne_interrupt_on()\n");
+#endif
+ sc = ne_device_for_irno (irq->name);
+ if (sc != NULL)
+ outport_byte (sc->port + IMR, NE_INTERRUPTS);
+}
+
+/* Turn NE2000 interrupts off. See ne_interrupt_on. */
+
+static void
+ne_interrupt_off (const rtems_irq_connect_data *irq)
+{
+ struct ne_softc *sc;
+
+#ifdef DEBUG_NE
+ printk ("ne_interrupt_off()\n");
+#endif
+ sc = ne_device_for_irno (irq->name);
+ if (sc != NULL)
+ outport_byte (sc->port + IMR, 0);
+}
+
+/* Initialize the NE2000 hardware. */
+
+static void
+ne_init_hardware (struct ne_softc *sc)
+{
+ unsigned int port = sc->port;
+ int i;
+
+#ifdef DEBUG_NE2000
+ printk ("ne_init_hardware()\n");
+#endif
+
+ /* Initialize registers. */
+
+ /* Set interface for page 0, Remote DMA complete, Stopped */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
+
+ /* Set FIFO threshold to 8, No auto-init Remote DMA, byte order=80x86 */
+ /* byte-wide DMA xfers */
+ if (sc->byte_transfers)
+ outport_byte (port + DCR, MSK_FT10 | MSK_BMS);
+ /* word-wide DMA xfers */
+ else
+ outport_byte (port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS);
+
+ /* Clear Remote Byte Count Registers */
+ outport_byte (port + RBCR0, 0);
+ outport_byte (port + RBCR1, 0);
+
+ /* For the moment, don't store incoming packets in memory. */
+ outport_byte (port + RCR, MSK_MON);
+
+ /* Place NIC in internal loopback mode */
+ outport_byte (port + TCR, MSK_LOOP);
+
+ /* Initialize transmit/receive (ring-buffer) Page Start */
+ outport_byte (port + TPSR, NE_FIRST_TX_PAGE);
+ outport_byte (port + PSTART, NE_FIRST_RX_PAGE);
+
+ /* Initialize Receiver (ring-buffer) Page Stop and Boundary */
+ outport_byte (port + PSTOP, NE_STOP_PAGE);
+ outport_byte (port + BNRY, NE_STOP_PAGE - 1);
+
+ /* Clear all interrupts */
+ outport_byte (port + ISR, 0xff);
+ /* Disable all interrupts */
+ outport_byte (port + IMR, 0);
+
+ /* Program Command Register for page 1 */
+ outport_byte (port + CMDR, MSK_PG1 | MSK_RD2 | MSK_STP);
+
+ /* Set the Ethernet hardware address. */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outport_byte (port + PAR + i, sc->arpcom.ac_enaddr[i]);
+
+ /* Set Current Page pointer to next_packet */
+ outport_byte (port + CURR, NE_FIRST_RX_PAGE);
+
+ /* Clear the multicast address. */
+ for (i = 0; i < MARsize; ++i)
+ outport_byte (port + MAR + i, 0);
+
+ /* Set page 0 registers */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
+
+ /* accept broadcast + multicast */
+ outport_byte (port + RCR, (sc->accept_broadcasts ? MSK_AB : 0) | MSK_AM);
+
+ /* Start interface */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA);
+
+ /* Take interface out of loopback */
+ outport_byte (port + TCR, 0);
+}
+
+/* Set up interrupts.
+*/
+static void
+ne_init_irq_handler(int irno)
+{
+ rtems_irq_connect_data irq;
+
+#ifdef DEBUG_NE
+ printk("ne_init_irq_handler(%d)\n", irno);
+#endif
+ irq.name = irno;
+ irq.hdl = ne_interrupt_handler;
+ irq.handle = (rtems_irq_hdl) irno;
+ irq.on = ne_interrupt_on;
+ irq.off = ne_interrupt_off;
+ irq.isOn = NULL;
+
+ if (!BSP_install_rtems_irq_handler (&irq))
+ rtems_panic ("Can't attach NE interrupt handler for irq %d\n", irno);
+}
+
+/* The NE2000 packet receive daemon. This task is started when the
+ NE2000 driver is initialized. */
+
+#ifdef DEBUG_NE
+static int ccc = 0; /* experinent! */
+#endif
+
+static void
+ne_rx_daemon (void *arg)
+{
+ struct ne_softc *sc = (struct ne_softc *) arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ unsigned int port = sc->port;
+
+ while (1)
+ {
+ rtems_event_set events;
+
+ /* Wait for the interrupt handler to tell us that there is a
+ packet ready to receive. */
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ /* Don't let the device interrupt us now. */
+ outport_byte (port + IMR, 0);
+
+ while (1)
+ {
+ unsigned char startpage, currpage;
+ unsigned short len;
+ unsigned char next, cnt1, cnt2;
+ struct mbuf *m;
+ unsigned char *p;
+ int startaddr;
+ int toend;
+ struct ether_header *eh;
+ struct ne_ring hdr; /* ring buffer header */
+ int reset;
+
+ inport_byte (port + BNRY, startpage);
+
+ outport_byte (port + CMDR, MSK_PG1 | MSK_RD2);
+ inport_byte (port + CURR, currpage);
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2);
+
+ ++startpage;
+ if (startpage >= NE_STOP_PAGE)
+ startpage = NE_FIRST_RX_PAGE;
+
+ if (startpage == currpage)
+ break;
+
+#ifdef DEBUG_NE2000
+ printk ("ne_rx_daemon: start page %x; current page %x\n",
+ startpage, currpage);
+#endif
+
+ reset = 0;
+
+ /* Read the buffer header */
+ startaddr = startpage * NE_PAGE_SIZE;
+ ne_read_data(sc, startaddr, sizeof(hdr), (unsigned char *)&hdr);
+ next = hdr.next;
+ if (next >= NE_STOP_PAGE)
+ next = NE_FIRST_RX_PAGE;
+
+ /* check packet length */
+ len = ( hdr.cnt_hi << 8 ) | hdr.cnt_lo;
+ if (currpage < startpage)
+ cnt1 = currpage + (NE_STOP_PAGE - NE_FIRST_RX_PAGE) - startpage;
+ else
+ cnt1 = currpage - startpage;
+ cnt2 = len / NE_PAGE_SIZE;
+ if (len % NE_PAGE_SIZE)
+ cnt2++;
+ if (cnt1 < cnt2)
+ {
+#ifdef DEBUG_NE
+ printk("(%x<%x:%x)", cnt1, cnt2, len);
+/*
+ printk("start page 0x%x; current page 0x%x\n",
+ startpage, currpage);
+ printk("cnt1 < cnt2 (0x%x, 0x%x); len 0x%x\n",
+ cnt1, cnt2, len);
+*/
+#endif
+ reset = 1;
+ }
+ if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN + sizeof(struct ne_ring)) ||
+ len < (ETHER_MIN_LEN - ETHER_CRC_LEN + sizeof(struct ne_ring)) ||
+ len > MCLBYTES)
+ {
+#ifdef DEBUG_NE
+ printk("(%x)", len);
+/*
+ printk("start page 0x%x; current page 0x%x\n",
+ startpage, currpage);
+ printk("len out of range: 0x%x\n", len);
+ printk("stat: 0x%x, next: 0x%x\n", hdr.rsr, hdr.next);
+*/
+#endif
+ reset = 1;
+ }
+#ifdef DEBUG_NE
+ if (++ccc == 100)
+ { ccc = 0; reset = 1;
+ printk("T");
+ }
+#endif
+
+ /* reset interface */
+ if (reset)
+ {
+ ne_reset(sc);
+ goto Next;
+ }
+
+ /* The first four bytes of the length are the buffer header. */
+ len -= sizeof(struct ne_ring);
+ startaddr += sizeof(struct ne_ring);
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+
+ p = mtod (m, unsigned char *);
+ m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
+
+ toend = NE_STOP_PAGE * NE_PAGE_SIZE - startaddr;
+ if (toend < len)
+ {
+ ne_read_data (sc, startaddr, toend, p);
+ p += toend;
+ len -= toend;
+ startaddr = NE_FIRST_RX_PAGE * NE_PAGE_SIZE;
+ }
+
+ if (len > 0)
+ ne_read_data (sc, startaddr, len, p);
+
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof (struct ether_header);
+
+#ifdef DEBUG_NE
+ /* printk("[r%d]", ((hdr.cnt_hi<<8) + hdr.cnt_lo - sizeof(hdr))); */
+ printk("<");
+#endif
+ ether_input (ifp, eh, m);
+ ++sc->stats.rx_packets;
+
+ outport_byte (port + BNRY, next - 1);
+ }
+
+ if (sc->overrun) {
+ outport_byte (port + ISR, MSK_OVW);
+ outport_byte (port + TCR, 0);
+ if (sc->resend)
+ outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA);
+ sc->resend = 0;
+ sc->overrun = 0;
+ }
+
+ Next:
+ /* Reenable device interrupts. */
+ outport_byte (port + IMR, NE_INTERRUPTS);
+ }
+}
+
+/* Load an NE2000 packet onto the device. */
+
+static void
+ne_loadpacket (struct ne_softc *sc, struct mbuf *m)
+{
+ unsigned int port = sc->port;
+ unsigned int dport = port + DATAPORT;
+ struct mbuf *mhold = m;
+ int leftover;
+ unsigned char leftover_data;
+ int timeout;
+ int send_cnt = 0;
+
+#ifdef DEBUG_NE2000
+ printk ("Uploading NE2000 packet\n");
+#endif
+
+ /* Reset remote DMA complete flag. */
+ outport_byte (port + ISR, MSK_RDC);
+
+ /* Write out the count. */
+ outport_byte (port + RBCR0, m->m_pkthdr.len);
+ outport_byte (port + RBCR1, m->m_pkthdr.len >> 8);
+
+ sc->sendlen[sc->nextavail] = m->m_pkthdr.len;
+
+ /* Tell the device which address we want to write to. */
+ outport_byte (port + RSAR0, 0);
+ outport_byte (port + RSAR1,
+ NE_FIRST_TX_PAGE + (sc->nextavail * NE_TX_PAGES));
+
+ /* Set up the write. */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RWR | MSK_STA);
+
+ /* Transfer the mbuf chain to device memory. NE2000 devices require
+ that the data be transferred as words, so we need to handle odd
+ length mbufs. Never occurs if we force byte transfers. */
+
+ leftover = 0;
+ leftover_data = '\0';
+
+ for (; m != NULL; m = m->m_next) {
+ int len;
+ unsigned char *data;
+
+ len = m->m_len;
+ if (len == 0)
+ continue;
+
+ data = mtod (m, unsigned char *);
+
+ if (leftover) {
+ unsigned char next;
+
+ /* Data left over from previous mbuf in chain. */
+ next = *data++;
+ --len;
+ outport_word (dport, leftover_data | (next << 8));
+ send_cnt += 2;
+ leftover = 0;
+ }
+
+ /* If using byte transfers, len always ends up as zero so
+ there are no leftovers. */
+
+ if (sc->byte_transfers)
+ while (len > 0) {
+ outport_byte (dport, *data++);
+ len--;
+ }
+ else
+ while (len > 1) {
+ outport_word (dport, data[0] | (data[1] << 8));
+ data += 2;
+ len -= 2;
+ send_cnt += 2;
+ }
+
+ if (len > 0)
+ {
+ leftover = 1;
+ leftover_data = *data++;
+ }
+ }
+
+ if (leftover)
+ {
+ outport_word (dport, leftover_data);
+ send_cnt += 2;
+ }
+
+#ifdef DEBUG_NE
+ /* printk("{l%d|%d}", send_cnt, sc->nextavail); */
+ printk("v");
+#endif
+ m_freem (mhold);
+
+ /* Wait for the device to complete accepting the data, with a
+ limiting counter so that we don't wait too long. */
+ for (timeout = 0; timeout < 100; ++timeout)
+ {
+ unsigned char status;
+
+ inport_byte (port + ISR, status);
+
+#ifdef DEBUG_NE2000
+ if ((status &~ MSK_RDC) != 0)
+ printk ("Status 0x%x while waiting for acknowledgement of uploaded packet\n",
+ status);
+#endif
+
+ if ((status & MSK_RDC) != 0) {
+ outport_byte (port + ISR, MSK_RDC);
+ break;
+ }
+ }
+
+ if (timeout >= 100)
+ printk ("Timed out waiting for acknowledgement of uploaded NE2000 packet\n");
+
+ ++sc->nextavail;
+ if (sc->nextavail == NE_TX_BUFS)
+ sc->nextavail = 0;
+}
+
+/* Tell the NE2000 to transmit a buffer whose contents we have already
+ loaded onto the device. */
+
+static void
+ne_transmit (struct ne_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ unsigned int port = sc->port;
+ int len;
+
+#ifdef DEBUG_NE2000
+ printk ("Transmitting NE2000 packet\n");
+#endif
+
+ len = sc->sendlen[sc->nextsend];
+ if (len < ET_MINLEN)
+ len = ET_MINLEN;
+ outport_byte (port + TBCR0, len);
+ outport_byte (port + TBCR1, len >> 8);
+
+ outport_byte (port + TPSR, NE_FIRST_TX_PAGE + (sc->nextsend * NE_TX_PAGES));
+
+ outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA);
+
+#ifdef DEBUG_NE
+ /* printk("{s%d|%d}", len, sc->nextsend); */
+ printk(">");
+#endif
+ ++sc->nextsend;
+ if (sc->nextsend == NE_TX_BUFS)
+ sc->nextsend = 0;
+
+ ++sc->stats.tx_packets;
+
+ /* set watchdog timer */
+ ifp->if_timer = 2;
+}
+
+/* The NE2000 packet transmit daemon. This task is started when the
+ NE2000 driver is initialized. */
+
+static void
+ne_tx_daemon (void *arg)
+{
+ struct ne_softc *sc = (struct ne_softc *) arg;
+ unsigned int port = sc->port;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ while (1) {
+ rtems_event_set events;
+
+ /* Wait for a packet to be ready for sending, or for there to be
+ room for another packet in the device memory. */
+ rtems_bsdnet_event_receive (START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+#ifdef DEBUG_NE2000
+ printk ("ne_tx_daemon\n");
+#endif
+
+ /* This daemon handles both uploading data onto the device and
+ telling the device to transmit data which has been uploaded.
+ These are separate tasks, because while the device is
+ transmitting one buffer we will upload another. */
+
+ /* Don't let the device interrupt us now. */
+ outport_byte (port + IMR, 0);
+
+ while (1) {
+ struct mbuf *m;
+
+ /* If the device is not transmitting a packet, and we have
+ uploaded a packet, tell the device to transmit it. */
+ if (! sc->transmitting && sc->inuse > 0) {
+ sc->transmitting = 1;
+ ne_transmit (sc);
+ }
+
+ /* If we don't have any more buffers to send, quit now. */
+ if (ifp->if_snd.ifq_head == NULL) {
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+
+ /* Allocate a buffer to load data into. If there are none
+ available, quit until a buffer has been transmitted. */
+ if (sc->inuse >= NE_TX_BUFS)
+ break;
+
+ ++sc->inuse;
+
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (m == NULL)
+ panic ("ne_tx_daemon");
+
+ ne_loadpacket (sc, m);
+
+ /* Check the device status. It may have finished transmitting
+ the last packet. */
+ ne_check_status(sc, 0);
+ }
+
+ /* Reenable device interrupts. */
+ outport_byte (port + IMR, NE_INTERRUPTS);
+ }
+}
+
+/* Start sending an NE2000 packet. */
+
+static void
+ne_start (struct ifnet *ifp)
+{
+ struct ne_softc *sc = ifp->if_softc;
+
+#ifdef DEBUG_NE
+ printk("S");
+#endif
+ /* Tell the transmit daemon to wake up and send a packet. */
+ rtems_bsdnet_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/* Initialize and start and NE2000. */
+
+static void
+ne_init (void *arg)
+{
+ struct ne_softc *sc = (struct ne_softc *) arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+#ifdef DEBUG_NE
+ printk("ne_init()\n");
+ ne_dump(sc);
+#endif
+
+ /* only once... */
+ if (sc->tx_daemon_tid == 0)
+ {
+ sc->inuse = 0;
+ sc->nextavail = 0;
+ sc->nextsend = 0;
+ sc->transmitting = 0;
+
+ ne_init_hardware (sc);
+
+ sc->tx_daemon_tid = rtems_bsdnet_newproc ("SCtx", 4096, ne_tx_daemon, sc);
+ sc->rx_daemon_tid = rtems_bsdnet_newproc ("SCrx", 4096, ne_rx_daemon, sc);
+
+ /* install rtems irq handler */
+ ne_init_irq_handler(sc->irno);
+ }
+
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/* Stop an NE2000. */
+
+static void
+ne_stop (struct ne_softc *sc)
+{
+ sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING;
+
+ ne_stop_hardware(sc);
+
+ sc->inuse = 0;
+ sc->nextavail = 0;
+ sc->nextsend = 0;
+ sc->transmitting = 0;
+ sc->overrun = 0;
+ sc->resend = 0;
+}
+
+static void
+ne_stop_hardware (struct ne_softc *sc)
+{
+ unsigned int port = sc->port;
+ int i;
+
+ /* Stop everything. */
+ outport_byte (port + CMDR, MSK_STP | MSK_RD2);
+
+ /* Wait for the interface to stop, using I as a time limit. */
+ for (i = 0; i < 5000; ++i)
+ {
+ unsigned char status;
+
+ inport_byte (port + ISR, status);
+ if ((status & MSK_RST) != 0)
+ break;
+ }
+}
+
+/* reinitializing interface
+*/
+static void
+ne_reset(struct ne_softc *sc)
+{
+ ne_stop(sc);
+ ne_init_hardware(sc);
+ sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+#ifdef DEBUG_NE
+ printk("*");
+#endif
+}
+
+#ifdef DEBUG_NE
+/* show anything about ne
+*/
+static void
+ne_dump(struct ne_softc *sc)
+{
+ int i;
+ printk("\nne configuration:\n");
+ printk("ethernet addr:");
+ for (i=0; i<ETHER_ADDR_LEN; i++)
+ printk(" %x", sc->arpcom.ac_enaddr[i]);
+ printk("\n");
+ printk("irq = %d\n", sc->irno);
+ printk("port = 0x%x\n", sc->port);
+ printk("accept_broadcasts = %d\n", sc->accept_broadcasts);
+ printk("byte_transfers = %d\n", sc->byte_transfers);
+}
+#endif
+
+/* Show NE2000 interface statistics. */
+
+static void
+ne_stats (struct ne_softc *sc)
+{
+ printf (" Received packets: %-8lu", sc->stats.rx_packets);
+ printf (" Transmitted packets: %-8lu\n", sc->stats.tx_packets);
+ printf (" Receive acks: %-8lu", sc->stats.rx_acks);
+ printf (" Transmit acks: %-8lu\n", sc->stats.tx_acks);
+ printf (" Packet overruns: %-8lu", sc->stats.overruns);
+ printf (" Frame errors: %-8lu\n", sc->stats.rx_frame_errors);
+ printf (" CRC errors: %-8lu", sc->stats.rx_crc_errors);
+ printf (" Missed packets: %-8lu\n", sc->stats.rx_missed_errors);
+ printf (" Interrupts: %-8lu\n", sc->stats.interrupts);
+}
+
+static int ne_set_multicast_filter(struct ne_softc* sc)
+{
+ int i=0;
+ unsigned int port = sc->port;
+ unsigned char cmd = 0;
+
+ /* Save CMDR settings */
+ inport_byte(port + CMDR, cmd);
+ /* Change to page 1 */
+ outport_byte(port + CMDR, cmd | MSK_PG1);
+
+ /* Set MAR to accept _all_ multicast packets */
+ for (i = 0; i < MARsize; ++i) {
+ outport_byte (port + MAR + i, 0xFF);
+ }
+
+ /* Revert to original CMDR settings */
+ outport_byte(port + CMDR, cmd);
+
+ return 0;
+}
+
+/* NE2000 driver ioctl handler. */
+
+static int
+ne_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct ne_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ error = ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ ne_stop (sc);
+ break;
+
+ case IFF_UP:
+ ne_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ ne_stop (sc);
+ ne_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ {
+ struct ifreq* ifr = (struct ifreq*) data;
+ error = (command == SIOCADDMULTI ?
+ ether_addmulti(ifr, &(sc->arpcom)) :
+ ether_delmulti(ifr, &(sc->arpcom)) );
+ /* ENETRESET indicates that driver should update its multicast filters */
+ if(error == ENETRESET) {
+ error = ne_set_multicast_filter(sc);
+ }
+ break;
+ }
+
+ case SIO_RTEMS_SHOW_STATS:
+ ne_stats (sc);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+static void
+ne_watchdog(struct ifnet *ifp)
+{
+ struct ne_softc *sc = ifp->if_softc;
+
+ printk("ne2000: device timeout\n");
+ ifp->if_oerrors++;
+
+ ne_reset(sc);
+}
+
+static void
+print_byte(unsigned char b)
+{
+ printk("%x%x", b >> 4, b & 0x0f);
+}
+
+/* Attach an NE2000 driver to the system. */
+
+int
+rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
+{
+ int i;
+ struct ne_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+
+ /* dettach ... */
+ if (!attach)
+ return 0;
+
+ /* Find a free driver. */
+ sc = NULL;
+ for (i = 0; i < NNEDRIVER; ++i) {
+ sc = &ne_softc[i];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc == NULL)
+ break;
+ }
+
+ if (sc == NULL) {
+ printf ("Too many NE2000 drivers.\n");
+ return 0;
+ }
+
+ memset (sc, 0, sizeof *sc);
+
+ /* Check whether we do byte-wide or word-wide transfers. */
+
+#ifdef NE2000_BYTE_TRANSFERS
+ sc->byte_transfers = true;
+#else
+ sc->byte_transfers = false;
+#endif
+
+ /* Handle the options passed in by the caller. */
+
+ if (config->mtu != 0)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ if (config->irno != 0)
+ sc->irno = config->irno;
+ else {
+ const char* opt;
+ opt = bsp_cmdline_arg ("--ne2k-irq=");
+ if (opt) {
+ opt += sizeof ("--ne2k-irq=") - 1;
+ sc->irno = strtoul (opt, 0, 0);
+ }
+ if (sc->irno == 0) {
+ /* We use 5 as the default IRQ. */
+ sc->irno = 5;
+ }
+ }
+
+ if (config->port != 0)
+ sc->port = config->port;
+ else {
+ const char* opt;
+ opt = bsp_cmdline_arg ("--ne2k-port=");
+ if (opt) {
+ opt += sizeof ("--ne2k-port=") - 1;
+ sc->port = strtoul (opt, 0, 0);
+ }
+ if (config->port == 0) {
+ /* We use 0x300 as the default IO port number. */
+ sc->port = 0x300;
+ }
+ }
+
+ sc->accept_broadcasts = ! config->ignore_broadcast;
+
+ if (config->hardware_address != NULL)
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ else
+ {
+ unsigned char prom[16];
+ int ia;
+
+ /* Read the PROM to get the Ethernet hardware address. */
+
+ outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
+
+ if (sc->byte_transfers) {
+ outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS);
+ }
+ else {
+ outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS);
+ }
+
+ outport_byte (sc->port + RBCR0, 0);
+ outport_byte (sc->port + RBCR1, 0);
+ outport_byte (sc->port + RCR, MSK_MON);
+ outport_byte (sc->port + TCR, MSK_LOOP);
+ outport_byte (sc->port + IMR, 0);
+ outport_byte (sc->port + ISR, 0xff);
+
+ ne_read_data (sc, 0, sizeof prom, prom);
+
+ outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
+
+ for (ia = 0; ia < ETHER_ADDR_LEN; ++ia)
+ sc->arpcom.ac_enaddr[ia] = prom[ia * 2];
+ }
+
+ /* Set up the network interface. */
+
+ ifp->if_softc = sc;
+ ifp->if_unit = i + 1;
+ ifp->if_name = "ne";
+ ifp->if_mtu = mtu;
+ ifp->if_init = ne_init;
+ ifp->if_ioctl = ne_ioctl;
+ ifp->if_watchdog = ne_watchdog;
+ ifp->if_start = ne_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /* Attach the interface. */
+
+ if_attach (ifp);
+ ether_ifattach (ifp);
+
+ printk("network device '%s' <", config->name);
+ print_byte(sc->arpcom.ac_enaddr[0]);
+ for (i=1; i<ETHER_ADDR_LEN; i++)
+ { printk(":");
+ print_byte(sc->arpcom.ac_enaddr[i]);
+ }
+ printk("> initialized on port 0x%x, irq %d\n", sc->port, sc->irno);
+
+ return 1;
+}
diff --git a/bsps/i386/pc386/net/wd8003.c b/bsps/i386/pc386/net/wd8003.c
new file mode 100644
index 0000000000..d10361bf2c
--- /dev/null
+++ b/bsps/i386/pc386/net/wd8003.c
@@ -0,0 +1,647 @@
+/*
+ * RTEMS driver for WD800x
+ *
+ * Based on the 68360 Network Driver by:
+ * W. Eric Norum
+ * Saskatchewan Accelerator Laboratory
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric@skatter.usask.ca
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <bsp.h>
+#include <libchip/wd80x3.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h> /* memcpy, memset */
+#include <errno.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <assert.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 <bsp/irq.h>
+
+#define ET_MINLEN 60 /* minimum message length */
+
+/*
+ * Number of WDs supported by this driver
+ */
+#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 driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#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
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#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;
+ struct mbuf **rxMbuf;
+ struct mbuf **txMbuf;
+ int acceptBroadcast;
+ int rxBdCount;
+ int txBdCount;
+ int txBdHead;
+ int txBdTail;
+ int txBdActiveCount;
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+ rtems_vector_number name;
+
+ 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 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
+
+#define SHATOT (8*1024) /* size of shared memory */
+#define SHAPAGE 256 /* shared memory information */
+#define MAXSIZ 1536 /*(MAXBUF - MESSH_SZ)*/
+#define OUTPAGE ((SHATOT-(MAXSIZ+SHAPAGE-1))/SHAPAGE)
+
+static volatile unsigned long overrun;
+static volatile unsigned long resend;
+static struct wd_softc wd_softc[NWDDRIVER];
+
+/*
+ * WD interrupt handler
+ */
+static void
+wd8003Enet_interrupt_handler (void * unused)
+{
+ unsigned int tport;
+ unsigned char status, status2;
+
+ tport = wd_softc[0].port ;
+
+ /*
+ * Read status
+ */
+ inport_byte(tport+ISR, status);
+ outport_byte(tport+IMR, 0x00);
+
+ /*
+ * Ring overwrite
+ */
+
+ if (status & MSK_OVW){
+ outport_byte(tport+CMDR, MSK_STP + MSK_RD2); /* stop 8390 */
+ Wait_X_ms(2);
+ outport_byte(tport+RBCR0, 0); /* clear byte count */
+ outport_byte(tport+RBCR1, 0);
+ inport_byte(tport+ISR, status2);
+ 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?
+ */
+ if (status & (MSK_PRX+MSK_RXE)) {
+ outport_byte(tport+ISR, status & (MSK_PRX+MSK_RXE));
+ wd_softc[0].rxInterrupts++;
+ rtems_bsdnet_event_send (wd_softc[0].rxDaemonTid, INTERRUPT_EVENT);
+ }
+
+}
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+wd8003Enet_initialize_hardware (struct wd_softc *sc)
+{
+ int i1, ultra;
+ char cc1, cc2;
+ unsigned char temp;
+ rtems_status_code status;
+ unsigned int tport;
+ unsigned char *hwaddr;
+
+ 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)
+ 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)((sc->bpar>>13)&0x3f));
+
+ outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
+ cc1 = MSK_BMS + MSK_FT10; /* configure 8 or 16 bits */
+
+ inport_byte(tport+0x07, temp) ;
+
+ ultra = ((temp & 0xf0) == 0x20 || (temp & 0xf0) == 0x40);
+ if (ultra)
+ cc1 = MSK_WTS + MSK_BMS + MSK_FT10;
+ outport_byte(tport+DCR, cc1);
+ outport_byte(tport+RBCR0, 0);
+ outport_byte(tport+RBCR1, 0);
+ outport_byte(tport+RCR, MSK_MON); /* disable the rxer */
+ outport_byte(tport+TCR, 0); /* normal operation */
+ outport_byte(tport+PSTOP, OUTPAGE); /* init PSTOP */
+ 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); /* enable interrupt */
+
+ outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
+
+ for (i1=0; i1<6; i1++) /* initial physical addr */
+ outport_byte(tport+PAR+i1, hwaddr[i1]);
+
+ for (i1=0; i1<MARsize; i1++) /* clear multicast */
+ outport_byte(tport+MAR+i1, 0);
+ outport_byte(tport+CURR, 0); /* init current packet */
+
+ outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
+ outport_byte(tport+CMDR, MSK_STA + MSK_RD2); /* put 8390 on line */
+ outport_byte(tport+RCR, MSK_AB); /* MSK_AB accept broadcast */
+
+ if (ultra) {
+ inport_byte(tport+0x0c, temp);
+ outport_byte(tport+0x0c, temp | 0x80);
+ outport_byte(tport+0x05, 0x80);
+ outport_byte(tport+0x06, 0x01);
+ }
+
+ /*
+ * Set up interrupts
+ */
+
+ status = rtems_interrupt_handler_install(
+ sc->name,
+ "wd8003",
+ RTEMS_INTERRUPT_UNIQUE,
+ wd8003Enet_interrupt_handler,
+ NULL
+ );
+ assert(status == RTEMS_SUCCESSFUL);
+}
+
+static void
+wd_rxDaemon (void *arg)
+{
+ 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;
+ unsigned char *shp, *temp;
+ unsigned short *real_short_ptr;
+ rtems_event_set events;
+
+ 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;
+
+ /* real_short_ptr avoids cast on lvalue which gcc no longer allows */
+ shp = dp->base + 1 + (SHAPAGE * start);
+ next = *shp++;
+ real_short_ptr = (unsigned short *)shp;
+ len = *(real_short_ptr)++ - 4;
+
+ if (next >= OUTPAGE){
+ next = 0;
+ }
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+
+ temp = (unsigned char *) 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);
+ }
+
+ /*
+ * 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;
+ uint8_t *shp, txReady;
+
+ tport = dp->port;
+
+ /*
+ * Waiting for Transmitter ready
+ */
+ inport_byte(tport+CMDR, txReady);
+ while(txReady & MSK_TXP)
+ inport_byte(tport+CMDR, txReady);
+
+ len = 0;
+ shp = dp->base + (SHAPAGE * OUTPAGE);
+
+ n = m;
+
+ for (;;){
+ len += m->m_len;
+ memcpy(shp, (char *)m->m_data, m->m_len);
+ shp += m->m_len ;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ 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);
+}
+
+/*
+ * Driver transmit daemon
+ */
+static 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;
+ }
+}
+
+/*
+ * Send packet (caller provides header).
+ */
+static void
+wd_start (struct ifnet *ifp)
+{
+ struct wd_softc *sc = ifp->if_softc;
+
+ rtems_bsdnet_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=wd_softc[0].port ;
+ inport_byte(tport+0x04,temp);
+ outport_byte(tport+0x04, temp & 0x7f);
+ outport_byte(tport + CMDR, MSK_STP + MSK_RD2);
+
+}
+
+/*
+ * Show interface statistics
+ */
+static void
+wd_stats (struct wd_softc *sc)
+{
+ 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);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+wd_ioctl (struct ifnet *ifp, ioctl_command_t 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_wd_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
+{
+ 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->name = config->irno;
+ else
+ sc->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;
+
+ /*
+ * init some variables
+ */
+ overrun = 0;
+ resend = 0;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ return 1;
+};