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