summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>1999-05-14 16:23:42 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>1999-05-14 16:23:42 +0000
commit44dd8a611657c4bc94513d12bb4903c3809a3f2a (patch)
treeea6a52bdd6c4c86065b5a039f7893e980ef57828
parentRegenerated. (diff)
downloadrtems-44dd8a611657c4bc94513d12bb4903c3809a3f2a.tar.bz2
Added 3COM 3C509 driver from Rosimildo DaSilva <rdasilva@connecttel.com>.
-rw-r--r--c/src/lib/libbsp/i386/pc386/3c509/3c509.c1636
-rw-r--r--c/src/lib/libbsp/i386/pc386/3c509/3c509.h423
-rw-r--r--c/src/lib/libbsp/i386/pc386/3c509/Makefile.in65
-rw-r--r--c/src/lib/libbsp/i386/pc386/3c509/elink.c78
-rw-r--r--c/src/lib/libbsp/i386/pc386/3c509/elink.h43
-rw-r--r--c/src/lib/libbsp/i386/pc386/Makefile.in2
-rw-r--r--c/src/lib/libbsp/i386/pc386/wrapup/Makefile.in2
7 files changed, 2247 insertions, 2 deletions
diff --git a/c/src/lib/libbsp/i386/pc386/3c509/3c509.c b/c/src/lib/libbsp/i386/pc386/3c509/3c509.c
new file mode 100644
index 0000000000..e4c27d894f
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/3c509/3c509.c
@@ -0,0 +1,1636 @@
+/**********************************************************************************
+ * $Header$
+ *
+ * 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
+ *******************************************************************************
+ *
+ *
+ * MODIFICATION/HISTORY:
+ * $Log$
+ *
+ **********************************************************************************/
+
+#include <bsp.h>
+
+#include <stdio.h>
+#include <stdarg.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 <sys/libkern.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <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_irq_connect_data irqInfo;
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ 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, unsigned char *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, unsigned char *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, unsigned char *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, unsigned char *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, unsigned char *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, unsigned char *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.
+ *
+ **********************************************************************************/
+void __inline outb( unsigned short io_addr, unsigned char out_data )
+{
+ outport_byte( io_addr, out_data );
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION: Routine to read a word as defined in FreeBSD.
+ *
+ * RETURNS: byte read.
+ *
+ **********************************************************************************/
+static __inline unsigned char inb( unsigned short io_addr )
+{
+ unsigned short 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: Waits until the EEPROM of the card is ready to be accessed.
+ *
+ * RETURNS: 0 - not ready; 1 - ok
+ *
+ **********************************************************************************/
+static int eeprom_rdy( struct ep_softc *sc )
+{
+ int i;
+
+ for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
+ continue;
+ if (i >= MAX_EEPROMBUSY)
+ {
+ printf("ep%d: eeprom failed to come ready.\n", sc->unit);
+ return (0);
+ }
+ return (1);
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * get_e: gets a 16 bits word from the EEPROM.
+ * We must have set the window before call this routine.
+ *
+ * RETURNS: data from EEPROM
+ *
+ **********************************************************************************/
+u_short get_e( struct ep_softc *sc, int offset )
+{
+ if( !eeprom_rdy(sc) )
+ return (0xffff);
+ outw(BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset );
+ if( !eeprom_rdy(sc) )
+ return( 0xffff );
+ return( inw( BASE + EP_W0_EEPROM_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( rtems_vector_number v )
+{
+ struct ep_softc *sc = (struct ep_softc *)&ep_softc[ 0 ];
+
+ /* 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_event_send( sc->rxDaemonTid, INTERRUPT_EVENT );
+}
+
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * RETURNS:
+ *
+ **********************************************************************************/
+static void nopOn(const rtems_irq_connect_data* notUsed)
+{
+ /* does nothing */
+}
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * RETURNS:
+ *
+ **********************************************************************************/
+static int _3c509_IsOn(const rtems_irq_connect_data* irq)
+{
+ return BSP_irq_enabled_at_i8259s (irq->name);
+}
+
+
+/**********************************************************************************
+ *
+ * DESCRIPTION:
+ * Initializes the ethernet hardware.
+ *
+ * RETURNS: nothing.
+ *
+ **********************************************************************************/
+static void _3c509_initialize_hardware (struct ep_softc *sc)
+{
+ rtems_status_code st;
+
+ epinit( sc );
+
+ /*
+ * Set up interrupts
+ */
+ sc->irqInfo.hdl = ( rtems_irq_hdl )ap_interrupt_handler;
+ sc->irqInfo.on = nopOn;
+ sc->irqInfo.off = nopOn;
+ sc->irqInfo.isOn = _3c509_IsOn;
+
+ printf ("3c509: IRQ with Kernel: %d\n", sc->irqInfo.name );
+ st = BSP_install_rtems_irq_handler( &sc->irqInfo );
+ if( !st )
+ {
+ rtems_panic ("Can't attach WD interrupt handler for irq %d\n", sc->irqInfo.name );
+ }
+}
+
+/**********************************************************************************
+ *
+ * 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_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, int 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->irqInfo.name = config->irno;
+ else
+ sc->irqInfo.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->irqInfo.name = irq;
+
+ GO_WINDOW( 0 );
+ SET_IRQ( BASE, irq );
+
+ printf( "3C509: I/O=0x%x, IRQ=%d, CONNECTOR=%s, ",
+ sc->ep_io_addr, sc->irqInfo.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, caddr_t), m->m_len / 4 );
+ if( m->m_len & 3 )
+ outsb(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t) + (m->m_len & (~3)), m->m_len & 3 );
+ }
+ else
+ {
+ outsw( BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2 );
+ if( m->m_len & 1 )
+ outb( BASE + EP_W1_TX_PIO_WR_1, *(mtod(m, caddr_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, caddr_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, caddr_t) + m->m_len, lenthisone / 4);
+ m->m_len += (lenthisone & ~3);
+ if (lenthisone & 3)
+ insb(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone & 3);
+ m->m_len += (lenthisone & 3);
+ }
+ else
+ {
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, lenthisone / 2);
+ m->m_len += lenthisone;
+ if( lenthisone & 1 )
+ *(mtod(m, caddr_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/c/src/lib/libbsp/i386/pc386/3c509/3c509.h b/c/src/lib/libbsp/i386/pc386/3c509/3c509.h
new file mode 100644
index 0000000000..3c12e989a1
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/3c509/3c509.h
@@ -0,0 +1,423 @@
+/*
+ * 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
+
+ */
+/*
+ * $Id$
+ *
+ * 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
+ */
+
+/*
+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/c/src/lib/libbsp/i386/pc386/3c509/Makefile.in b/c/src/lib/libbsp/i386/pc386/3c509/Makefile.in
new file mode 100644
index 0000000000..6e926826dc
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/3c509/Makefile.in
@@ -0,0 +1,65 @@
+#
+# $Id$
+#
+
+@SET_MAKE@
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = ../../../../../../..
+subdir = c/src/lib/libbsp/i386/pc386/3c509
+
+INSTALL = @INSTALL@
+
+RTEMS_ROOT = $(top_srcdir)/@RTEMS_TOPdir@
+PROJECT_ROOT = @PROJECT_ROOT@
+
+VPATH = @srcdir@
+
+PGM=${ARCH}/3c509.rel
+
+# C source names, if any, go here -- minus the .c
+C_PIECES=3c509 elink
+C_FILES=$(C_PIECES:%=%.c)
+C_O_FILES=$(C_PIECES:%=${ARCH}/%.o)
+
+H_FILES=
+
+SRCS=$(C_FILES) $(H_FILES)
+OBJS=$(C_O_FILES)
+
+include $(RTEMS_ROOT)/make/custom/${RTEMS_BSP}.cfg
+include $(RTEMS_ROOT)/make/leaf.cfg
+
+#
+# (OPTIONAL) Add local stuff here using +=
+#
+
+DEFINES += -D__INSIDE_RTEMS_BSD_TCPIP_STACK__
+CPPFLAGS +=
+CFLAGS +=
+
+LD_PATHS +=
+LD_LIBS +=
+LDFLAGS +=
+
+#
+# Add your list of files to delete here. The config files
+# already know how to delete some stuff, so you may want
+# to just run 'make clean' first to see what gets missed.
+# 'make clobber' already includes 'make clean'
+#
+
+CLEAN_ADDITIONS +=
+CLOBBER_ADDITIONS +=
+
+${PGM}: ${SRCS} ${OBJS}
+ $(make-rel)
+
+all: ${ARCH} $(SRCS) $(PGM)
+
+# the .rel file built here will be put into libbsp.a by ../wrapup/Makefile
+install: all
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
diff --git a/c/src/lib/libbsp/i386/pc386/3c509/elink.c b/c/src/lib/libbsp/i386/pc386/3c509/elink.c
new file mode 100644
index 0000000000..064d747143
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/3c509/elink.c
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+/*
+ * Common code for dealing with 3COM ethernet cards.
+ */
+
+#include <sys/types.h>
+
+#include "elink.h"
+
+
+extern void outb( unsigned short io_addr, unsigned char 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/c/src/lib/libbsp/i386/pc386/3c509/elink.h b/c/src/lib/libbsp/i386/pc386/3c509/elink.h
new file mode 100644
index 0000000000..66e099ecf3
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/3c509/elink.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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/c/src/lib/libbsp/i386/pc386/Makefile.in b/c/src/lib/libbsp/i386/pc386/Makefile.in
index f2215728c4..2827b729a4 100644
--- a/c/src/lib/libbsp/i386/pc386/Makefile.in
+++ b/c/src/lib/libbsp/i386/pc386/Makefile.in
@@ -19,7 +19,7 @@ include $(RTEMS_ROOT)/make/custom/${RTEMS_BSP}.cfg
include $(RTEMS_ROOT)/make/directory.cfg
# We only build the Network library if HAS_NETWORKING was defined
-NETWORK_yes_V = dec21140 ne2000 wd8003
+NETWORK_yes_V = dec21140 ne2000 wd8003 3c509
NETWORK = $(NETWORK_$(HAS_NETWORKING)_V)
# wrapup is the one that actually builds and installs the library
diff --git a/c/src/lib/libbsp/i386/pc386/wrapup/Makefile.in b/c/src/lib/libbsp/i386/pc386/wrapup/Makefile.in
index 9b5c8073e6..fce8c0c3e1 100644
--- a/c/src/lib/libbsp/i386/pc386/wrapup/Makefile.in
+++ b/c/src/lib/libbsp/i386/pc386/wrapup/Makefile.in
@@ -16,7 +16,7 @@ PROJECT_ROOT = @PROJECT_ROOT@
VPATH = @srcdir@
# We only build the Network library if HAS_NETWORKING was defined
-NETWORK_yes_V = dec21140 ne2000 wd8003
+NETWORK_yes_V = dec21140 ne2000 wd8003 3c509
NETWORK = $(NETWORK_$(HAS_NETWORKING)_V)
BSP_PIECES=startup clock console timer $(NETWORK)