changeset 1526:dd185273ac78

First import of the LANCE PCI driver.
author asl
date Tue, 02 Mar 2004 10:13:55 +0000
parents 1ea738d9701c
children 29e6a3503d34
files packages/devs/eth/amd/lancepci/current/ChangeLog packages/devs/eth/amd/lancepci/current/cdl/amd_lancepci_eth_drivers.cdl packages/devs/eth/amd/lancepci/current/src/amd_lance.h packages/devs/eth/amd/lancepci/current/src/if_lancepci.c
diffstat 4 files changed, 2017 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/amd/lancepci/current/ChangeLog
@@ -0,0 +1,66 @@
+2004-03-02  Andrew Lunn  <andrew.lunn@ascom.ch>
+
+        * src/if_lancepci.c: Added casts to fix various compiler warnings.
+
+2004-02-27  Iztok Zupet  <iz@elsis.si>
+
+        * if_lancepci.c: fetch ESA from chips registers instead
+          EEPROM. There seems to be no usable data in the VMware EEPROM
+          emulation. But the data which comes in CSR_PAR registers seems
+          OK.
+
+2003-01-26  Iztok Zupet  <iz@vsr.si>
+
+        * if_lancepci.c: added aditional delay (printf) to init
+          function, to fix it under Vmware 4.
+
+2003-01-26  Iztok Zupet  <iz@vsr.si>
+
+        * if_lancepci.c: fixed buffer reset after start,
+          added additional delays (50ms)after start and control
+          function, to let Vmware get a tick so that it can
+          service the virtual chip. Added an interrupt controled
+          transmit busy flag (cpd->txbusyh), so that the stop/start
+          function can wait for it, thus not stopping the chip 
+          aburptly.
+
+        * amd_lance.h: additional items in cpd data structure. 
+
+2002-07-17  Iztok Zupet  <iz@vsr.si>
+
+        * all: Cloned from PCnet original 
+        
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/amd/lancepci/current/cdl/amd_lancepci_eth_drivers.cdl
@@ -0,0 +1,96 @@
+# ====================================================================
+#
+#      amd_lancepci_eth_drivers.cdl
+#
+#      Ethernet drivers - support for AMD LANCE PCI (vmWare) ethernet
+#      controllers
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+## at http://sources.redhat.com/ecos/ecos-license/
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      gthomas
+# Contributors:   gthomas, jskov, iz
+# Date:           2002-07-17
+#
+#####DESCRIPTIONEND####
+#
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_ETH_AMD_LANCEPCI {
+    display       "AMD LANCE compatible ethernet driver"
+    description   "Ethernet driver for AMD PCI LANCE compatible controllers."
+
+    parent        CYGPKG_IO_ETH_DRIVERS
+    active_if     CYGPKG_IO_ETH_DRIVERS
+
+    active_if     CYGINT_DEVS_ETH_AMD_LANCEPCI_REQUIRED
+
+    include_dir   .
+    include_files ; # none _exported_ whatsoever
+    compile       -library=libextras.a if_lancepci.c
+
+    define_proc {
+        puts $::cdl_header "#include <pkgconf/system.h>";
+        puts $::cdl_header "#include CYGDAT_DEVS_ETH_AMD_LANCEPCI_CFG";
+    }
+
+    cdl_option CYGNUM_DEVS_ETH_AMD_LANCEPCI_DEV_COUNT {
+        display "Number of supported interfaces."
+        calculated    { CYGINT_DEVS_ETH_AMD_LANCEPCI_REQUIRED }
+        flavor        data
+        description   "
+            This option selects the number of PCI ethernet interfaces to
+            be supported by the driver."
+    }
+
+    cdl_component CYGPKG_DEVS_ETH_AMD_LANCEPCI_OPTIONS {
+        display "LANCEPCI ethernet driver build options"
+        flavor  none
+        no_define
+
+        cdl_option CYGPKG_DEVS_ETH_AMD_LANCEPCI_CFLAGS_ADD {
+            display "Additional compiler flags"
+            flavor  data
+            no_define
+            default_value { "-D_KERNEL -D__ECOS" }
+            description   "
+                This option modifies the set of compiler flags for
+                building the LANCEPCI ethernet driver package.
+                These flags are used in addition
+                to the set of global flags."
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/amd/lancepci/current/src/amd_lance.h
@@ -0,0 +1,532 @@
+#ifndef CYGONCE_DEVS_ETH_AMD_LANCE_H
+#define CYGONCE_DEVS_ETH_AMD_LANCE_H
+//==========================================================================
+//
+//      amd_lance.h
+//
+//      AMD Lance Ethernet chip
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD or other sources,
+// and are covered by the appropriate copyright disclaimers included herein.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    jskov, iz
+// Contributors: jskov, hmt, iz
+// Date:         2002-07-17
+// Purpose:      Hardware description of AMD Lance series.
+// Description:  
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <cyg/hal/hal_io.h>
+
+//------------------------------------------------------------------------
+// Get macros from platform header
+#define __WANT_CONFIG
+#include CYGDAT_DEVS_ETH_AMD_LANCEPCI_INL
+#undef  __WANT_CONFIG
+
+//------------------------------------------------------------------------
+// Set to perms of:
+// 0 disables all debug output
+// 1 for process debug output
+// 2 for added data IO output: get_reg, put_reg
+// 4 for packet allocation/free output
+// 8 for only startup status, so we can tell we're installed OK
+#define DEBUG 0x0
+
+#if DEBUG & 1
+#define DEBUG_FUNCTION() do { os_printf("%s\n", __FUNCTION__); } while (0)
+#else
+#define DEBUG_FUNCTION() do {} while(0)
+#endif
+
+// ------------------------------------------------------------------------
+// Macros for keeping track of statistics
+#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD)
+# define KEEP_STATISTICS
+#endif
+
+#ifdef KEEP_STATISTICS
+# define INCR_STAT( _x_ )        (cpd->stats. _x_ ++)
+#else
+# define INCR_STAT( _x_ )        CYG_EMPTY_STATEMENT
+#endif
+
+//------------------------------------------------------------------------
+// Cache translation
+#ifndef CYGARC_UNCACHED_ADDRESS
+# define CYGARC_UNCACHED_ADDRESS(x) (x)
+#endif
+
+//------------------------------------------------------------------------
+// Address translation
+#ifndef HAL_PCI_CPU_TO_BUS
+# error "HAL PCI support must define translation macros"
+#endif
+
+// ------------------------------------------------------------------------
+// Macros for accessing structure elements
+
+#define _SU8( _base_, _offset_) \
+        *((cyg_uint8 *)((CYG_ADDRWORD)_base_+(_offset_)))
+#define _SU16( _base_, _offset_) \
+        *((cyg_uint16 *)((CYG_ADDRWORD)_base_+(_offset_)))
+#define _SU32( _base_, _offset_) \
+        *((cyg_uint32 *)((CYG_ADDRWORD)_base_+(_offset_)))
+
+#define _SI8( _base_, _offset_) \
+        *((cyg_int8 *)((CYG_ADDRWORD)_base_+(_offset_)))
+#define _SI16( _base_, _offset_) \
+        *((cyg_int16 *)((CYG_ADDRWORD)_base_+(_offset_)))
+#define _SI32( _base_, _offset_) \
+        *((cyg_int32 *)((CYG_ADDRWORD)_base_+(_offset_)))
+
+// ------------------------------------------------------------------------
+// Macros for accessing controller registers
+#ifndef HAL_PCI_IO_READ_UINT8
+# define HAL_PCI_IO_READ_UINT8(addr, datum)   HAL_READ_UINT8(addr, datum)
+# define HAL_PCI_IO_WRITE_UINT8(addr, datum)  HAL_WRITE_UINT8(addr, datum)
+# define HAL_PCI_IO_READ_UINT16(addr, datum)  HAL_READ_UINT16(addr, datum)
+# define HAL_PCI_IO_WRITE_UINT16(addr, datum) HAL_WRITE_UINT16(addr, datum)
+# define HAL_PCI_IO_READ_UINT32(addr, datum)  HAL_READ_UINT32(addr, datum)
+# define HAL_PCI_IO_WRITE_UINT32(addr, datum) HAL_WRITE_UINT32(addr, datum)
+#endif
+
+// ------------------------------------------------------------------------
+// IO map registers
+#define LANCE_IO_EEPROM   0x00
+#define LANCE_IO_ID       0x0e
+#define LANCE_IO_RDP      0x10
+#define LANCE_IO_RAP      0x12
+#define LANCE_IO_RESET    0x14
+#define LANCE_IO_BDP      0x16
+
+// The ID of the 79C790 is 0x5757 - that may be different in other
+// (older) cards.
+#define LANCE_IO_ID_KEY   0x5757
+
+// ------------------------------------------------------------------------
+// Controller registers come in three sets: CSR, BCR and ANR. Use
+// high-bits do differentiate, make the put/get functions do the right
+// thing depending the state of these bits.
+#define LANCE_RAP_MASK    0x007f
+//#define LANCE_CSR_FLAG  0x0000        // implied
+#define LANCE_BCR_FLAG    0x0080
+#define LANCE_ANR_FLAG    0x0100
+
+
+// CSR registers
+#define LANCE_CSR_CSCR    0
+#define LANCE_CSR_IBA0    1
+#define LANCE_CSR_IBA1    2
+#define LANCE_CSR_IM      3
+#define LANCE_CSR_TFC     4
+#define LANCE_CSR_ECI     5
+#define LANCE_CSR_LAR0    8
+#define LANCE_CSR_LAR1    9
+#define LANCE_CSR_LAR2    10
+#define LANCE_CSR_LAR3    11
+#define LANCE_CSR_PAR0    12
+#define LANCE_CSR_PAR1    13
+#define LANCE_CSR_PAR2    14
+#define LANCE_CSR_MODE    15
+#define LANCE_CSR_BARRL   24
+#define LANCE_CSR_BARRU   25
+#define LANCE_CSR_BATRL   30
+#define LANCE_CSR_BATRU   31
+#define LANCE_CSR_RRC     72
+#define LANCE_CSR_TRC     74
+#define LANCE_CSR_RRLEN   76
+#define LANCE_CSR_TRLEN   78
+#define LANCE_CSR_ID_LO   88
+#define LANCE_CSR_ID_HI   89
+
+
+#define LANCE_CSR_CSCR_ERR       0x8000
+#define LANCE_CSR_CSCR_RES       0x4000
+#define LANCE_CSR_CSCR_CERR      0x2000
+#define LANCE_CSR_CSCR_MISS      0x1000
+#define LANCE_CSR_CSCR_MERR      0x0800
+#define LANCE_CSR_CSCR_RINT      0x0400
+#define LANCE_CSR_CSCR_TINT      0x0200
+#define LANCE_CSR_CSCR_IDON      0x0100
+#define LANCE_CSR_CSCR_INTR      0x0080
+#define LANCE_CSR_CSCR_IENA      0x0040
+#define LANCE_CSR_CSCR_RXON      0x0020
+#define LANCE_CSR_CSCR_TXON      0x0010
+#define LANCE_CSR_CSCR_TDMD      0x0008
+#define LANCE_CSR_CSCR_STOP      0x0004
+#define LANCE_CSR_CSCR_STRT      0x0002
+#define LANCE_CSR_CSCR_INIT      0x0001
+
+#define LANCE_CSR_CSCR_EV_MASK   0x007f
+
+#define LANCE_CSR_IM_MISSM       0x1000
+#define LANCE_CSR_IM_MERRM       0x0800
+#define LANCE_CSR_IM_RINTM       0x0400
+#define LANCE_CSR_IM_TINTM       0x0200
+#define LANCE_CSR_IM_IDONM       0x0100
+#define LANCE_CSR_IM_DXSUFLO     0x0040
+#define LANCE_CSR_IM_LAPPEN      0x0020
+#define LANCE_CSR_IM_DXMT2PD     0x0010
+#define LANCE_CSR_IM_EMBA        0x0008
+#define LANCE_CSR_IM_BSWP        0x0004
+
+#define LANCE_CSR_TFC_TXDPOLL    0x1000
+#define LANCE_CSR_TFC_APAD_XMT   0x0800
+#define LANCE_CSR_TFC_ASTRP_RCV  0x0400
+#define LANCE_CSR_TFC_MFCO       0x0200
+#define LANCE_CSR_TFC_MFCOM      0x0100
+#define LANCE_CSR_TFC_UINTCMD    0x0080
+#define LANCE_CSR_TFC_UINT       0x0040
+#define LANCE_CSR_TFC_RCVCCO     0x0020
+#define LANCE_CSR_TFC_RCVCCOM    0x0010
+#define LANCE_CSR_TFC_TXSTRT     0x0008
+#define LANCE_CSR_TFC_TXSTRTM    0x0004
+
+#define LANCE_CSR_ECI_TOKINTD      0x8000
+#define LANCE_CSR_ECI_LTINTEN      0x4000
+#define LANCE_CSR_ECI_SINT         0x0800
+#define LANCE_CSR_ECI_SINTE        0x0400
+#define LANCE_CSR_ECI_EXDINT       0x0080
+#define LANCE_CSR_ECI_EXDINTE      0x0040
+#define LANCE_CSR_ECI_MPPLBA       0x0020
+#define LANCE_CSR_ECI_MPINT        0x0010
+#define LANCE_CSR_ECI_MPINTE       0x0008
+#define LANCE_CSR_ECI_MPEN         0x0004
+#define LANCE_CSR_ECI_MPMODE       0x0002
+#define LANCE_CSR_ECI_SPND         0x0001
+
+#define LANCE_CSR_MODE_PROM        0x8000
+#define LANCE_CSR_MODE_DRCVBC      0x4000
+#define LANCE_CSR_MODE_DRCVPA      0x2000
+#define LANCE_CSR_MODE_PORTSEL     0x0180
+#define LANCE_CSR_MODE_INTL        0x0040
+#define LANCE_CSR_MODE_DRTY        0x0020
+#define LANCE_CSR_MODE_FCOLL       0x0010
+#define LANCE_CSR_MODE_DXMTFCS     0x0008
+#define LANCE_CSR_MODE_LOOP        0x0004
+#define LANCE_CSR_MODE_DTX         0x0002
+#define LANCE_CSR_MODE_DRX         0x0001
+
+// BCR registers
+#define LANCE_BCR_SWSTYLE (20 |LANCE_BCR_FLAG)
+#define LANCE_BCR_MIIADDR (33 |LANCE_BCR_FLAG)
+#define LANCE_BCR_MIIDATA (34 |LANCE_BCR_FLAG)
+
+#define LANCE_BCR_MIIADDR_PHYAD    0x03e0
+
+
+//----------------------------------------------------------------------------
+// Receive buffer Descriptor
+#if 1
+#define LANCE_RD_PTR       0x00        // 32 bit
+#define LANCE_RD_BLEN      0x04        // 16 bit (2's complement, negative)
+#define LANCE_RD_MLEN      0x06        // 16 bit
+#define LANCE_RD_SIZE      0x08
+
+#define LANCE_RD_PTR_OWN       0x80000000
+#define LANCE_RD_PTR_ERR       0x40000000
+#define LANCE_RD_PTR_FRAM      0x20000000
+#define LANCE_RD_PTR_OFLO      0x10000000
+#define LANCE_RD_PTR_CRC       0x08000000
+#define LANCE_RD_PTR_BUFF      0x04000000
+#define LANCE_RD_PTR_STP       0x02000000
+#define LANCE_RD_PTR_ENP       0x01000000
+#define LANCE_RD_PTR_MASK      0x00ffffff
+#else
+
+#define LANCE_RD_PTR       0x00
+#define LANCE_RD_BLEN      0x04
+#define LANCE_RD_MLEN      0x08
+#define LANCE_RD_USER      0x0c
+#define LANCE_RD_SIZE      0x10
+
+#define LANCE_RD_BLEN_OWN       0x80000000
+#define LANCE_RD_BLEN_ERR       0x40000000
+#define LANCE_RD_BLEN_FRAM      0x20000000
+#define LANCE_RD_BLEN_OFLO      0x10000000
+#define LANCE_RD_BLEN_CRC       0x08000000
+#define LANCE_RD_BLEN_BUFF      0x04000000
+#define LANCE_RD_BLEN_STP       0x02000000
+#define LANCE_RD_BLEN_ENP       0x01000000
+#define LANCE_RD_BLEN_BPE       0x00800000
+#define LANCE_RD_BLEN_PAM       0x00400000
+#define LANCE_RD_BLEN_LAFM      0x00200000
+#define LANCE_RD_BLEN_BAM       0x00100000
+#define LANCE_RD_BLEN_MASK      0x0000ffff
+#endif
+
+// Transmit buffer Descriptor
+#if 1
+#define LANCE_TD_PTR       0x00        // 32 bit
+#define LANCE_TD_LEN       0x04        // 16 bit (2's complement, negative)
+#define LANCE_TD_MISC      0x06        // 16 bit
+#define LANCE_TD_SIZE      0x08
+
+#define LANCE_TD_PTR_OWN       0x80000000
+#define LANCE_TD_PTR_ERR       0x40000000
+#define LANCE_TD_PTR_ADD_FCS   0x20000000
+#define LANCE_TD_PTR_MORE      0x10000000
+#define LANCE_TD_PTR_ONE       0x08000000
+#define LANCE_TD_PTR_DEF       0x04000000
+#define LANCE_TD_PTR_STP       0x02000000
+#define LANCE_TD_PTR_ENP       0x01000000
+#define LANCE_TD_PTR_MASK      0x00ffffff
+#else
+#define LANCE_TD_PTR       0x00
+#define LANCE_TD_LEN       0x04
+#define LANCE_TD_MISC      0x08
+#define LANCE_TD_USER      0x0c
+#define LANCE_TD_SIZE      0x10
+
+#define LANCE_TD_LEN_OWN       0x80000000
+#define LANCE_TD_LEN_ERR       0x40000000
+#define LANCE_TD_LEN_ADD_FCS   0x20000000
+#define LANCE_TD_LEN_MORE      0x10000000
+#define LANCE_TD_LEN_ONE       0x08000000
+#define LANCE_TD_LEN_DEF       0x04000000
+#define LANCE_TD_LEN_STP       0x02000000
+#define LANCE_TD_LEN_ENP       0x01000000
+#define LANCE_TD_LEN_BPE       0x00800000
+#define LANCE_TD_LEN_MASK      0x0000ffff
+
+#define LANCE_TD_FLAGS_BUFF     0x80000000
+#define LANCE_TD_FLAGS_UFLO     0x40000000
+#define LANCE_TD_FLAGS_EX_DEF   0x20000000
+#define LANCE_TD_FLAGS_LCOL     0x10000000
+#define LANCE_TD_FLAGS_LCAR     0x08000000
+#define LANCE_TD_FLAGS_RTRY     0x04000000
+#define LANCE_TD_FLAGS_TRC_MASK 0x0000000f
+#endif
+
+
+#define LANCE_TD_MISC_BUFF     0x8000
+#define LANCE_TD_MISC_UFLO     0x4000
+#define LANCE_TD_MISC_EXDEF    0x2000
+#define LANCE_TD_MISC_LCOL     0x1000
+#define LANCE_TD_MISC_LCAR     0x0800
+#define LANCE_TD_MISC_RTRY     0x0400
+#define LANCE_TD_MISC_TDR      0x03ff
+
+// Initialization Buffer
+#define LANCE_IB_MODE            0
+#define LANCE_IB_PADR0           2
+#define LANCE_IB_PADR1           4
+#define LANCE_IB_PADR2           6
+#define LANCE_IB_LADRF0          8
+#define LANCE_IB_LADRF1          10
+#define LANCE_IB_LADRF2          12
+#define LANCE_IB_LADRF3          14
+#define LANCE_IB_RDRA            16
+#define LANCE_IB_TDRA            20
+#define LANCE_IB_SIZE            24
+
+#define LANCE_IB_TDRA_CNT_shift  29
+#define LANCE_IB_TDRA_PTR_mask   0x00ffffff
+#define LANCE_IB_RDRA_CNT_shift  29
+#define LANCE_IB_RDRA_PTR_mask   0x00ffffff
+
+// ------------------------------------------------------------------------
+
+#ifdef KEEP_STATISTICS
+struct amd_lancepci_stats {
+    unsigned int tx_good             ;
+    unsigned int tx_max_collisions   ;
+    unsigned int tx_late_collisions  ;
+    unsigned int tx_underrun         ;
+    unsigned int tx_carrier_loss     ;
+    unsigned int tx_deferred         ;
+    unsigned int tx_sqetesterrors    ;
+    unsigned int tx_single_collisions;
+    unsigned int tx_mult_collisions  ;
+    unsigned int tx_total_collisions ;
+    unsigned int rx_good             ;
+    unsigned int rx_crc_errors       ;
+    unsigned int rx_align_errors     ;
+    unsigned int rx_resource_errors  ;
+    unsigned int rx_overrun_errors   ;
+    unsigned int rx_collisions       ;
+    unsigned int rx_short_frames     ;
+    unsigned int rx_too_long_frames  ;
+    unsigned int rx_symbol_errors    ;
+    unsigned int interrupts          ;
+    unsigned int rx_count            ;
+    unsigned int rx_deliver          ;
+    unsigned int rx_resource         ;
+    unsigned int rx_restart          ;
+    unsigned int tx_count            ;
+    unsigned int tx_complete         ;
+    unsigned int tx_dropped          ;
+};
+#endif
+
+typedef struct lancepci_priv_data {
+    int index;
+    cyg_uint8                           // (split up for atomic byte access)
+        found:1,                        // was hardware discovered?
+        mac_addr_ok:1,                  // can we bring up?
+        active:1,                       // has this if been brung up?
+        hardwired_esa:1,                // set if ESA is hardwired via CDL
+        txbusy:1,                       // A packet has been sent
+        txbusyh:1,                      // A packet has been sent for HW
+        spare1:2; 
+
+    cyg_uint16 event;
+    
+    unsigned long txkey;                // Used to ack when packet sent
+    unsigned char* base;                // Base address of controller EPROM region
+    int interrupt;                      // Interrupt vector used by controller
+    unsigned char esa[6];            // Controller ESA
+    // Function to configure the ESA - may fetch ESA from EPROM or 
+    // RedBoot config option.
+    void (*config_esa)(struct lancepci_priv_data* cpd);
+    void *ndp;                          // Network Device Pointer
+
+    cyg_handle_t  interrupt_handle;
+    cyg_interrupt interrupt_object;
+    int devid;
+
+    cyg_uint8* rx_buffers;              // ptr to base of buffer mem
+    cyg_uint8* rx_ring;                 // ptr to base of rx ring memory
+    int rx_ring_cnt;                    // number of entries in ring
+    int rx_ring_log_cnt;                // log of above
+    int rx_ring_next;                   // index of next full ring entry
+
+    cyg_uint8* tx_buffers;
+    cyg_uint8* tx_ring;
+    int tx_ring_cnt;
+    int tx_ring_log_cnt;
+    int tx_ring_free;                   // index of next free ring entry
+    int tx_ring_alloc;                  // index of first controller owned ring
+    int tx_ring_owned;                  // number of controller owned ring entries
+
+    int rxpacket;
+#ifdef KEEP_STATISTICS
+    struct amd_lancepci_stats stats;
+#endif
+#if DEBUG & 1
+    cyg_uint32 txd;
+#endif
+    cyg_uint8* init_table;				// lance init table pointer
+
+} lancepci_priv_data;
+
+// ------------------------------------------------------------------------
+
+static __inline__ cyg_uint16
+get_reg(struct eth_drv_sc *sc, int regno)
+{
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+    cyg_uint16 val, addr;
+
+    if (regno & LANCE_ANR_FLAG) {
+        // We could do this with recursive calls to get/put reg
+        // functions, but might as well just do it directly.
+        // First set ANR address
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_RAP, LANCE_BCR_MIIADDR & LANCE_RAP_MASK);
+        HAL_PCI_IO_READ_UINT16(cpd->base+LANCE_IO_BDP, addr);
+        addr &= LANCE_BCR_MIIADDR_PHYAD;
+        addr |= (regno & LANCE_RAP_MASK);
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_BDP, addr);
+        // Then read ANR register data
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_RAP, LANCE_BCR_MIIDATA & LANCE_RAP_MASK);
+        HAL_PCI_IO_READ_UINT16(cpd->base+LANCE_IO_BDP, val);
+    } else {
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_RAP, regno & LANCE_RAP_MASK);
+        if (regno & LANCE_BCR_FLAG)
+            HAL_PCI_IO_READ_UINT16(cpd->base+LANCE_IO_BDP, val);
+        else
+            HAL_PCI_IO_READ_UINT16(cpd->base+LANCE_IO_RDP, val);
+    }
+#if DEBUG & 2
+    os_printf("read %s reg %d val 0x%04x\n", 
+                (regno & LANCE_ANR_FLAG) ? "anr" : (regno & LANCE_BCR_FLAG) ? "bcr" : "csr", 
+                regno & LANCE_RAP_MASK, val);
+#endif
+    return val;
+}
+
+static __inline__ void
+put_reg(struct eth_drv_sc *sc, int regno, cyg_uint16 val)
+{
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+    cyg_uint16 addr;
+
+    if (regno & LANCE_ANR_FLAG) {
+        // We could do this with recursive calls to get/put reg
+        // functions, but might as well just do it directly.
+        // First set ANR address
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_RAP, LANCE_BCR_MIIADDR & LANCE_RAP_MASK);
+        HAL_PCI_IO_READ_UINT16(cpd->base+LANCE_IO_BDP, addr);
+        addr &= LANCE_BCR_MIIADDR_PHYAD;
+        addr |= (regno & LANCE_RAP_MASK);
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_BDP, addr);
+        // Then write ANR register data
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_RAP, LANCE_BCR_MIIDATA & LANCE_RAP_MASK);
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_BDP, val);
+    } else {
+        HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_RAP, regno & LANCE_RAP_MASK);
+        if (regno & LANCE_BCR_FLAG)
+            HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_BDP, val);
+        else
+            HAL_PCI_IO_WRITE_UINT16(cpd->base+LANCE_IO_RDP, val);
+    }
+
+#if DEBUG & 2
+    os_printf("write %s reg %d val 0x%04x\n", 
+                (regno & LANCE_ANR_FLAG) ? "anr" : (regno & LANCE_BCR_FLAG) ? "bcr" : "csr", 
+                regno & LANCE_RAP_MASK, val);
+#endif
+}
+
+// ------------------------------------------------------------------------
+#endif // CYGONCE_DEVS_ETH_AMD_LANCE_H
+// EOF amd_lance.h
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/amd/lancepci/current/src/if_lancepci.c
@@ -0,0 +1,1323 @@
+//==========================================================================
+//
+//      dev/if_lancepci.c
+//
+//      Ethernet device driver for AMD PCI Lance (for instance vmWare VLANCE)
+//      compatible controllers
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD or other sources,
+// and are covered by the appropriate copyright disclaimers included herein.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    jskov, based on lan91cxx driver by hmt & jskov, iz
+// Contributors: gthomas, jskov, hmt, iz
+// Date:         2002-07-17, 2003-01-26
+// Purpose:
+// Description:  hardware driver for AMD Lance PCI (and possibly PCnet)
+//               and wmWare VLANCE ethernet
+// Notes:        The controller is used in its 16bit mode. That means that
+//               all addresses are 24bit only - and that all controller
+//               accessed memory must be within the same 16MB region
+//               (starting at 0 on older controllers).
+//
+//               The KEEP_STATISTICS code is not implemented yet. Look
+//               for FIXME macro.
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+//#####VMWAREDESCRIPTIONBEGIN####
+//
+// Notes:        The vmWare VLACNCE virtual controller does not seem to do
+//               anything about SUSPEND  and seems it must be reinitialized after
+//               every STOP. In addition it lacks some registers.
+//
+//		 Sometimes, the driver must wait to let Vmware get a tick, to
+//		 process the chip initialization and control functions!!!
+//
+//		 That's the reason for not patching the PCnet driver
+//		 but cloning a special one from it.
+//
+//
+//####VMWAREDESCRIPTIONEND####
+//==========================================================================
+
+#include <pkgconf/system.h>
+#include <pkgconf/devs_eth_amd_lancepci.h>
+#include <pkgconf/io_eth_drivers.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_if.h>             // delays
+#include <string.h>
+#include <cyg/io/eth/netdev.h>
+#include <cyg/io/eth/eth_drv.h>
+#ifdef CYGPKG_NET
+#include <pkgconf/net.h>
+#include <cyg/kernel/kapi.h>
+#include <net/if.h>  			// Needed for struct ifnet
+#include <pkgconf/io_eth_drivers.h>
+#endif
+#include CYGHWR_MEMORY_LAYOUT_H
+
+#ifdef CYGPKG_IO_PCI
+#include <cyg/io/pci.h>
+#else
+#error "Need PCI package here"
+#endif
+
+#define FIXME 0
+
+#define _BUF_SIZE 1544
+
+#ifdef CYGPKG_INFRA_DEBUG
+// Then we log, OOI, the number of times we get a bad packet number
+// from the tx done fifo.
+int lancepci_txfifo_good = 0;
+int lancepci_txfifo_bad = 0;
+#endif
+
+#include "amd_lance.h"
+#define __WANT_DEVS
+#include CYGDAT_DEVS_ETH_AMD_LANCEPCI_INL
+#undef  __WANT_DEVS
+
+//#define DEBUG 0xff
+
+#if defined(CYGPKG_REDBOOT)
+
+static void db_printf( char *fmt, ... )
+{
+    extern int start_console(void);
+    extern void end_console(int);
+    va_list a;
+    int old_console;
+    va_start( a, fmt );
+    old_console = start_console();
+    diag_vprintf( fmt, a );
+    end_console(old_console);
+    va_end( a );
+}
+
+#else
+
+#define db_printf diag_printf
+
+#endif
+
+
+static struct eth_drv_sc *oursc;	//a dummy sc pointer
+
+static void lancepci_poll(struct eth_drv_sc *sc);
+
+
+// This ISR is called when the ethernet interrupt occurs
+static cyg_uint32
+lancepci_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+    struct lancepci_priv_data *cpd = (struct lancepci_priv_data *)data;
+
+    DEBUG_FUNCTION();
+
+    INCR_STAT( interrupts );
+    cpd->event = get_reg(oursc, LANCE_CSR_CSCR);
+    if (cpd->event & LANCE_CSR_CSCR_TINT)
+        cpd->txbusyh=0;				// take care of HW txbusy flag
+    cyg_drv_interrupt_mask(cpd->interrupt);
+    cyg_drv_interrupt_acknowledge(cpd->interrupt);
+    return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR
+}
+
+static void
+lancepci_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+    // This conditioning out is necessary because of explicit calls to this
+    // DSR - which would not ever be called in the case of a polled mode
+    // usage ie. in RedBoot.
+#ifdef CYGPKG_IO_ETH_DRIVERS_NET
+    struct lancepci_priv_data* cpd = (struct lancepci_priv_data *)data;
+    struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(cpd->ndp);
+    struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance);
+
+    // but here, it must be a *sc:
+    eth_drv_dsr( vector, count, (cyg_addrword_t)sc );
+#else
+# ifndef CYGPKG_REDBOOT
+#  error Empty lancepci ethernet DSR is compiled.  Is this what you want?
+# endif
+#endif
+}
+
+
+// The deliver function (ex-DSR)  handles the ethernet [logical] processing
+static void
+lancepci_deliver(struct eth_drv_sc *sc)
+{
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+
+    DEBUG_FUNCTION();
+
+    // Service the interrupt:
+    lancepci_poll(sc);
+    // Allow interrupts to happen again
+    cyg_drv_interrupt_unmask(cpd->interrupt);
+}
+
+static int
+lancepci_int_vector(struct eth_drv_sc *sc)
+{
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+
+    return (cpd->interrupt);
+}
+
+// ------------------------------------------------------------------------
+// Memory management
+//
+// Simply carve off from the front of the PCI mapped window into real memory
+static cyg_uint32 lancepci_heap_size;
+static cyg_uint8 *lancepci_heap_base;
+static cyg_uint8 *lancepci_heap_free;
+
+static void*
+pciwindow_mem_alloc(int size)
+{
+    void *p_memory;
+    int _size = size;
+
+    CYG_ASSERT(
+        (CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_BASE <= (int)lancepci_heap_free)
+        &&
+        ((CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_BASE +
+          CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_SIZE) > (int)lancepci_heap_free)
+        &&
+        (0 < lancepci_heap_size)
+        &&
+        (CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_SIZE >= lancepci_heap_size)
+        &&
+        (CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_BASE == (int)lancepci_heap_base),
+        "Heap variables corrupted" );
+
+    p_memory = (void *)0;
+    size = (size + 3) & ~3;
+    if ( (lancepci_heap_free+size) < (lancepci_heap_base+lancepci_heap_size) ) {
+        cyg_uint32 *p;
+        p_memory = (void *)lancepci_heap_free;
+        lancepci_heap_free += size;
+        for ( p = (cyg_uint32 *)p_memory; _size > 0; _size -= 4 )
+            *p++ = 0;
+    }
+
+#if DEBUG & 9
+    db_printf("Allocated %d bytes at 0x%08x\n", size, p_memory);
+#endif
+
+    return p_memory;
+}
+
+static cyg_pci_match_func find_lancepci_match_func;
+
+static cyg_bool
+find_lancepci_match_func( cyg_uint16 v, cyg_uint16 d, cyg_uint32 c, void *p )
+{
+#if DEBUG & 9
+    db_printf("PCI match vendor 0x%04x device 0x%04x\n", v, d);
+#endif
+    return (0x1022 == v) && (0x2000 == d);
+}
+
+static int
+pci_init_find_lancepci( void )
+{
+    cyg_pci_device_id devid;
+    cyg_pci_device dev_info;
+    cyg_uint16 cmd;
+    int device_index;
+    int found_devices = 0;
+
+    DEBUG_FUNCTION();
+
+#ifdef CYGARC_UNCACHED_ADDRESS
+    CYG_ASSERT( CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) ==
+                CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_BASE,
+      "PCI window configured does not match PCI memory section base" );
+#else
+    CYG_ASSERT( (CYG_ADDRWORD)CYGMEM_SECTION_pci_window ==
+                CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_BASE,
+      "PCI window configured does not match PCI memory section base" );
+#endif
+    CYG_ASSERT( CYGMEM_SECTION_pci_window_SIZE ==
+                CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_SIZE,
+        "PCI window configured does not match PCI memory section size" );
+
+    if (
+#ifdef CYGARC_UNCACHED_ADDRESS
+         CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) !=
+#else
+         (CYG_ADDRWORD)CYGMEM_SECTION_pci_window !=
+#endif
+         (CYG_ADDRWORD)CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_BASE
+         ||
+         CYGMEM_SECTION_pci_window_SIZE !=
+         CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_SIZE ) {
+#if DEBUG & 8
+        db_printf("pci_init_find_lancepci(): PCI window misconfigured\n");
+#endif
+        return 0;
+    }
+
+    // First initialize the heap in PCI window'd memory
+    lancepci_heap_size = CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_SIZE;
+    lancepci_heap_base = (cyg_uint8 *)CYGHWR_AMD_LANCEPCI_PCI_MEM_MAP_BASE;
+    lancepci_heap_free = lancepci_heap_base;
+#if DEBUG & 9
+    db_printf("pcimem : 0x%08x size: 0x%08x\n", lancepci_heap_base, lancepci_heap_size);
+#endif
+
+    cyg_pci_init();
+#if DEBUG & 8
+    db_printf("Finished cyg_pci_init();\n");
+#endif
+
+    devid = CYG_PCI_NULL_DEVID;
+
+    for (device_index = 0;
+         device_index < CYGNUM_DEVS_ETH_AMD_LANCEPCI_DEV_COUNT;
+         device_index++) {
+        struct lancepci_priv_data* cpd = lancepci_priv_array[device_index];
+
+        cpd->index = device_index;
+
+        // See above for find_lancepci_match_func - it selects any of several
+        // variants.  This is necessary in case we have multiple mixed-type
+        // devices on one board in arbitrary orders.
+        if (cyg_pci_find_matching( &find_lancepci_match_func, NULL, &devid )) {
+#if DEBUG & 8
+            db_printf("eth%d = lancepci\n", device_index);
+#endif
+            cyg_pci_get_device_info(devid, &dev_info);
+
+            cpd->interrupt_handle = 0; // Flag not attached.
+            if (cyg_pci_translate_interrupt(&dev_info, &cpd->interrupt)) {
+#if DEBUG & 8
+                db_printf(" Wired to HAL vector %d\n", cpd->interrupt);
+#endif
+                cyg_drv_interrupt_create(
+                    cpd->interrupt,
+                    1,                  // Priority - unused
+                    (cyg_addrword_t)cpd,// Data item passed to ISR & DSR
+                    lancepci_isr,          // ISR
+                    lancepci_dsr,          // DSR
+                    &cpd->interrupt_handle, // handle to intr obj
+                    &cpd->interrupt_object ); // space for int obj
+
+                cyg_drv_interrupt_attach(cpd->interrupt_handle);
+
+                // Don't unmask the interrupt yet, that could get us into a
+                // race.
+            }
+            else {
+                cpd->interrupt = 0;
+#if DEBUG & 8
+                db_printf(" Does not generate interrupts.\n");
+#endif
+            }
+
+            if (cyg_pci_configure_device(&dev_info)) {
+#if DEBUG & 8
+                int i;
+                db_printf("Found device on bus %d, devfn 0x%02x:\n",
+                          CYG_PCI_DEV_GET_BUS(devid),
+                          CYG_PCI_DEV_GET_DEVFN(devid));
+
+                if (dev_info.command & CYG_PCI_CFG_COMMAND_ACTIVE) {
+                    db_printf(" Note that board is active. Probed"
+                              " sizes and CPU addresses invalid!\n");
+                }
+                db_printf(" Vendor    0x%04x", dev_info.vendor);
+                db_printf("\n Device    0x%04x", dev_info.device);
+                db_printf("\n Command   0x%04x, Status 0x%04x\n",
+                          dev_info.command, dev_info.status);
+
+                db_printf(" Class/Rev 0x%08x", dev_info.class_rev);
+                db_printf("\n Header 0x%02x\n", dev_info.header_type);
+
+                db_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n",
+                          dev_info.header.normal.sub_vendor,
+                          dev_info.header.normal.sub_id);
+
+                for(i = 0; i < CYG_PCI_MAX_BAR; i++) {
+                    db_printf(" BAR[%d]    0x%08x /", i, dev_info.base_address[i]);
+                    db_printf(" probed size 0x%08x / CPU addr 0x%08x\n",
+                              dev_info.base_size[i], dev_info.base_map[i]);
+                }
+                db_printf(" eth%d configured\n", device_index);
+#endif
+                found_devices++;
+                cpd->found = 1;
+                cpd->active = 0;
+                cpd->devid = devid;
+                cpd->base = (unsigned char*) dev_info.base_map[0];
+#if DEBUG & 8
+                db_printf(" I/O address = 0x%08x\n", cpd->base);
+#endif
+
+                // Don't use cyg_pci_set_device_info since it clears
+                // some of the fields we want to print out below.
+                cyg_pci_read_config_uint16(dev_info.devid,
+                                           CYG_PCI_CFG_COMMAND, &cmd);
+                cmd |= (CYG_PCI_CFG_COMMAND_IO         // enable I/O space
+                        | CYG_PCI_CFG_COMMAND_MEMORY   // enable memory space
+                        | CYG_PCI_CFG_COMMAND_MASTER); // enable bus master
+                cyg_pci_write_config_uint16(dev_info.devid,
+                                            CYG_PCI_CFG_COMMAND, cmd);
+
+                // This is the indicator for "uses an interrupt"
+                if (cpd->interrupt_handle != 0) {
+                    cyg_drv_interrupt_acknowledge(cpd->interrupt);
+                    cyg_drv_interrupt_unmask(cpd->interrupt);
+#if DEBUG & 8
+                    db_printf(" Enabled interrupt %d\n", cpd->interrupt);
+#endif
+                }
+#if DEBUG & 8
+                db_printf(" **** Device enabled for I/O and Memory "
+                            "and Bus Master\n");
+#endif
+            }
+            else {
+                cpd->found = 0;
+                cpd->active = 0;
+#if DEBUG & 8
+                db_printf("Failed to configure device %d\n", device_index);
+#endif
+            }
+        }
+        else {
+            cpd->found = 0;
+            cpd->active = 0;
+#if DEBUG & 8
+            db_printf("eth%d not found\n", device_index);
+#endif
+        }
+    }
+
+    if (0 == found_devices)
+        return 0;
+
+    return 1;
+}
+
+
+static bool
+amd_lancepci_init(struct cyg_netdevtab_entry *tab)
+{
+    static int initialized = 0; // only probe PCI et al *once*
+    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+    cyg_uint16 val;
+    cyg_uint32 b;
+    cyg_uint8* p;
+    cyg_uint8* d;
+    int i;
+
+    DEBUG_FUNCTION();
+
+    if ( 0 == initialized++ ) {
+        // then this is the first time ever:
+        if ( ! pci_init_find_lancepci() ) {
+#if DEBUG & 8
+            db_printf( "pci_init_find_lancepci failed" );
+#endif
+            return false;
+        }
+    }
+
+    // If this device is not present, exit
+    if (0 == cpd->found)
+        return 0;
+
+#if DEBUG & 8
+    db_printf("lancepci at base 0x%08x, EEPROM key 0x%04x\n",
+                cpd->base, _SU16(cpd->base, LANCE_IO_ID));
+#endif
+
+#if 0
+    // FIXME: Doesn't work with non-conforming EEPROMS
+    if (LANCE_IO_ID_KEY != _SU16(cpd->base, LANCE_IO_ID) ) {
+        db_printf("Lance EPROM key not found\n");
+        return false;
+    }
+#endif
+
+#if DEBUG & 9
+    db_printf("pcimem : %08x size: %08x\n", lancepci_heap_base, lancepci_heap_size);
+#endif
+
+    // Prepare ESA
+    if (!cpd->hardwired_esa) {
+        // Don't use the address from the EEPROM for VMware
+        // Use the address that VMware prepares in CSR_PAR registers
+        // if You want to be able to use NAT networking. (iz@elsis.si Feb 27 04)
+        //
+        // p = cpd->base + LANCE_IO_EEPROM;
+        // for (i = 0; i < 6; i++)
+        // cpd->esa[i] = *p++;
+        put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_STOP);
+        for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
+            cyg_uint16 z = get_reg(sc, LANCE_CSR_PAR0+i/2 );
+            cpd->esa[i] =   (cyg_uint8)(0xff & z);
+            cpd->esa[i+1] = (cyg_uint8)(0xff & (z >> 8));
+      }
+
+    }
+#if DEBUG & 9
+    db_printf("Lance - %s ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                (cpd->hardwired_esa) ? "static" : "eeprom",
+                cpd->esa[0], cpd->esa[1], cpd->esa[2],
+                cpd->esa[3], cpd->esa[4], cpd->esa[5] );
+#endif
+
+
+    // Prepare RX and TX rings
+    p = cpd->rx_ring = (cyg_uint8*) CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc((1<<cpd->rx_ring_log_cnt)*LANCE_RD_SIZE));
+    memset(cpd->rx_ring,0,(1<<cpd->rx_ring_log_cnt)*LANCE_RD_SIZE);
+
+    d = cpd->rx_buffers = (cyg_uint8*) CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc(_BUF_SIZE*cpd->rx_ring_cnt));
+    memset(cpd->rx_buffers,0,_BUF_SIZE*cpd->rx_ring_cnt);
+
+    for (i = 0; i < cpd->rx_ring_cnt; i++) {
+        HAL_PCI_CPU_TO_BUS(d, (cyg_uint8 *)b);
+        _SU32(p, LANCE_RD_PTR) = (b & LANCE_RD_PTR_MASK) | LANCE_RD_PTR_OWN;
+        _SU16(p, LANCE_RD_BLEN) = (-_BUF_SIZE);
+        p += LANCE_RD_SIZE;
+        d += _BUF_SIZE;
+    }
+    cpd->rx_ring_next = 0;
+
+    p = cpd->tx_ring = (cyg_uint8*) CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc((1<<cpd->tx_ring_log_cnt)*LANCE_TD_SIZE));
+    memset(cpd->tx_ring,0,(1<<cpd->tx_ring_log_cnt)*LANCE_TD_SIZE);
+
+    d = cpd->tx_buffers = (cyg_uint8*) CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc(_BUF_SIZE*cpd->tx_ring_cnt));
+    for (i = 0; i < cpd->tx_ring_cnt; i++) {
+        HAL_PCI_CPU_TO_BUS(d, (cyg_uint8 *)b);
+        _SU32(p, LANCE_RD_PTR) = b & LANCE_TD_PTR_MASK;
+        p += LANCE_TD_SIZE;
+        d += _BUF_SIZE;
+    }
+    cpd->tx_ring_free = cpd->tx_ring_alloc = cpd->tx_ring_owned = 0;
+
+    // Initialization table
+    cpd->init_table = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc(LANCE_IB_SIZE));
+    _SU16(cpd->init_table, LANCE_IB_MODE) = 0x0000;
+    for (i = 0; i < 6; i++)
+        _SU8(cpd->init_table, LANCE_IB_PADR0+i) = cpd->esa[i];
+    for (i = 0; i < 8; i++)
+        _SU8(cpd->init_table, LANCE_IB_LADRF0+i) = 0;
+
+    HAL_PCI_CPU_TO_BUS(cpd->rx_ring, (cyg_uint8 *)b);
+    _SU32(cpd->init_table, LANCE_IB_RDRA) = ((b & LANCE_IB_RDRA_PTR_mask)
+                                        | (cpd->rx_ring_log_cnt << LANCE_IB_RDRA_CNT_shift));
+    HAL_PCI_CPU_TO_BUS(cpd->tx_ring, (cyg_uint8 *)b);
+    _SU32(cpd->init_table, LANCE_IB_TDRA) = ((b & LANCE_IB_TDRA_PTR_mask)
+                                        | (cpd->tx_ring_log_cnt << LANCE_IB_TDRA_CNT_shift));
+
+#if DEBUG & 9
+    db_printf("Loading up lance controller from table at 0x%08x\n", cpd->init_table);
+    db_printf(" Mode 0x%04x\n", _SU16(cpd->init_table, LANCE_IB_MODE));
+    db_printf(" PADR %02x:%02x:%02x:%02x:%02x:%02x ",
+                _SU8(cpd->init_table, LANCE_IB_PADR0+0), _SU8(cpd->init_table, LANCE_IB_PADR0+1),
+                _SU8(cpd->init_table, LANCE_IB_PADR0+2), _SU8(cpd->init_table, LANCE_IB_PADR0+3),
+                _SU8(cpd->init_table, LANCE_IB_PADR0+4), _SU8(cpd->init_table, LANCE_IB_PADR0+5));
+    db_printf("LADR %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+                _SU8(cpd->init_table, LANCE_IB_LADRF0+0), _SU8(cpd->init_table, LANCE_IB_LADRF0+1),
+                _SU8(cpd->init_table, LANCE_IB_LADRF0+2), _SU8(cpd->init_table, LANCE_IB_LADRF0+3),
+                _SU8(cpd->init_table, LANCE_IB_LADRF0+4), _SU8(cpd->init_table, LANCE_IB_LADRF0+5),
+                _SU8(cpd->init_table, LANCE_IB_LADRF0+5), _SU8(cpd->init_table, LANCE_IB_LADRF0+7));
+    db_printf(" RX 0x%08x (len %d) TX 0x%08x (len %d)\n",
+                _SU32(cpd->init_table, LANCE_IB_RDRA) & 0x1fffffff,
+                (_SU32(cpd->init_table, LANCE_IB_RDRA) >> LANCE_IB_RDRA_CNT_shift) & 7,
+                _SU32(cpd->init_table, LANCE_IB_TDRA) & 0x1fffffff,
+                (_SU32(cpd->init_table, LANCE_IB_TDRA) >> LANCE_IB_TDRA_CNT_shift) & 7);
+#endif
+
+    // Reset chip
+    HAL_PCI_IO_READ_UINT16(cpd->base+LANCE_IO_RESET, val);
+
+    // Load up chip with buffers.
+    // Note: There is a 16M limit on the addresses used by the driver
+    // since the top 8 bits of the init_table address is appended to
+    // all other addresses used by the controller.
+    HAL_PCI_CPU_TO_BUS(cpd->init_table, (cyg_uint8 *)b);
+    put_reg(sc, LANCE_CSR_IBA0, (b >>  0) & 0xffff);
+    put_reg(sc, LANCE_CSR_IBA1, (b >> 16) & 0xffff);
+    // Disable automatic TX polling (_send will force a poll), pad
+    // XT frames to legal length, mask status interrupts.
+    put_reg(sc, LANCE_CSR_TFC, (LANCE_CSR_TFC_TXDPOLL | LANCE_CSR_TFC_APAD_XMT
+                                | LANCE_CSR_TFC_MFCOM | LANCE_CSR_TFC_RCVCCOM
+                                | LANCE_CSR_TFC_TXSTRTM));
+    // Recover after TX FIFO underflow
+    put_reg(sc, LANCE_CSR_IM, LANCE_CSR_IM_DXSUFLO);
+    // Initialize controller - load up init_table
+    put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_INIT);
+    while (0 == (get_reg(sc, LANCE_CSR_CSCR) & LANCE_CSR_CSCR_IDON));
+
+    // Stop controller
+    put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_STOP);
+
+#if DEBUG & 9
+    db_printf("lancepci controller state is now:\n");
+    db_printf(" Mode 0x%04x  TFC 0x%04x\n", _SU16(cpd->init_table, LANCE_IB_MODE), get_reg(sc, LANCE_CSR_TFC));
+    db_printf(" PADR %04x:%04x:%04x ",
+                get_reg(sc, LANCE_CSR_PAR0),
+                get_reg(sc, LANCE_CSR_PAR1),
+                get_reg(sc, LANCE_CSR_PAR2));
+    db_printf("LADR %04x:%04x:%04x:%04x\n",
+                get_reg(sc, LANCE_CSR_LAR0),
+                get_reg(sc, LANCE_CSR_LAR1),
+                get_reg(sc, LANCE_CSR_LAR2),
+                get_reg(sc, LANCE_CSR_LAR3));
+    db_printf(" RX 0x%04x%04x (len 0x%04x) TX 0x%04x%04x (len 0x%04x)\n",
+                get_reg(sc, LANCE_CSR_BARRU), get_reg(sc, LANCE_CSR_BARRL),
+                get_reg(sc, LANCE_CSR_RRLEN),
+                get_reg(sc, LANCE_CSR_BATRU), get_reg(sc, LANCE_CSR_BATRL),
+                get_reg(sc, LANCE_CSR_TRLEN));
+
+    val = get_reg(sc, LANCE_CSR_ID_LO);
+    db_printf("lancepci ID 0x%04x (%s) ",
+                val,
+                (0x5003 == val) ? "Am79C973" : (0x7003 == val) ? "Am79C975" :
+		        (0x1003 == val) ? "Am79C900 or wmWare VLANCE" : "Unknown");
+    val = get_reg(sc, LANCE_CSR_ID_HI);
+    db_printf("Part IDU 0x%03x Silicon rev %d\n",
+                val & 0x0fff, (val >> 12) & 0xf);
+#endif
+    // and record the net dev pointer
+    cpd->ndp = (void *)tab;
+    cpd->active = 0;
+    oursc=sc;
+
+    // Initialize upper level driver
+    (sc->funs->eth_drv->init)(sc, cpd->esa);
+    cpd->txbusyh=cpd->txbusy=0;
+    cpd->event=0;
+    db_printf("Lancepci driver loaded and Init Done\n");
+    return true;
+}
+
+static void
+lancepci_stop(struct eth_drv_sc *sc)
+{
+    cyg_uint32 b;
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+
+    DEBUG_FUNCTION();
+    if (!cpd->active)
+        return;
+    if (cpd->txbusyh) {
+#if DEBUG & 9
+        db_printf("Lancepci-stop:waiting for tx empty\n");
+#endif
+        b=100;
+        while (cpd->txbusyh && b) {
+	    CYGACC_CALL_IF_DELAY_US(200);
+	    b--;
+	 }
+    }
+    put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_STOP);
+    cpd->active = 0;
+#if DEBUG & 9
+    db_printf("Lancepci-stop:done\n");
+#endif
+}
+
+//
+// This function is called to "start up" the interface.  It may be called
+// multiple times, even when the hardware is already running.  It will be
+// called whenever something "hardware oriented" changes and should leave
+// the hardware ready to send/receive packets.
+//
+static void
+lancepci_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
+{
+    cyg_uint16 reg;
+    cyg_uint32 b;
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+#ifdef CYGPKG_NET
+    struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+#endif
+    DEBUG_FUNCTION();
+
+    // If device is already active, stop it
+#if DEBUG & 9
+	db_printf("Lancepci-start:entered\n");
+#endif
+    if (cpd->active)
+     {
+        if (cpd->txbusyh) {
+#if DEBUG & 9
+	    db_printf("Lancepci-start:waiting for tx empty\n");
+#endif
+	    b=100;
+     	    while (cpd->txbusyh && b) {
+	        CYGACC_CALL_IF_DELAY_US(200);
+		b--;
+	    }
+	}
+    	put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_STOP);
+    	cpd->active = 0;
+#if DEBUG & 9
+    	db_printf("Lancepci-start:stopped\n");
+#endif
+    }
+    CYGACC_CALL_IF_DELAY_US(200);
+
+#ifdef CYGPKG_NET
+    if (( 0
+#ifdef ETH_DRV_FLAGS_PROMISC_MODE
+         != (flags & ETH_DRV_FLAGS_PROMISC_MODE)
+#endif
+        ) || (ifp->if_flags & IFF_PROMISC)
+        ) {
+        // Then we select promiscuous mode.
+    	_SU16(cpd->init_table, LANCE_IB_MODE) = 0x0000|LANCE_CSR_MODE_PROM;
+#if DEBUG & 9
+	db_printf("Promisc MODE!");
+#endif
+    }
+    else _SU16(cpd->init_table, LANCE_IB_MODE) = 0x0000;
+#endif
+    cpd->rx_ring_next = 0;
+    cpd->tx_ring_free = cpd->tx_ring_alloc = cpd->tx_ring_owned = 0;
+    // Init the chip again
+    HAL_PCI_CPU_TO_BUS(cpd->init_table, (cyg_uint8 *)b);
+    put_reg(sc, LANCE_CSR_IBA0, (b >>  0) & 0xffff);
+    put_reg(sc, LANCE_CSR_IBA1, (b >> 16) & 0xffff);
+    // Disable automatic TX polling (_send will force a poll), pad
+    // XT frames to legal length, mask status interrupts.
+    put_reg(sc, LANCE_CSR_TFC, (LANCE_CSR_TFC_TXDPOLL | LANCE_CSR_TFC_APAD_XMT
+                                | LANCE_CSR_TFC_MFCOM | LANCE_CSR_TFC_RCVCCOM
+                                | LANCE_CSR_TFC_TXSTRTM));
+    // Recover after TX FIFO underflow
+    put_reg(sc, LANCE_CSR_IM, LANCE_CSR_IM_DXSUFLO);
+    // Initialize controller - load up init_table
+    put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_INIT);
+    while (0 == (get_reg(sc, LANCE_CSR_CSCR) & LANCE_CSR_CSCR_IDON));
+	reg=get_reg(sc,LANCE_CSR_CSCR);
+    put_reg(sc, LANCE_CSR_CSCR, (reg|(LANCE_CSR_CSCR_IENA | LANCE_CSR_CSCR_STRT))&~LANCE_CSR_CSCR_INIT);
+#if DEBUG & 9
+	reg=get_reg(sc,LANCE_CSR_CSCR);
+	db_printf("CSR after start = %4x\n",reg);
+#endif
+    cpd->active = 1; cpd->txbusy=0; cpd->txbusyh=0;
+    // delay is necessary for Vmware to get a tick !!!
+    CYGACC_CALL_IF_DELAY_US(50000);
+}
+
+//
+// This routine is called to perform special "control" opertions
+//
+static int
+lancepci_control(struct eth_drv_sc *sc, unsigned long key,
+               void *data, int data_length)
+{
+    cyg_uint8 *esa = (cyg_uint8 *)data;
+    int i, res;
+    cyg_uint16 reg;
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+
+    DEBUG_FUNCTION();
+
+#if DEBUG & 9
+	db_printf("Lancepci-control:entered\n");
+#endif
+    res = 0;                            // expect success
+    switch (key) {
+        case ETH_DRV_SET_MAC_ADDRESS:
+#if 9 & DEBUG
+            db_printf("PCNET - set ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                esa[0], esa[1], esa[2], esa[3], esa[4], esa[5] );
+#endif // DEBUG
+            for ( i = 0; i < sizeof(cpd->esa);  i++ )
+                cpd->esa[i] = esa[i];
+            for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
+                reg = cpd->esa[i] | (cpd->esa[i+1] << 8);
+                put_reg(sc, LANCE_CSR_PAR0+i/2, reg );
+            }
+            for (i = 0; i < 6; i++)	// in case of later restart
+                _SU8(cpd->init_table, LANCE_IB_PADR0+i) = cpd->esa[i];
+            break;
+#ifdef ETH_DRV_GET_MAC_ADDRESS
+        case ETH_DRV_GET_MAC_ADDRESS:
+            // Extract the MAC address that is in the chip, and tell the
+            // system about it.
+            for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
+                cyg_uint16 z = get_reg(sc, LANCE_CSR_PAR0+i/2 );
+                esa[i] =   (cyg_uint8)(0xff & z);
+                esa[i+1] = (cyg_uint8)(0xff & (z >> 8));
+            }
+            break;
+#endif
+#ifdef ETH_DRV_GET_IF_STATS_UD
+        case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE
+#endif
+        // drop through
+#ifdef ETH_DRV_GET_IF_STATS
+        case ETH_DRV_GET_IF_STATS:
+#endif
+
+#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD)
+        {
+            struct ether_drv_stats *p = (struct ether_drv_stats *)data;
+            // Chipset entry is no longer supported; RFC1573.
+            for ( i = 0; i < SNMP_CHIPSET_LEN; i++ )
+                p->snmp_chipset[i] = 0;
+
+            // This perhaps should be a config opt, so you can make up your own
+            // description, or supply it from the instantiation.
+            strcpy( p->description, "AMD LancePCI" );
+            // CYG_ASSERT( 48 > strlen(p->description), "Description too long" );
+
+            p->operational = 3;         // LINK UP
+            p->duplex = 2;              // 2 = SIMPLEX
+            p->speed = 10 * 1000000;
+
+#if FIXME
+#ifdef KEEP_STATISTICS
+            {
+                struct amd_lancepci_stats *ps = &(cpd->stats);
+
+                // Admit to it...
+                p->supports_dot3        = true;
+
+                p->tx_good              = ps->tx_good             ;
+                p->tx_max_collisions    = ps->tx_max_collisions   ;
+                p->tx_late_collisions   = ps->tx_late_collisions  ;
+                p->tx_underrun          = ps->tx_underrun         ;
+                p->tx_carrier_loss      = ps->tx_carrier_loss     ;
+                p->tx_deferred          = ps->tx_deferred         ;
+                p->tx_sqetesterrors     = ps->tx_sqetesterrors    ;
+                p->tx_single_collisions = ps->tx_single_collisions;
+                p->tx_mult_collisions   = ps->tx_mult_collisions  ;
+                p->tx_total_collisions  = ps->tx_total_collisions ;
+                p->rx_good              = ps->rx_good             ;
+                p->rx_crc_errors        = ps->rx_crc_errors       ;
+                p->rx_align_errors      = ps->rx_align_errors     ;
+                p->rx_resource_errors   = ps->rx_resource_errors  ;
+                p->rx_overrun_errors    = ps->rx_overrun_errors   ;
+                p->rx_collisions        = ps->rx_collisions       ;
+                p->rx_short_frames      = ps->rx_short_frames     ;
+                p->rx_too_long_frames   = ps->rx_too_long_frames  ;
+                p->rx_symbol_errors     = ps->rx_symbol_errors    ;
+
+                p->interrupts           = ps->interrupts          ;
+                p->rx_count             = ps->rx_count            ;
+                p->rx_deliver           = ps->rx_deliver          ;
+                p->rx_resource          = ps->rx_resource         ;
+                p->rx_restart           = ps->rx_restart          ;
+                p->tx_count             = ps->tx_count            ;
+                p->tx_complete          = ps->tx_complete         ;
+                p->tx_dropped           = ps->tx_dropped          ;
+            }
+#endif // KEEP_STATISTICS
+#endif // FIXME
+
+            p->tx_queue_len = 1;
+            break;
+        }
+#endif
+        default:
+            res = 1;
+            break;
+    }
+#if DEBUG & 9
+    db_printf("Lancepci-control:done\n");
+#endif
+    CYGACC_CALL_IF_DELAY_US(50000);	// let VMware get a tick
+    return res;
+}
+
+//
+// This routine is called to see if it is possible to send another packet.
+// It will return non-zero if a transmit is possible, zero otherwise.
+//
+static int
+lancepci_can_send(struct eth_drv_sc *sc)
+{
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+
+    DEBUG_FUNCTION();
+
+    return (0 == cpd->txbusy);
+}
+
+//
+// This routine is called to send data to the hardware.
+static void
+lancepci_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
+            int total_len, unsigned long key)
+{
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+    int i, len, plen, ring_entry;
+
+    cyg_uint8* sdata = NULL;
+    cyg_uint8 *d, *buf, *txd;
+    cyg_uint16 ints;
+    cyg_uint32 b;
+
+    DEBUG_FUNCTION();
+
+    INCR_STAT( tx_count );
+
+    cpd->txbusy = 1; cpd->txbusyh=1;
+    cpd->txkey = key;
+
+    // Find packet length
+    plen = 0;
+    for (i = 0;  i < sg_len;  i++)
+        plen += sg_list[i].len;
+
+    CYG_ASSERT( plen == total_len, "sg data length mismatch" );
+
+    // Get next TX descriptor
+    ring_entry = cpd->tx_ring_free;
+    do {
+        if (cpd->tx_ring_owned == cpd->tx_ring_cnt) {
+            // Is this a dead end? Probably is.
+#if DEBUG & 1
+            db_printf("%s: Allocation failed! Retrying...\n", __FUNCTION__ );
+#endif
+            continue;
+        }
+
+        cpd->tx_ring_free++;
+        cpd->tx_ring_owned++;
+        if (cpd->tx_ring_free == cpd->tx_ring_cnt)
+            cpd->tx_ring_free = 0;
+    } while (0);
+
+    txd = cpd->tx_ring + ring_entry*LANCE_TD_SIZE;
+    buf = cpd->tx_buffers + ring_entry*_BUF_SIZE;
+    CYG_ASSERT(0 == (_SU32(txd, LANCE_TD_PTR) & LANCE_TD_PTR_OWN),
+               "TX descriptor not free");
+
+#if DEBUG & 4
+    db_printf("#####Tx descriptor 0x%08x buffer 0x%08x\n",
+                txd, buf);
+#endif
+
+    // Put data into buffer
+    d = buf;
+    for (i = 0;  i < sg_len;  i++) {
+        sdata = (cyg_uint8 *)sg_list[i].buf;
+        len = sg_list[i].len;
+
+        CYG_ASSERT( sdata, "No sg data pointer here" );
+        while(len--)
+            *d++ = *sdata++;
+    }
+    CYG_ASSERT( sdata, "No sg data pointer outside" );
+
+#if DEBUG & 1
+    db_printf("CSCR %04x\n", get_reg(sc, LANCE_CSR_CSCR));
+#endif
+    _SU16(txd, LANCE_TD_LEN) = (-plen);
+    _SU16(txd, LANCE_TD_MISC) = 0;
+    HAL_PCI_CPU_TO_BUS(buf, (cyg_uint8 *)b);
+    _SU32(txd, LANCE_TD_PTR) = ((b & LANCE_TD_PTR_MASK)
+                                | LANCE_TD_PTR_OWN | LANCE_TD_PTR_STP | LANCE_TD_PTR_ENP);
+
+#if DEBUG & 1
+    db_printf("Last TX: LEN %04x MISC %04x PTR %08x\n",
+                _SU16(txd, LANCE_TD_LEN),
+                _SU16(txd, LANCE_TD_MISC),
+                _SU32(txd, LANCE_TD_PTR));
+#endif
+
+    // This delay seems to be necessary on some platforms
+    // (Malta 5kc for example).
+    // Why it is needed is not clear, but removing it or
+    // reducing it cause transmission failures in RedBoot (at least).
+    CYGACC_CALL_IF_DELAY_US(100);
+
+
+    // Set transmit demand
+    ints = get_reg(sc, LANCE_CSR_CSCR);
+    ints &= LANCE_CSR_CSCR_EV_MASK;
+    ints |= LANCE_CSR_CSCR_TDMD;
+    put_reg(sc, LANCE_CSR_CSCR, ints);
+
+#if DEBUG & 1
+    ints = get_reg(sc, LANCE_CSR_CSCR);
+    db_printf("%s:END: ints at TX: 0x%04x\n", __FUNCTION__, ints);
+#endif
+
+    // This is another mystery delay like the one above. This one is
+    // even stranger, since waiting here at the _end_ of the function
+    // should have no effect.
+    CYGACC_CALL_IF_DELAY_US(200);
+}
+
+static void
+lancepci_TxEvent(struct eth_drv_sc *sc, int stat)
+{
+     struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+    int success = 1;
+    cyg_uint8 *txd;
+    cyg_uint16 ints;
+    cyg_uint32 pkt_stat;
+
+    DEBUG_FUNCTION();
+
+    if (0 == cpd->tx_ring_owned) {
+#if DEBUG & 1
+        db_printf("%s: got TX completion when no outstanding packets\n", __FUNCTION__);
+#endif
+        return;
+    }
+
+    INCR_STAT( tx_complete );
+
+    txd = cpd->tx_ring + cpd->tx_ring_alloc*LANCE_TD_SIZE;
+    pkt_stat = _SU32(txd, LANCE_TD_PTR);
+    if (pkt_stat & LANCE_TD_PTR_OWN) {
+#if DEBUG & 1
+        db_printf("%s: got TX completion when buffer is still owned\n", __FUNCTION__);
+#endif
+        // first dirty ring entry not freed - wtf?
+    }
+
+    if (pkt_stat & LANCE_TD_PTR_ERR) {
+        // We had an error. Tell the stack.
+        success = 0;
+#if DEBUG & 1
+        db_printf("%s: TX failure, retrying...\n", __FUNCTION__);
+#endif
+    }
+
+    cpd->tx_ring_alloc++;
+    if (cpd->tx_ring_alloc == cpd->tx_ring_cnt)
+        cpd->tx_ring_alloc = 0;
+    cpd->tx_ring_owned--;
+
+#if FIXME
+#ifdef KEEP_STATISTICS
+    {
+        cyg_uint16 reg;
+
+        reg = get_reg( sc, LANCE_CSR_CSCR );
+
+        // Covering each bit in turn...
+        if ( reg & LANCE_STATUS_TX_UNRN   ) INCR_STAT( tx_underrun );
+        //if ( reg & LANCE_STATUS_LINK_OK ) INCR_STAT(  );
+        //if ( reg & LANCE_STATUS_CTR_ROL ) INCR_STAT(  );
+        //if ( reg & LANCE_STATUS_EXC_DEF ) INCR_STAT(  );
+        if ( reg & LANCE_STATUS_LOST_CARR ) INCR_STAT( tx_carrier_loss );
+        if ( reg & LANCE_STATUS_LATCOL    ) INCR_STAT( tx_late_collisions );
+        //if ( reg & LANCE_STATUS_WAKEUP  ) INCR_STAT(  );
+        if ( reg & LANCE_STATUS_TX_DEFR   ) INCR_STAT( tx_deferred );
+        //if ( reg & LANCE_STATUS_LTX_BRD ) INCR_STAT(  );
+        if ( reg & LANCE_STATUS_SQET      ) INCR_STAT( tx_sqetesterrors );
+        if ( reg & LANCE_STATUS_16COL     ) INCR_STAT( tx_max_collisions );
+        //if ( reg & LANCE_STATUS_LTX_MULT) INCR_STAT(  );
+        if ( reg & LANCE_STATUS_MUL_COL   ) INCR_STAT( tx_mult_collisions );
+        if ( reg & LANCE_STATUS_SNGL_COL  ) INCR_STAT( tx_single_collisions );
+        if ( reg & LANCE_STATUS_TX_SUC    ) INCR_STAT( tx_good );
+
+        cpd->stats.tx_total_collisions =
+            cpd->stats.tx_late_collisions +
+            cpd->stats.tx_max_collisions +
+            cpd->stats.tx_mult_collisions +
+            cpd->stats.tx_single_collisions;
+
+        // We do not need to look in the Counter Register (LANCE_COUNTER)
+        // because it just mimics the info we already have above.
+    }
+#endif // KEEP_STATISTICS
+#endif // FIXME
+
+    // Ack the TX int which clears the packet from the TX completion
+    // queue.
+    ints = get_reg(sc, LANCE_CSR_CSCR);
+    ints |= LANCE_CSR_CSCR_TINT;
+    put_reg(sc, LANCE_CSR_CSCR, ints);
+
+#if DEBUG & 4
+    db_printf("#####Tx packet freed 0x%08x\n", txd );
+#endif
+
+    if ( cpd->txbusy ) {
+        cpd->txbusy = 0;
+        (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, success);
+    }
+}
+
+
+//
+// This function is called when a packet has been received.  Its job is
+// to prepare to unload the packet from the hardware.  Once the length of
+// the packet is known, the upper layer of the driver can be told.  When
+// the upper layer is ready to unload the packet, the internal function
+// 'lancepci_recv' will be called to actually fetch it from the hardware.
+//
+static void
+lancepci_RxEvent(struct eth_drv_sc *sc)
+{
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+    cyg_uint8 *rxd;
+    cyg_uint32 rstat;
+    cyg_uint16 ints, len;
+
+    DEBUG_FUNCTION();
+
+    ints = get_reg(sc, LANCE_CSR_CSCR);
+#if DEBUG & 1
+    db_printf("RxEvent - CSR: 0x%04x\n", ints);
+#endif
+
+    while (1) {
+        // Get state of next (supposedly) full ring entry
+        cpd->rxpacket = cpd->rx_ring_next;
+        rxd = cpd->rx_ring + cpd->rxpacket*LANCE_RD_SIZE;
+        rstat = _SU32(rxd, LANCE_RD_PTR);
+
+        // Keep going until we hit an entry that is owned by the
+        // controller.
+        if (rstat & LANCE_RD_PTR_OWN) {
+#if DEBUG & 1
+            int i;
+            for (i = 0; i < cpd->rx_ring_cnt; i++) {
+                rxd = cpd->rx_ring + i*LANCE_RD_SIZE;
+                rstat = _SU32(rxd, LANCE_RD_PTR);
+
+                if (!(rstat & LANCE_RD_PTR_OWN)) {
+                    int i;
+                    cyg_uint32 rstat;
+                    cyg_uint16 mlen, blen;
+                    cyg_uint8* rxd;
+
+                    db_printf("%s: Inconsistent RX state\n", __FUNCTION__);
+                    for (i = 0; i < cpd->rx_ring_cnt; i++) {
+                        rxd = cpd->rx_ring + i*LANCE_RD_SIZE;
+
+                        rstat = _SU32(rxd, LANCE_RD_PTR);
+                        blen = _SU16(rxd, LANCE_RD_BLEN);
+                        mlen = _SU16(rxd, LANCE_RD_MLEN);
+                        db_printf(" %02d: 0x%08x:0x%04x:0x%04x\n", i, rstat, blen, mlen);
+                    }
+                }
+            }
+#endif
+            break;
+        }
+
+#if DEBUG & 4
+        db_printf("#####Rx packet at index %d\n", cpd->rxpacket);
+#endif
+
+        // Increment counts
+        INCR_STAT( rx_count );
+        cpd->rx_ring_next++;
+        if (cpd->rx_ring_next == cpd->rx_ring_cnt) cpd->rx_ring_next = 0;
+
+        len = _SU16(rxd, LANCE_RD_MLEN);
+
+#ifdef KEEP_STATISTICS
+        //if ( rstat & LANCE_RD_PTR_FRAM ) INCR_STAT( rx_frame_errors );
+        //if ( rstat & LANCE_RD_PTR_OFLO ) INCR_STAT(  );
+        if ( rstat & LANCE_RD_PTR_CRC ) INCR_STAT( rx_crc_errors );
+        //if ( rstat & LANCE_RD_PTR_BUFF ) INCR_STAT(  );
+#endif // KEEP_STATISTICS
+
+        if (0 == (rstat & LANCE_RD_PTR_ERR)) {
+            // It's OK
+            INCR_STAT( rx_good );
+
+#if DEBUG & 1
+            db_printf("RxEvent good rx - stat: 0x%08x, len: 0x%04x\n", rstat, len);
+#endif
+            // Check for bogusly short packets; can happen in promisc
+            // mode: Asserted against and checked by upper layer
+            // driver.
+#ifdef CYGPKG_NET
+            if ( len > sizeof( struct ether_header ) )
+                // then it is acceptable; offer the data to the network stack
+#endif
+                (sc->funs->eth_drv->recv)(sc, len);
+        } else {
+            // Not OK for one reason or another...
+#if DEBUG & 1
+            db_printf("RxEvent - No RX bit: stat: 0x%08x, len: 0x%04x\n",
+                        rstat, len);
+#endif
+        }
+
+        // Free packet (clear all status flags, and set OWN)
+        _SU32(rxd, LANCE_RD_PTR) &= LANCE_RD_PTR_MASK;
+        _SU32(rxd, LANCE_RD_PTR) |= LANCE_RD_PTR_OWN;
+    }
+
+    // Ack RX interrupt set
+    ints = get_reg(sc, LANCE_CSR_CSCR);
+    ints &= LANCE_CSR_CSCR_EV_MASK;
+    ints |= LANCE_CSR_CSCR_RINT;
+    put_reg(sc, LANCE_CSR_CSCR, ints);
+}
+
+//
+// This function is called as a result of the "eth_drv_recv()" call above.
+// Its job is to actually fetch data for a packet from the hardware once
+// memory buffers have been allocated for the packet.  Note that the buffers
+// may come in pieces, using a scatter-gather list.  This allows for more
+// efficient processing in the upper layers of the stack.
+//
+static void
+lancepci_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
+{
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+    int i, mlen=0, plen;
+    cyg_uint8 *data, *rxd, *buf;
+
+    DEBUG_FUNCTION();
+
+    rxd = cpd->rx_ring + cpd->rxpacket*LANCE_RD_SIZE;
+    buf = cpd->rx_buffers + cpd->rxpacket*_BUF_SIZE;
+
+    INCR_STAT( rx_deliver );
+
+    plen = _SU16(rxd, LANCE_RD_MLEN);
+
+    for (i = 0;  i < sg_len;  i++) {
+        data = (cyg_uint8*)sg_list[i].buf;
+        mlen = sg_list[i].len;
+
+#if DEBUG & 1
+        db_printf("%s : mlen %x, plen %x\n", __FUNCTION__, mlen, plen);
+#endif
+        if (data) {
+            while (mlen > 0) {
+                *data++ = *buf++;
+                mlen--;
+                plen--;
+            }
+        }
+    }
+}
+
+static void
+lancepci_poll(struct eth_drv_sc *sc)
+{
+    cyg_uint16 event;
+    struct lancepci_priv_data *cpd =
+        (struct lancepci_priv_data *)sc->driver_private;
+
+//  DEBUG_FUNCTION();
+
+    while (1) {
+        // Get the (unmasked) requests
+	if (cpd->event) {
+	    event=cpd->event;
+	    cpd->event=0;
+	}
+	else
+            event = get_reg(sc, LANCE_CSR_CSCR);
+        if (!((LANCE_CSR_CSCR_ERR|LANCE_CSR_CSCR_INTR) & event))
+            break;
+
+        if (event & LANCE_CSR_CSCR_RINT) {
+            lancepci_RxEvent(sc);
+        }
+        else if (event & LANCE_CSR_CSCR_TINT) {
+	    cpd->txbusyh=0;		// again , for polled mode
+            lancepci_TxEvent(sc, event);
+        }
+        else if (event & LANCE_CSR_CSCR_MISS) {
+#if DEBUG & 1
+            int i;
+            cyg_uint32 rstat;
+            cyg_uint16 mlen, blen;
+            cyg_uint8* rxd;
+            struct lancepci_priv_data *cpd =
+                (struct lancepci_priv_data *)sc->driver_private;
+
+            db_printf("%s: Ran out of RX buffers (%04x)\n", __FUNCTION__, event);
+            for (i = 0; i < cpd->rx_ring_cnt; i++) {
+                rxd = cpd->rx_ring + i*LANCE_TD_SIZE;
+
+                rstat = _SU32(rxd, LANCE_RD_PTR);
+                blen = _SU16(rxd, LANCE_RD_BLEN);
+                mlen = _SU16(rxd, LANCE_RD_MLEN);
+                db_printf(" %02d: 0x%08x:0x%04x:0x%04x\n", i, rstat, blen, mlen);
+            }
+#endif
+            event &= LANCE_CSR_CSCR_EV_MASK;
+            event |= LANCE_CSR_CSCR_MISS;
+            put_reg(sc, LANCE_CSR_CSCR, event);
+        }
+        else {
+#if DEBUG & 1
+            db_printf("%s: Unknown interrupt: 0x%04x\n", __FUNCTION__, event);
+#endif
+            put_reg(sc, LANCE_CSR_CSCR, event);
+        }
+    }
+}
+
+// EOF if_lancepci.c