Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / drivers / net / ns8390.c
diff --git a/qemu/roms/ipxe/src/drivers/net/ns8390.c b/qemu/roms/ipxe/src/drivers/net/ns8390.c
new file mode 100644 (file)
index 0000000..0ffc621
--- /dev/null
@@ -0,0 +1,1037 @@
+/**************************************************************************
+ETHERBOOT -  BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+  Date: May/94
+
+ This code is based heavily on David Greenman's if_ed.c driver
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+  This software may be used, modified, copied, distributed, and sold, in
+  both source and binary form provided that the above copyright and these
+  terms are retained. Under no circumstances are the authors responsible for
+  the proper functioning of this software, nor do the authors assume any
+  responsibility for damages incurred with its use.
+
+Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
+Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
+3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
+SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
+3c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98
+RX overrun by Klaus Espenlaub (espenlaub@informatik.uni-ulm.de) on 3/10/99
+  parts taken from the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
+SMC8416 PIO support added by Andrew Bettison (andrewb@zip.com.au) on 4/3/02
+  based on the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
+
+**************************************************************************/
+
+FILE_LICENCE ( BSD2 );
+
+/* #warning "ns8390.c: FIXME: split ISA and PCI, clean up" */
+
+#if 1
+
+#if !defined(INCLUDE_NS8390) && !defined(INCLUDE_WD) && \
+    !defined(INCLUDE_NE) && !defined(INCLUDE_3C503)
+  /* The driver named ns8390 is the PCI driver, often called
+     "PCI ne2000 clones". */
+# define INCLUDE_NS8390 1
+#endif
+
+#include "etherboot.h"
+#include "nic.h"
+#include "ns8390.h"
+#include <ipxe/ethernet.h>
+#ifdef INCLUDE_NS8390
+#include <ipxe/pci.h>
+#else
+#include <ipxe/isa.h>
+#endif
+
+static unsigned char   eth_vendor, eth_flags;
+#ifdef INCLUDE_WD
+static unsigned char   eth_laar;
+#endif
+static unsigned short  eth_nic_base, eth_asic_base;
+static unsigned char   eth_memsize, eth_rx_start, eth_tx_start;
+static Address         eth_bmem, eth_rmem;
+static unsigned char   eth_drain_receiver;
+
+#ifdef INCLUDE_WD
+static struct wd_board {
+       const char *name;
+       char id;
+       char flags;
+       char memsize;
+} wd_boards[] = {
+       {"WD8003S",     TYPE_WD8003S,   0,                      MEM_8192},
+       {"WD8003E",     TYPE_WD8003E,   0,                      MEM_8192},
+       {"WD8013EBT",   TYPE_WD8013EBT, FLAG_16BIT,             MEM_16384},
+       {"WD8003W",     TYPE_WD8003W,   0,                      MEM_8192},
+       {"WD8003EB",    TYPE_WD8003EB,  0,                      MEM_8192},
+       {"WD8013W",     TYPE_WD8013W,   FLAG_16BIT,             MEM_16384},
+       {"WD8003EP/WD8013EP",
+                       TYPE_WD8013EP,  0,                      MEM_8192},
+       {"WD8013WC",    TYPE_WD8013WC,  FLAG_16BIT,             MEM_16384},
+       {"WD8013EPC",   TYPE_WD8013EPC, FLAG_16BIT,             MEM_16384},
+       {"SMC8216T",    TYPE_SMC8216T,  FLAG_16BIT | FLAG_790,  MEM_16384},
+       {"SMC8216C",    TYPE_SMC8216C,  FLAG_16BIT | FLAG_790,  MEM_16384},
+       {"SMC8416T",    TYPE_SMC8416T,  FLAG_16BIT | FLAG_790,  MEM_8192},
+       {"SMC8416C/BT", TYPE_SMC8416C,  FLAG_16BIT | FLAG_790,  MEM_8192},
+       {"SMC8013EBP",  TYPE_SMC8013EBP,FLAG_16BIT,             MEM_16384},
+       {NULL,          0,              0,                      0}
+};
+#endif
+
+#ifdef INCLUDE_3C503
+static unsigned char   t503_output;    /* AUI or internal xcvr (Thinnet) */
+#endif
+
+#if    defined(INCLUDE_WD)
+#define        ASIC_PIO        WD_IAR
+#define        eth_probe       wd_probe
+#if    defined(INCLUDE_3C503) || defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if    defined(INCLUDE_3C503)
+#define        eth_probe       t503_probe
+#if    defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if    defined(INCLUDE_NE)
+#define        eth_probe       ne_probe
+#if    defined(INCLUDE_NS8390) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if    defined(INCLUDE_NS8390)
+#define        eth_probe       nepci_probe
+#if    defined(INCLUDE_NE) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if    defined(INCLUDE_3C503)
+#define        ASIC_PIO        _3COM_RFMSB
+#else
+#if    defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+#define        ASIC_PIO        NE_DATA
+#endif
+#endif
+
+#if    defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM)) || (defined(INCLUDE_WD) && defined(WD_790_PIO))
+/**************************************************************************
+ETH_PIO_READ - Read a frame via Programmed I/O
+**************************************************************************/
+static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt)
+{
+#ifdef INCLUDE_WD
+       outb(src & 0xff, eth_asic_base + WD_GP2);
+       outb(src >> 8, eth_asic_base + WD_GP2);
+#else
+       outb(D8390_COMMAND_RD2 |
+               D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+       outb(cnt, eth_nic_base + D8390_P0_RBCR0);
+       outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
+       outb(src, eth_nic_base + D8390_P0_RSAR0);
+       outb(src>>8, eth_nic_base + D8390_P0_RSAR1);
+       outb(D8390_COMMAND_RD0 |
+               D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+
+#ifdef INCLUDE_3C503
+       outb(src & 0xff, eth_asic_base + _3COM_DALSB);
+       outb(src >> 8, eth_asic_base + _3COM_DAMSB);
+       outb(t503_output | _3COM_CR_START, eth_asic_base + _3COM_CR);
+#endif
+#endif
+
+       if (eth_flags & FLAG_16BIT)
+               cnt = (cnt + 1) >> 1;
+
+       while(cnt--) {
+#ifdef INCLUDE_3C503
+               while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
+                       ;
+#endif
+
+               if (eth_flags & FLAG_16BIT) {
+                       *((unsigned short *)dst) = inw(eth_asic_base + ASIC_PIO);
+                       dst += 2;
+               }
+               else
+                       *(dst++) = inb(eth_asic_base + ASIC_PIO);
+       }
+
+#ifdef INCLUDE_3C503
+       outb(t503_output, eth_asic_base + _3COM_CR);
+#endif
+}
+
+/**************************************************************************
+ETH_PIO_WRITE - Write a frame via Programmed I/O
+**************************************************************************/
+static void eth_pio_write(const unsigned char *src, unsigned int dst, unsigned int cnt)
+{
+#ifdef COMPEX_RL2000_FIX
+       unsigned int x;
+#endif /* COMPEX_RL2000_FIX */
+#ifdef INCLUDE_WD
+       outb(dst & 0xff, eth_asic_base + WD_GP2);
+       outb(dst >> 8, eth_asic_base + WD_GP2);
+#else
+       outb(D8390_COMMAND_RD2 |
+               D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+       outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
+       outb(cnt, eth_nic_base + D8390_P0_RBCR0);
+       outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
+       outb(dst, eth_nic_base + D8390_P0_RSAR0);
+       outb(dst>>8, eth_nic_base + D8390_P0_RSAR1);
+       outb(D8390_COMMAND_RD1 |
+               D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+
+#ifdef INCLUDE_3C503
+       outb(dst & 0xff, eth_asic_base + _3COM_DALSB);
+       outb(dst >> 8, eth_asic_base + _3COM_DAMSB);
+
+       outb(t503_output | _3COM_CR_DDIR | _3COM_CR_START, eth_asic_base + _3COM_CR);
+#endif
+#endif
+
+       if (eth_flags & FLAG_16BIT)
+               cnt = (cnt + 1) >> 1;
+
+       while(cnt--)
+       {
+#ifdef INCLUDE_3C503
+               while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
+                       ;
+#endif
+
+               if (eth_flags & FLAG_16BIT) {
+                       outw(*((unsigned short *)src), eth_asic_base + ASIC_PIO);
+                       src += 2;
+               }
+               else
+                       outb(*(src++), eth_asic_base + ASIC_PIO);
+       }
+
+#ifdef INCLUDE_3C503
+       outb(t503_output, eth_asic_base + _3COM_CR);
+#else
+#ifdef COMPEX_RL2000_FIX
+       for (x = 0;
+               x < COMPEX_RL2000_TRIES &&
+               (inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
+               != D8390_ISR_RDC;
+               ++x);
+       if (x >= COMPEX_RL2000_TRIES)
+               printf("Warning: Compex RL2000 aborted wait!\n");
+#endif /* COMPEX_RL2000_FIX */
+#ifndef        INCLUDE_WD
+       while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
+               != D8390_ISR_RDC);
+#endif
+#endif
+}
+#else
+/**************************************************************************
+ETH_PIO_READ - Dummy routine when NE2000 not compiled in
+**************************************************************************/
+static void eth_pio_read(unsigned int src __unused, unsigned char *dst  __unused, unsigned int cnt __unused) {}
+#endif
+
+
+/**************************************************************************
+enable_multycast - Enable Multicast
+**************************************************************************/
+static void enable_multicast(unsigned short eth_nic_base) 
+{
+       unsigned char mcfilter[8];
+       int i;
+       memset(mcfilter, 0xFF, 8);
+       outb(4, eth_nic_base+D8390_P0_RCR);     
+       outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1, eth_nic_base + D8390_P0_COMMAND);
+       for(i=0;i<8;i++)
+       {
+               outb(mcfilter[i], eth_nic_base + 8 + i);
+               if(inb(eth_nic_base + 8 + i)!=mcfilter[i])
+                       printf("Error SMC 83C690 Multicast filter read/write mishap %d\n",i);
+       }
+       outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0, eth_nic_base + D8390_P0_COMMAND);
+       outb(4 | 0x08, eth_nic_base+D8390_P0_RCR);
+}
+
+/**************************************************************************
+NS8390_RESET - Reset adapter
+**************************************************************************/
+static void ns8390_reset(struct nic *nic)
+{
+       int i;
+
+       eth_drain_receiver = 0;
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_790)
+               outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+       else
+#endif
+               outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+                       D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+       if (eth_flags & FLAG_16BIT)
+               outb(0x49, eth_nic_base+D8390_P0_DCR);
+       else
+               outb(0x48, eth_nic_base+D8390_P0_DCR);
+       outb(0, eth_nic_base+D8390_P0_RBCR0);
+       outb(0, eth_nic_base+D8390_P0_RBCR1);
+       outb(0x20, eth_nic_base+D8390_P0_RCR);  /* monitor mode */
+       outb(2, eth_nic_base+D8390_P0_TCR);
+       outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
+       outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_790) {
+#ifdef WD_790_PIO
+               outb(0x10, eth_asic_base + 0x06); /* disable interrupts, enable PIO */
+               outb(0x01, eth_nic_base + 0x09); /* enable ring read auto-wrap */
+#else
+               outb(0, eth_nic_base + 0x09);
+#endif
+       }
+#endif
+       outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
+       outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
+       outb(0xFF, eth_nic_base+D8390_P0_ISR);
+       outb(0, eth_nic_base+D8390_P0_IMR);
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_790)
+               outb(D8390_COMMAND_PS1 |
+                       D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+       else
+#endif
+               outb(D8390_COMMAND_PS1 |
+                       D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+       for (i=0; i<ETH_ALEN; i++)
+               outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
+       for (i=0; i<ETH_ALEN; i++)
+               outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
+       outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_790)
+               outb(D8390_COMMAND_PS0 |
+                       D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+       else
+#endif
+               outb(D8390_COMMAND_PS0 |
+                       D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+       outb(0xFF, eth_nic_base+D8390_P0_ISR);
+       outb(0, eth_nic_base+D8390_P0_TCR);     /* transmitter on */
+       outb(4, eth_nic_base+D8390_P0_RCR);     /* allow rx broadcast frames */
+
+       enable_multicast(eth_nic_base);
+
+#ifdef INCLUDE_3C503
+        /*
+         * No way to tell whether or not we're supposed to use
+         * the 3Com's transceiver unless the user tells us.
+         * 'flags' should have some compile time default value
+         * which can be changed from the command menu.
+         */
+       t503_output = (nic->flags) ? 0 : _3COM_CR_XSEL;
+       outb(t503_output, eth_asic_base + _3COM_CR);
+#endif
+}
+
+static int ns8390_poll(struct nic *nic, int retrieve);
+
+#ifndef        INCLUDE_3C503
+/**************************************************************************
+ETH_RX_OVERRUN - Bring adapter back to work after an RX overrun
+**************************************************************************/
+static void eth_rx_overrun(struct nic *nic)
+{
+       int start_time;
+
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_790)
+               outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+       else
+#endif
+               outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+                       D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+
+       /* wait for at least 1.6ms - we wait one timer tick */
+       start_time = currticks();
+       while (currticks() - start_time <= 1)
+               /* Nothing */;
+
+       outb(0, eth_nic_base+D8390_P0_RBCR0);   /* reset byte counter */
+       outb(0, eth_nic_base+D8390_P0_RBCR1);
+
+       /*
+        * Linux driver checks for interrupted TX here. This is not necessary,
+        * because the transmit routine waits until the frame is sent.
+        */
+
+       /* enter loopback mode and restart NIC */
+       outb(2, eth_nic_base+D8390_P0_TCR);
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_790)
+               outb(D8390_COMMAND_PS0 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+       else
+#endif
+               outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+                       D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+
+       /* clear the RX ring, acknowledge overrun interrupt */
+       eth_drain_receiver = 1;
+       while (ns8390_poll(nic, 1))
+               /* Nothing */;
+       eth_drain_receiver = 0;
+       outb(D8390_ISR_OVW, eth_nic_base+D8390_P0_ISR);
+
+       /* leave loopback mode - no packets to be resent (see Linux driver) */
+       outb(0, eth_nic_base+D8390_P0_TCR);
+}
+#endif /* INCLUDE_3C503 */
+
+/**************************************************************************
+NS8390_TRANSMIT - Transmit a frame
+**************************************************************************/
+static void ns8390_transmit(
+       struct nic *nic,
+       const char *d,                  /* Destination */
+       unsigned int t,                 /* Type */
+       unsigned int s,                 /* size */
+       const char *p)                  /* Packet */
+{
+#if defined(INCLUDE_3C503) || (defined(INCLUDE_WD) && ! defined(WD_790_PIO))
+       Address         eth_vmem = bus_to_virt(eth_bmem);
+#endif
+#ifdef INCLUDE_3C503
+        if (!(eth_flags & FLAG_PIO)) {
+                memcpy((char *)eth_vmem, d, ETH_ALEN); /* dst */
+                memcpy((char *)eth_vmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+                *((char *)eth_vmem+12) = t>>8;         /* type */
+                *((char *)eth_vmem+13) = t;
+                memcpy((char *)eth_vmem+ETH_HLEN, p, s);
+                s += ETH_HLEN;
+                while (s < ETH_ZLEN) *((char *)eth_vmem+(s++)) = 0;
+        }
+#endif
+
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_16BIT) {
+               outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+               inb(0x84);
+       }
+#ifndef        WD_790_PIO
+       /* Memory interface */
+       if (eth_flags & FLAG_790) {
+               outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
+               inb(0x84);
+       }
+       inb(0x84);
+       memcpy((char *)eth_vmem, d, ETH_ALEN);  /* dst */
+       memcpy((char *)eth_vmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+       *((char *)eth_vmem+12) = t>>8;          /* type */
+       *((char *)eth_vmem+13) = t;
+       memcpy((char *)eth_vmem+ETH_HLEN, p, s);
+       s += ETH_HLEN;
+       while (s < ETH_ZLEN) *((char *)eth_vmem+(s++)) = 0;
+       if (eth_flags & FLAG_790) {
+               outb(0, eth_asic_base + WD_MSR);
+               inb(0x84);
+       }
+#else
+       inb(0x84);
+#endif
+#endif
+
+#if    defined(INCLUDE_3C503)
+       if (eth_flags & FLAG_PIO)
+#endif
+#if    defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM)) || (defined(INCLUDE_WD) && defined(WD_790_PIO))
+       {
+               /* Programmed I/O */
+               unsigned short type;
+               type = (t >> 8) | (t << 8);
+               eth_pio_write( (unsigned char *) d, eth_tx_start<<8, ETH_ALEN);
+               eth_pio_write(nic->node_addr, (eth_tx_start<<8)+ETH_ALEN, ETH_ALEN);
+               /* bcc generates worse code without (const+const) below */
+               eth_pio_write((unsigned char *)&type, (eth_tx_start<<8)+(ETH_ALEN+ETH_ALEN), 2);
+               eth_pio_write( (unsigned char *) p, (eth_tx_start<<8)+ETH_HLEN, s);
+               s += ETH_HLEN;
+               if (s < ETH_ZLEN) s = ETH_ZLEN;
+       }
+#endif
+#if    defined(INCLUDE_3C503)
+#endif
+
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_16BIT) {
+               outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+               inb(0x84);
+       }
+       if (eth_flags & FLAG_790)
+               outb(D8390_COMMAND_PS0 |
+                       D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+       else
+#endif
+               outb(D8390_COMMAND_PS0 |
+                       D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+       outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
+       outb(s, eth_nic_base+D8390_P0_TBCR0);
+       outb(s>>8, eth_nic_base+D8390_P0_TBCR1);
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_790)
+               outb(D8390_COMMAND_PS0 |
+                       D8390_COMMAND_TXP | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+       else
+#endif
+               outb(D8390_COMMAND_PS0 |
+                       D8390_COMMAND_TXP | D8390_COMMAND_RD2 |
+                       D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+}
+
+/**************************************************************************
+NS8390_POLL - Wait for a frame
+**************************************************************************/
+static int ns8390_poll(struct nic *nic, int retrieve)
+{
+       int ret = 0;
+       unsigned char rstat, curr, next;
+       unsigned short len, frag;
+       unsigned short pktoff;
+       unsigned char *p;
+       struct ringbuffer pkthdr;
+
+#ifndef        INCLUDE_3C503
+       /* avoid infinite recursion: see eth_rx_overrun() */
+       if (!eth_drain_receiver && (inb(eth_nic_base+D8390_P0_ISR) & D8390_ISR_OVW)) {
+               eth_rx_overrun(nic);
+               return(0);
+       }
+#endif /* INCLUDE_3C503 */
+       rstat = inb(eth_nic_base+D8390_P0_RSR);
+       if (!(rstat & D8390_RSTAT_PRX)) return(0);
+       next = inb(eth_nic_base+D8390_P0_BOUND)+1;
+       if (next >= eth_memsize) next = eth_rx_start;
+       outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
+       curr = inb(eth_nic_base+D8390_P1_CURR);
+       outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
+       if (curr >= eth_memsize) curr=eth_rx_start;
+       if (curr == next) return(0);
+
+       if ( ! retrieve ) return 1;
+
+#ifdef INCLUDE_WD
+       if (eth_flags & FLAG_16BIT) {
+               outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+               inb(0x84);
+       }
+#ifndef        WD_790_PIO
+       if (eth_flags & FLAG_790) {
+               outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
+               inb(0x84);
+       }
+#endif
+       inb(0x84);
+#endif
+       pktoff = next << 8;
+       if (eth_flags & FLAG_PIO)
+               eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4);
+       else
+               memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4);
+       pktoff += sizeof(pkthdr);
+       /* incoming length includes FCS so must sub 4 */
+       len = pkthdr.len - 4;
+       if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
+               || len > ETH_FRAME_LEN) {
+               printf("Bogus packet, ignoring\n");
+               return (0);
+       }
+       else {
+               p = nic->packet;
+               nic->packetlen = len;           /* available to caller */
+               frag = (eth_memsize << 8) - pktoff;
+               if (len > frag) {               /* We have a wrap-around */
+                       /* read first part */
+                       if (eth_flags & FLAG_PIO)
+                               eth_pio_read(pktoff, p, frag);
+                       else
+                               memcpy(p, bus_to_virt(eth_rmem + pktoff), frag);
+                       pktoff = eth_rx_start << 8;
+                       p += frag;
+                       len -= frag;
+               }
+               /* read second part */
+               if (eth_flags & FLAG_PIO)
+                       eth_pio_read(pktoff, p, len);
+               else
+                       memcpy(p, bus_to_virt(eth_rmem + pktoff), len);
+               ret = 1;
+       }
+#ifdef INCLUDE_WD
+#ifndef        WD_790_PIO
+       if (eth_flags & FLAG_790) {
+               outb(0, eth_asic_base + WD_MSR);
+               inb(0x84);
+       }
+#endif
+       if (eth_flags & FLAG_16BIT) {
+               outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+               inb(0x84);
+       }
+       inb(0x84);
+#endif
+       next = pkthdr.next;             /* frame number of next packet */
+       if (next == eth_rx_start)
+               next = eth_memsize;
+       outb(next-1, eth_nic_base+D8390_P0_BOUND);
+       return(ret);
+}
+
+/**************************************************************************
+NS8390_DISABLE - Turn off adapter
+**************************************************************************/
+static void ns8390_disable ( struct nic *nic ) {
+       ns8390_reset(nic);
+}
+
+/**************************************************************************
+NS8390_IRQ - Enable, Disable, or Force interrupts
+**************************************************************************/
+static void ns8390_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+  switch ( action ) {
+  case DISABLE :
+    break;
+  case ENABLE :
+    break;
+  case FORCE :
+    break;
+  }
+}
+
+static struct nic_operations ns8390_operations;
+static struct nic_operations ns8390_operations = {
+       .connect        = dummy_connect,
+       .poll           = ns8390_poll,
+       .transmit       = ns8390_transmit,
+       .irq            = ns8390_irq,
+};
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+**************************************************************************/
+#ifdef INCLUDE_NS8390
+static int eth_probe (struct nic *nic, struct pci_device *pci)
+#else
+static int eth_probe (struct dev *dev, unsigned short *probe_addrs __unused)
+#endif
+{
+       int i;
+#ifdef INCLUDE_NS8390
+       unsigned short pci_probe_addrs[] = { pci->ioaddr, 0 };
+       unsigned short *probe_addrs = pci_probe_addrs;
+#endif
+       eth_vendor = VENDOR_NONE;
+       eth_drain_receiver = 0;
+
+       nic->irqno  = 0;
+
+#ifdef INCLUDE_WD
+{
+       /******************************************************************
+       Search for WD/SMC cards
+       ******************************************************************/
+       struct wd_board *brd;
+       unsigned short chksum;
+       unsigned char c;
+       for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE;
+               eth_asic_base += 0x20) {
+               chksum = 0;
+               for (i=8; i<16; i++)
+                       chksum += inb(eth_asic_base+i);
+               /* Extra checks to avoid soundcard */
+               if ((chksum & 0xFF) == 0xFF &&
+                       inb(eth_asic_base+8) != 0xFF &&
+                       inb(eth_asic_base+9) != 0xFF)
+                       break;
+       }
+       if (eth_asic_base > WD_HIGH_BASE)
+               return (0);
+       /* We've found a board */
+       eth_vendor = VENDOR_WD;
+       eth_nic_base = eth_asic_base + WD_NIC_ADDR;
+
+       nic->ioaddr = eth_nic_base;
+
+       c = inb(eth_asic_base+WD_BID);  /* Get board id */
+       for (brd = wd_boards; brd->name; brd++)
+               if (brd->id == c) break;
+       if (!brd->name) {
+               printf("Unknown WD/SMC NIC type %hhX\n", c);
+               return (0);     /* Unknown type */
+       }
+       eth_flags = brd->flags;
+       eth_memsize = brd->memsize;
+       eth_tx_start = 0;
+       eth_rx_start = D8390_TXBUF_SIZE;
+       if ((c == TYPE_WD8013EP) &&
+               (inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) {
+                       eth_flags = FLAG_16BIT;
+                       eth_memsize = MEM_16384;
+       }
+       if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) {
+               eth_bmem = (0x80000 |
+                ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13));
+       } else
+               eth_bmem = WD_DEFAULT_MEM;
+       if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) {
+               /* from Linux driver, 8416BT detects as 8216 sometimes */
+               unsigned int addr = inb(eth_asic_base + 0xb);
+               if (((addr >> 4) & 3) == 0) {
+                       brd += 2;
+                       eth_memsize = brd->memsize;
+               }
+       }
+       outb(0x80, eth_asic_base + WD_MSR);     /* Reset */
+       for (i=0; i<ETH_ALEN; i++) {
+               nic->node_addr[i] = inb(i+eth_asic_base+WD_LAR);
+       }
+       DBG ( "\n%s base %4.4x", brd->name, eth_asic_base );
+       if (eth_flags & FLAG_790) {
+#ifdef WD_790_PIO
+               DBG ( ", PIO mode, addr %s\n", eth_ntoa ( nic->node_addr ) );
+               eth_bmem = 0;
+               eth_flags |= FLAG_PIO;          /* force PIO mode */
+               outb(0, eth_asic_base+WD_MSR);
+#else
+               DBG ( ", Memory %x, MAC Addr %s\n", eth_bmem, eth_ntoa ( nic->node_addr) );
+
+               outb(WD_MSR_MENB, eth_asic_base+WD_MSR);
+               outb((inb(eth_asic_base+0x04) |
+                       0x80), eth_asic_base+0x04);
+               outb(((unsigned)(eth_bmem >> 13) & 0x0F) |
+                       ((unsigned)(eth_bmem >> 11) & 0x40) |
+                       (inb(eth_asic_base+0x0B) & 0xB0), eth_asic_base+0x0B);
+               outb((inb(eth_asic_base+0x04) &
+                       ~0x80), eth_asic_base+0x04);
+#endif
+       } else {
+
+               DBG (", Memory %x, MAC Addr %s\n", eth_bmem, eth_ntoa ( nic->node_addr) );
+
+               outb(((unsigned)(eth_bmem >> 13) & 0x3F) | 0x40, eth_asic_base+WD_MSR);
+       }
+       if (eth_flags & FLAG_16BIT) {
+               if (eth_flags & FLAG_790) {
+                       eth_laar = inb(eth_asic_base + WD_LAAR);
+                       outb(WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+               } else {
+                       outb((eth_laar =
+                               WD_LAAR_L16EN | 1), eth_asic_base + WD_LAAR);
+/*
+       The previous line used to be
+                               WD_LAAR_M16EN | WD_LAAR_L16EN | 1));
+       jluke@deakin.edu.au reported that removing WD_LAAR_M16EN made
+       it work for WD8013s.  This seems to work for my 8013 boards. I
+       don't know what is really happening.  I wish I had data sheets
+       or more time to decode the Linux driver. - Ken
+*/
+               }
+               inb(0x84);
+       }
+}
+#endif
+#ifdef INCLUDE_3C503
+#ifdef T503_AUI
+       nic->flags = 1;         /* aui */
+#else
+       nic->flags = 0;         /* no aui */
+#endif
+        /******************************************************************
+        Search for 3Com 3c503 if no WD/SMC cards
+        ******************************************************************/
+       if (eth_vendor == VENDOR_NONE) {
+               int     idx;
+               int     iobase_reg, membase_reg;
+               static unsigned short   base[] = {
+                       0x300, 0x310, 0x330, 0x350,
+                       0x250, 0x280, 0x2A0, 0x2E0, 0 };
+
+               /* Loop through possible addresses checking each one */
+
+               for (idx = 0; (eth_nic_base = base[idx]) != 0; ++idx) {
+
+                       eth_asic_base = eth_nic_base + _3COM_ASIC_OFFSET;
+/*
+ * Note that we use the same settings for both 8 and 16 bit cards:
+ * both have an 8K bank of memory at page 1 while only the 16 bit
+ * cards have a bank at page 0.
+ */
+                       eth_memsize = MEM_16384;
+                       eth_tx_start = 32;
+                       eth_rx_start = 32 + D8390_TXBUF_SIZE;
+
+               /* Check our base address. iobase and membase should */
+               /* both have a maximum of 1 bit set or be 0. */
+
+                       iobase_reg = inb(eth_asic_base + _3COM_BCFR);
+                       membase_reg = inb(eth_asic_base + _3COM_PCFR);
+
+                       if ((iobase_reg & (iobase_reg - 1)) ||
+                               (membase_reg & (membase_reg - 1)))
+                               continue;               /* nope */
+
+               /* Now get the shared memory address */
+
+                       eth_flags = 0;
+
+                       switch (membase_reg) {
+                               case _3COM_PCFR_DC000:
+                                       eth_bmem = 0xdc000;
+                                       break;
+                               case _3COM_PCFR_D8000:
+                                       eth_bmem = 0xd8000;
+                                       break;
+                               case _3COM_PCFR_CC000:
+                                       eth_bmem = 0xcc000;
+                                       break;
+                               case _3COM_PCFR_C8000:
+                                       eth_bmem = 0xc8000;
+                                       break;
+                               case _3COM_PCFR_PIO:
+                                       eth_flags |= FLAG_PIO;
+                                       eth_bmem = 0;
+                                       break;
+                               default:
+                                       continue;       /* nope */
+                               }
+                       break;
+               }
+
+               if (base[idx] == 0)             /* not found */
+                       return (0);
+#ifndef        T503_SHMEM
+               eth_flags |= FLAG_PIO;          /* force PIO mode */
+               eth_bmem = 0;
+#endif
+               eth_vendor = VENDOR_3COM;
+
+
+        /* Need this to make ns8390_poll() happy. */
+
+                eth_rmem = eth_bmem - 0x2000;
+
+        /* Reset NIC and ASIC */
+
+                outb(_3COM_CR_RST | _3COM_CR_XSEL, eth_asic_base + _3COM_CR );
+                outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR );
+
+        /* Get our ethernet address */
+
+                outb(_3COM_CR_EALO | _3COM_CR_XSEL, eth_asic_base + _3COM_CR);
+               nic->ioaddr = eth_nic_base;
+                DBG ( "\n3Com 3c503 base %4.4x, ", eth_nic_base );
+                if (eth_flags & FLAG_PIO)
+                       DBG ( "PIO mode" );
+                else
+                       DBG ( "memory %4.4x", eth_bmem );
+                for (i=0; i<ETH_ALEN; i++) {
+                        nic->node_addr[i] = inb(eth_nic_base+i);
+                }
+                DBG ( ", %s, MAC Addr %s\n", nic->flags ? "AUI" : "internal xcvr",
+                     eth_ntoa ( nic->node_addr ) );
+
+                outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR);
+        /*
+         * Initialize GA configuration register. Set bank and enable shared
+         * mem. We always use bank 1. Disable interrupts.
+         */
+                outb(_3COM_GACFR_RSEL |
+                       _3COM_GACFR_MBS0 | _3COM_GACFR_TCM | _3COM_GACFR_NIM, eth_asic_base + _3COM_GACFR);
+
+                outb(0xff, eth_asic_base + _3COM_VPTR2);
+                outb(0xff, eth_asic_base + _3COM_VPTR1);
+                outb(0x00, eth_asic_base + _3COM_VPTR0);
+        /*
+         * Clear memory and verify that it worked (we use only 8K)
+         */
+
+               if (!(eth_flags & FLAG_PIO)) {
+                       memset(bus_to_virt(eth_bmem), 0, 0x2000);
+                       for(i = 0; i < 0x2000; ++i)
+                               if (*((char *)(bus_to_virt(eth_bmem+i)))) {
+                                       printf ("Failed to clear 3c503 shared mem.\n");
+                                       return (0);
+                               }
+               }
+        /*
+         * Initialize GA page/start/stop registers.
+         */
+                outb(eth_tx_start, eth_asic_base + _3COM_PSTR);
+                outb(eth_memsize, eth_asic_base + _3COM_PSPR);
+        }
+#endif
+#if    defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+{
+       /******************************************************************
+       Search for NE1000/2000 if no WD/SMC or 3com cards
+       ******************************************************************/
+       unsigned char c;
+       if (eth_vendor == VENDOR_NONE) {
+               unsigned char romdata[16];
+               unsigned char testbuf[32];
+               int idx;
+               static unsigned char test[] = "NE*000 memory";
+               static unsigned short base[] = {
+#ifdef NE_SCAN
+                       NE_SCAN,
+#endif
+                       0 };
+               /* if no addresses supplied, fall back on defaults */
+               if (probe_addrs == NULL || probe_addrs[0] == 0)
+                       probe_addrs = base;
+               eth_bmem = 0;           /* No shared memory */
+               for (idx = 0; (eth_nic_base = probe_addrs[idx]) != 0; ++idx) {
+                       eth_flags = FLAG_PIO;
+                       eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
+                       eth_memsize = MEM_16384;
+                       eth_tx_start = 32;
+                       eth_rx_start = 32 + D8390_TXBUF_SIZE;
+                       c = inb(eth_asic_base + NE_RESET);
+                       outb(c, eth_asic_base + NE_RESET);
+                       (void) inb(0x84);
+                       outb(D8390_COMMAND_STP |
+                               D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
+                       outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
+                       outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
+                       outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
+                       outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
+#ifdef NS8390_FORCE_16BIT
+                       eth_flags |= FLAG_16BIT;        /* force 16-bit mode */
+#endif
+
+                       eth_pio_write( (unsigned char *) test, 8192, sizeof(test));
+                       eth_pio_read(8192, testbuf, sizeof(test));
+                       if (!memcmp(test, testbuf, sizeof(test)))
+                               break;
+                       eth_flags |= FLAG_16BIT;
+                       eth_memsize = MEM_32768;
+                       eth_tx_start = 64;
+                       eth_rx_start = 64 + D8390_TXBUF_SIZE;
+                       outb(D8390_DCR_WTS |
+                               D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
+                       outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
+                       outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
+                       eth_pio_write( (unsigned char *) test, 16384, sizeof(test));
+                       eth_pio_read(16384, testbuf, sizeof(test));
+                       if (!memcmp(testbuf, test, sizeof(test)))
+                               break;
+               }
+               if (eth_nic_base == 0)
+                       return (0);
+               if (eth_nic_base > ISA_MAX_ADDR)        /* PCI probably */
+                       eth_flags |= FLAG_16BIT;
+               eth_vendor = VENDOR_NOVELL;
+               eth_pio_read(0, romdata, sizeof(romdata));
+               for (i=0; i<ETH_ALEN; i++) {
+                       nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
+               }
+               nic->ioaddr = eth_nic_base;
+               DBG ( "\nNE%c000 base %4.4x, MAC Addr %s\n",
+                     (eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base,
+                     eth_ntoa ( nic->node_addr ) );
+       }
+}
+#endif
+       if (eth_vendor == VENDOR_NONE)
+               return(0);
+        if (eth_vendor != VENDOR_3COM)
+               eth_rmem = eth_bmem;
+       ns8390_reset(nic);
+       nic->nic_op     = &ns8390_operations;
+
+        /* Based on PnP ISA map */
+#ifdef INCLUDE_WD
+        dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+        dev->devid.device_id = htons(0x812a);
+#endif
+#ifdef INCLUDE_3C503
+        dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+        dev->devid.device_id = htons(0x80f3);
+#endif
+#ifdef INCLUDE_NE
+        dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+        dev->devid.device_id = htons(0x80d6);
+#endif
+       return 1;
+}
+
+#ifdef INCLUDE_WD
+struct isa_driver wd_driver __isa_driver = {
+       .type    = NIC_DRIVER,
+       .name    = "WD",
+       .probe   = wd_probe,
+       .ioaddrs = 0, 
+};
+ISA_ROM("wd","WD8003/8013, SMC8216/8416, SMC 83c790 (EtherEZ)");
+#endif
+
+#ifdef INCLUDE_3C503
+struct isa_driver t503_driver __isa_driver = {
+       .type    = NIC_DRIVER,
+       .name    = "3C503",
+       .probe   = t503_probe,
+       .ioaddrs = 0, 
+};
+ISA_ROM("3c503","3Com503, Etherlink II[/16]");
+#endif
+
+#ifdef INCLUDE_NE
+struct isa_driver ne_driver __isa_driver = {
+       .type    = NIC_DRIVER,
+       .name    = "NE*000",
+       .probe   = ne_probe,
+       .ioaddrs = 0, 
+};
+ISA_ROM("ne","NE1000/2000 and clones");
+#endif
+
+#ifdef INCLUDE_NS8390
+static struct pci_device_id nepci_nics[] = {
+/* A few NE2000 PCI clones, list not exhaustive */
+PCI_ROM(0x10ec, 0x8029, "rtl8029",      "Realtek 8029", 0),
+PCI_ROM(0x1186, 0x0300, "dlink-528",    "D-Link DE-528", 0),
+PCI_ROM(0x1050, 0x0940, "winbond940",   "Winbond NE2000-PCI", 0),              /* Winbond 86C940 / 89C940 */
+PCI_ROM(0x1050, 0x5a5a, "winbond940f",  "Winbond W89c940F", 0),                /* Winbond 89C940F */
+PCI_ROM(0x11f6, 0x1401, "compexrl2000", "Compex ReadyLink 2000", 0),
+PCI_ROM(0x8e2e, 0x3000, "ktiet32p2",    "KTI ET32P2", 0),
+PCI_ROM(0x4a14, 0x5000, "nv5000sc",     "NetVin NV5000SC", 0),
+PCI_ROM(0x12c3, 0x0058, "holtek80232",  "Holtek HT80232", 0),
+PCI_ROM(0x12c3, 0x5598, "holtek80229",  "Holtek HT80229", 0),
+PCI_ROM(0x10bd, 0x0e34, "surecom-ne34", "Surecom NE34", 0),
+PCI_ROM(0x1106, 0x0926, "via86c926",    "Via 86c926", 0),
+};
+
+PCI_DRIVER ( nepci_driver, nepci_nics, PCI_NO_CLASS );
+
+DRIVER ( "NE2000/PCI", nic_driver, pci_driver, nepci_driver,
+        nepci_probe, ns8390_disable );
+
+#endif /* INCLUDE_NS8390 */
+
+#endif
+
+/*
+ * Local variables:
+ *  c-basic-offset: 8
+ *  c-indent-level: 8
+ *  tab-width: 8
+ * End:
+ */