summaryrefslogtreecommitdiffstats
path: root/bsps/i386/pc386/net/3c509.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/i386/pc386/net/3c509.c')
-rw-r--r--bsps/i386/pc386/net/3c509.c1538
1 files changed, 1538 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+" ); */
+}