bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: realtek 8139


From: Alfred M. Szmidt
Subject: Re: realtek 8139
Date: Sat, 21 Feb 2004 19:33:16 +0100 (MET)

Could you try the following hack?  In true Hurd hacker glory, it is
completely untested. :-)

Index: i386/linux/Makefile.in
--- i386/linux/Makefile.in
+++ i386/linux/Makefile.in
@@ -33,7 +33,7 @@
 # Files for driver support.
 linux-c-files = version.c softirq.c delay.c dma.c resource.c printk.c \
                bios32.c irq.c ctype.c vsprintf.c main.c misc.c sched.c \
-               kmem.c block.c setup.c
+               kmem.c block.c setup.c  pci-scan.c
 
 # `dev' directory contains modified and glue source files.
 vpath %.c $(linuxsrcdir)/dev/kernel
Index: linux/src/drivers/net/kern_compat.h
--- /dev/null
+++ linux/src/drivers/net/kern_compat.h
@@ -0,0 +1,284 @@
+#ifndef _KERN_COMPAT_H
+#define _KERN_COMPAT_H
+/* kern_compat.h: Linux PCI network adapter backward compatibility code. */
+/*
+       $Revision: 1.17 $ $Date: 2002/11/17 17:37:00 $
+
+       Kernel compatibility defines.
+       This file provides macros to mask the difference between kernel 
versions.
+       It is designed primarily to allow device drivers to be written so that
+       they work with a range of kernel versions.
+
+       Written 1999-2002 Donald Becker, Scyld Computing Corporation
+       This software may be used and distributed according to the terms
+       of the GNU General Public License (GPL), incorporated herein by
+       reference.  Drivers interacting with these functions are derivative
+       works and thus are covered the GPL.  They must include an explicit
+       GPL notice.
+
+       This code also provides inline scan and activate functions for PCI 
network
+       interfaces.  It has an interface identical to pci-scan.c, but is
+       intended as an include file to simplify using updated drivers with older
+       kernel versions.
+       This code version matches pci-scan.c:v0.05 9/16/99
+
+       The author may be reached as becker@scyld.com, or
+       Donald Becker
+       Scyld Computing Corporation
+       410 Severn Ave., Suite 210
+       Annapolis MD 21403
+
+       Other contributers:
+       <none>
+*/
+
+/* We try to use defined values to decide when an interface has changed or
+   added features, but we must have the kernel version number for a few. */
+#if ! defined(LINUX_VERSION_CODE)  ||  (LINUX_VERSION_CODE < 0x10000)
+#include <linux/version.h>
+#endif
+/* Older kernel versions didn't include modversions automatically. */
+#if LINUX_VERSION_CODE < 0x20300  &&  defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+/* There was no support for PCI address space mapping in 2.0, but the
+   Alpha needed it.  See the 2.2 documentation. */
+#if LINUX_VERSION_CODE < 0x20100  &&  ! defined(__alpha__)
+#define ioremap(a,b)\
+    (((unsigned long)(a) >= 0x100000) ? vremap(a,b) : (void*)(a))
+#define iounmap(v)\
+    do { if ((unsigned long)(v) >= 0x100000) vfree(v);} while (0)
+#endif
+
+/* Support for adding info about the purpose of and parameters for kernel
+   modules was added in 2.1. */
+#if LINUX_VERSION_CODE < 0x20115
+#define MODULE_AUTHOR(name)  extern int nonesuch
+#define MODULE_DESCRIPTION(string)  extern int nonesuch
+#define MODULE_PARM(varname, typestring)  extern int nonesuch
+#define MODULE_PARM_DESC(var,desc) extern int nonesuch
+#endif
+#if !defined(MODULE_LICENSE)
+#define MODULE_LICENSE(license)        \
+static const char __module_license[] __attribute__((section(".modinfo"))) =   \
+"license=" license
+#endif
+#if !defined(MODULE_PARM_DESC)
+#define MODULE_PARM_DESC(var,desc)             \
+const char __module_parm_desc_##var[]          \
+__attribute__((section(".modinfo"))) =         \
+"parm_desc_" __MODULE_STRING(var) "=" desc
+#endif
+
+/* SMP and better multiarchitecture support were added.
+   Using an older kernel means we assume a little-endian uniprocessor.
+*/
+#if LINUX_VERSION_CODE < 0x20123
+#define hard_smp_processor_id() smp_processor_id()
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#define cpu_to_le16(val) (val)
+#define cpu_to_le32(val) (val)
+#define le16_to_cpu(val) (val)
+#define le16_to_cpus(val)              /* In-place conversion. */
+#define le32_to_cpu(val) (val)
+#define cpu_to_be16(val) ((((val) & 0xff) << 8) +  (((val) >> 8) & 0xff))
+#define cpu_to_be32(val) ((cpu_to_be16(val) << 16) + cpu_to_be16((val) >> 16))
+typedef long spinlock_t;
+#define SPIN_LOCK_UNLOCKED 0
+#define spin_lock(lock)
+#define spin_unlock(lock)
+#define spin_lock_irqsave(lock, flags) do {save_flags(flags); cli();} while(0)
+#define spin_unlock_irqrestore(lock, flags) restore_flags(flags)
+#endif
+
+#if LINUX_VERSION_CODE <= 0x20139
+#define        net_device_stats enet_statistics
+#else
+#define NETSTATS_VER2
+#endif
+
+/* These are used by the netdrivers to report values from the
+   MII (Media Indpendent Interface) management registers.
+*/
+#ifndef SIOCGMIIPHY
+#define SIOCGMIIPHY (SIOCDEVPRIVATE)           /* Get the PHY in use. */
+#define SIOCGMIIREG (SIOCDEVPRIVATE+1)                 /* Read a PHY register. 
*/
+#define SIOCSMIIREG (SIOCDEVPRIVATE+2)                 /* Write a PHY 
register. */
+#endif
+#ifndef SIOCGPARAMS
+#define SIOCGPARAMS (SIOCDEVPRIVATE+3)                 /* Read operational 
parameters. */
+#define SIOCSPARAMS (SIOCDEVPRIVATE+4)                 /* Set operational 
parameters. */
+#endif
+
+#if !defined(HAVE_NETIF_MSG)
+enum {
+       NETIF_MSG_DRV           = 0x0001,
+       NETIF_MSG_PROBE         = 0x0002,
+       NETIF_MSG_LINK          = 0x0004,
+       NETIF_MSG_TIMER         = 0x0008,
+       NETIF_MSG_IFDOWN        = 0x0010,
+       NETIF_MSG_IFUP          = 0x0020,
+       NETIF_MSG_RX_ERR        = 0x0040,
+       NETIF_MSG_TX_ERR        = 0x0080,
+       NETIF_MSG_TX_QUEUED     = 0x0100,
+       NETIF_MSG_INTR          = 0x0200,
+       NETIF_MSG_TX_DONE       = 0x0400,
+       NETIF_MSG_RX_STATUS     = 0x0800,
+       NETIF_MSG_PKTDATA       = 0x1000,
+       /* 2000 is reserved. */
+       NETIF_MSG_WOL           = 0x4000,
+       NETIF_MSG_MISC          = 0x8000,
+       NETIF_MSG_RXFILTER      = 0x10000,
+};
+#define NETIF_MSG_MAX 0x10000
+#endif
+
+#if !defined(NETIF_MSG_MAX) || NETIF_MSG_MAX < 0x8000
+#define NETIF_MSG_MISC 0x8000
+#endif
+#if !defined(NETIF_MSG_MAX) || NETIF_MSG_MAX < 0x10000
+#define NETIF_MSG_RXFILTER 0x10000
+#endif
+
+#if LINUX_VERSION_CODE < 0x20155
+#include <linux/bios32.h>
+#define PCI_SUPPORT_VER1
+/* A minimal version of the 2.2.* PCI support that handles configuration
+   space access.
+   Drivers that actually use pci_dev fields must do explicit compatibility.
+   Note that the struct pci_dev * "pointer" is actually a byte mapped integer!
+*/
+#if LINUX_VERSION_CODE < 0x20014
+struct pci_dev { int not_used; };
+#endif
+
+#define pci_find_slot(bus, devfn) (struct pci_dev*)((bus<<8) | devfn | 0xf0000)
+#define bus_number(pci_dev) ((((int)(pci_dev))>>8) & 0xff)
+#define devfn_number(pci_dev) (((int)(pci_dev)) & 0xff)
+#define pci_bus_number(pci_dev) ((((int)(pci_dev))>>8) & 0xff)
+#define pci_devfn(pci_dev) (((int)(pci_dev)) & 0xff)
+
+#ifndef CONFIG_PCI
+extern inline int pci_present(void) { return 0; }
+#else
+#define pci_present pcibios_present
+#endif
+
+#define pci_read_config_byte(pdev, where, valp)\
+       pcibios_read_config_byte(bus_number(pdev), devfn_number(pdev), where, 
valp)
+#define pci_read_config_word(pdev, where, valp)\
+       pcibios_read_config_word(bus_number(pdev), devfn_number(pdev), where, 
valp)
+#define pci_read_config_dword(pdev, where, valp)\
+       pcibios_read_config_dword(bus_number(pdev), devfn_number(pdev), where, 
valp)
+#define pci_write_config_byte(pdev, where, val)\
+       pcibios_write_config_byte(bus_number(pdev), devfn_number(pdev), where, 
val)
+#define pci_write_config_word(pdev, where, val)\
+       pcibios_write_config_word(bus_number(pdev), devfn_number(pdev), where, 
val)
+#define pci_write_config_dword(pdev, where, val)\
+       pcibios_write_config_dword(bus_number(pdev), devfn_number(pdev), where, 
val)
+#else
+#define PCI_SUPPORT_VER2
+#define pci_bus_number(pci_dev) ((pci_dev)->bus->number)
+#define pci_devfn(pci_dev) ((pci_dev)->devfn)
+#endif
+
+/* The arg count changed, but function name did not.
+   We cover that bad choice by defining a new name.
+*/
+#if LINUX_VERSION_CODE < 0x20159
+#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE)
+#define dev_free_skb_irq(skb) dev_kfree_skb(skb, FREE_WRITE)
+#elif LINUX_VERSION_CODE < 0x20400
+#define dev_free_skb(skb) dev_kfree_skb(skb)
+#define dev_free_skb_irq(skb) dev_kfree_skb(skb)
+#else
+#define dev_free_skb(skb) dev_kfree_skb(skb)
+#define dev_free_skb_irq(skb) dev_kfree_skb_irq(skb)
+#endif
+
+/* Added at the suggestion of Jes Sorensen. */
+#if LINUX_VERSION_CODE > 0x20153
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(__arginit) __arginit
+#endif
+
+/* The old 'struct device' used a too-generic name. */
+#if LINUX_VERSION_CODE < 0x2030d
+#define net_device device
+#endif
+
+/* More changes for the 2.4 kernel, some in the zillion 2.3.99 releases. */
+#if LINUX_VERSION_CODE < 0x20363
+#define DECLARE_MUTEX(name) struct semaphore (name) = MUTEX;
+#define down_write(semaphore_p) down(semaphore_p)
+#define down_read(semaphore_p) down(semaphore_p)
+#define up_write(semaphore_p) up(semaphore_p)
+#define up_read(semaphore_p) up(semaphore_p)
+#define get_free_page get_zeroed_page
+/* Note that the kernel version has a broken time_before()! */
+#define time_after(a,b) ((long)(b) - (long)(a) < 0)
+#define time_before(a,b) ((long)(a) - (long)(b) < 0)
+#endif
+
+/* The 2.2 kernels added the start of capability-based security for operations
+   that formerally could only be done by root.
+*/
+#if ! defined(CAP_NET_ADMIN)
+#define capable(CAP_XXX) (suser())
+#endif
+
+#if ! defined(HAVE_NETIF_QUEUE)
+#define netif_wake_queue(dev)   do { clear_bit( 0, (void*)&(dev)->tbusy); 
mark_bh(NET_BH); } while (0)
+#define netif_start_tx_queue(dev) do { (dev)->tbusy = 0; dev->start = 1; } 
while (0)
+#define netif_stop_tx_queue(dev) do { (dev)->tbusy = 1; dev->start = 0; } 
while (0)
+#define netif_queue_paused(dev) ((dev)->tbusy != 0)
+/* Splitting these lines exposes a bug in some preprocessors. */
+#define netif_pause_tx_queue(dev) (test_and_set_bit( 0, (void*)&(dev)->tbusy))
+#define netif_unpause_tx_queue(dev) do { clear_bit( 0, (void*)&(dev)->tbusy); 
} while (0)
+#define netif_resume_tx_queue(dev) do { clear_bit( 0, (void*)&(dev)->tbusy); 
mark_bh(NET_BH); } while (0)
+
+#define netif_running(dev) ((dev)->start != 0)
+#define netif_device_attach(dev) do {; } while (0)
+#define netif_device_detach(dev) do {; } while (0)
+#define netif_device_present(dev) (1)
+#define netif_set_tx_timeout(dev, func, deltajiffs)   do {; } while (0)
+#define netif_link_down(dev)  (dev)->flags &= ~IFF_RUNNING
+#define netif_link_up(dev)  (dev)->flags |= IFF_RUNNING
+
+#else
+
+#define netif_start_tx_queue(dev) netif_start_queue(dev)
+#define netif_stop_tx_queue(dev) netif_stop_queue(dev)
+#define netif_queue_paused(dev) netif_queue_stopped(dev)
+#define netif_resume_tx_queue(dev) netif_wake_queue(dev)
+/* Only used in transmit path.  No function in 2.4. */
+#define netif_pause_tx_queue(dev)  0
+#define netif_unpause_tx_queue(dev) do {; } while (0)
+
+#ifdef __LINK_STATE_NOCARRIER
+#define netif_link_down(dev)  netif_carrier_off(dev)
+#define netif_link_up(dev)  netif_carrier_on(dev)
+#else
+#define netif_link_down(dev)  (dev)->flags &= ~IFF_RUNNING
+#define netif_link_up(dev)  (dev)->flags |= IFF_RUNNING
+#endif
+
+#endif
+#ifndef PCI_DMA_BUS_IS_PHYS
+#define pci_dma_sync_single(pci_dev, base_addr, extent, tofrom) do {; } while 
(0)
+#define pci_map_single(pci_dev, base_addr, extent, dir) virt_to_bus(base_addr)
+#define pci_unmap_single(pci_dev, base_addr, extent, dir) do {; } while (0)
+#endif
+
+#endif
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
Index: linux/src/drivers/net/pci-scan.c
--- /dev/null
+++ linux/src/drivers/net/pci-scan.c
@@ -0,0 +1,660 @@
+/* pci-scan.c: Linux PCI network adapter support code. */
+/*
+       Originally written 1999-2002 by Donald Becker.
+
+       This software may be used and distributed according to the terms
+       of the GNU General Public License (GPL), incorporated herein by
+       reference.  Drivers interacting with these functions are derivative
+       works and thus are covered the GPL.  They must include an explicit
+       GPL notice.
+
+       This code provides common scan and activate functions for PCI network
+       interfaces.
+
+       The author may be reached as becker@scyld.com, or
+       Donald Becker
+       Scyld Computing Corporation
+       410 Severn Ave., Suite 210
+       Annapolis MD 21403
+
+       Other contributers:
+*/
+static const char version[] =
+"pci-scan.c:v1.11 8/31/2002  Donald Becker <becker@scyld.com>"
+" http://www.scyld.com/linux/drivers.html\n";;
+
+/* A few user-configurable values that may be modified when a module. */
+
+static int msg_level = 1;              /* 1 normal messages, 0 quiet .. 7 
verbose. */
+static int min_pci_latency = 32;
+
+#if ! defined(__KERNEL__)
+#define __KERNEL__ 1
+#endif
+#if !defined(__OPTIMIZE__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error You must compile this driver with the proper options, including "-O".
+#endif
+
+#if defined(MODULE) && ! defined(EXPORT_SYMTAB)
+#define EXPORT_SYMTAB
+#endif
+
+#include <linux/config.h>
+#if defined(CONFIG_SMP) && ! defined(__SMP__)
+#define __SMP__
+#endif
+#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#include <linux/version.h>
+#define MOD_INC_USE_COUNT       do { } while (0)
+#define MOD_DEC_USE_COUNT       do { } while (0)
+/* #include <linux/modversions.h> */
+/* #if LINUX_VERSION_CODE < 0x20500  &&  defined(MODVERSIONS) */
+/* /\* Another interface semantics screw-up. *\/ */
+/* #include <linux/module.h> */
+/* #include <linux/modversions.h> */
+/* #else */
+/* #include <linux/modversions.h> */
+/* #include <linux/module.h> */
+/* #endif */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#if LINUX_VERSION_CODE >= 0x20300
+/* Bogus change in the middle of a "stable" kernel series.
+   Also, in 2.4.7+ slab must come before interrupt.h to avoid breakage. */
+#include <linux/slab.h>
+#else
+#include <linux/malloc.h>
+#endif
+#include <asm/io.h>
+#include "pci-scan.h"
+#include "kern_compat.h"
+#if defined(CONFIG_APM)  &&  LINUX_VERSION_CODE < 0x20400 
+#include <linux/apm_bios.h>
+#endif
+#ifdef CONFIG_PM
+/* New in 2.4 kernels, pointlessly incompatible with earlier APM. */
+#include <linux/pm.h>
+#endif
+
+#if (LINUX_VERSION_CODE >= 0x20100) && defined(MODULE)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#if (LINUX_VERSION_CODE < 0x20100)
+#define PCI_CAPABILITY_LIST    0x34    /* Offset of first capability list 
entry */
+#define PCI_STATUS_CAP_LIST    0x10    /* Support Capability List */
+#define PCI_CAP_ID_PM          0x01    /* Power Management */
+#endif
+
+int (*register_hotswap_hook)(struct drv_id_info *did);
+void (*unregister_hotswap_hook)(struct drv_id_info *did);
+
+#if LINUX_VERSION_CODE > 0x20118  &&  defined(MODULE)
+MODULE_LICENSE("GPL");
+MODULE_PARM(msg_level, "i");
+MODULE_PARM(min_pci_latency, "i");
+MODULE_PARM_DESC(msg_level, "Enable additional status messages (0-7)");
+MODULE_PARM_DESC(min_pci_latency,
+                                "Minimum value for the PCI Latency Timer 
settings");
+#if defined(EXPORT_SYMTAB)
+EXPORT_SYMBOL_NOVERS(pci_drv_register);
+EXPORT_SYMBOL_NOVERS(pci_drv_unregister);
+EXPORT_SYMBOL_NOVERS(acpi_wake);
+EXPORT_SYMBOL_NOVERS(acpi_set_pwr_state);
+EXPORT_SYMBOL_NOVERS(register_hotswap_hook);
+EXPORT_SYMBOL_NOVERS(unregister_hotswap_hook);
+#endif
+#endif
+
+/* List of registered drivers. */
+static struct drv_id_info *drv_list;
+/* List of detected PCI devices, for APM events. */
+static struct dev_info {
+       struct dev_info *next;
+       void *dev;
+       struct drv_id_info *drv_id;
+       int flags;
+} *dev_list;
+
+/*
+  This code is not intended to support every configuration.
+  It is intended to minimize duplicated code by providing the functions
+  needed in almost every PCI driver.
+
+  The "no kitchen sink" policy:
+  Additional features and code will be added to this module only if more
+  than half of the drivers for common hardware would benefit from the feature.
+*/
+
+/*
+  Ideally we would detect and number all cards of a type (e.g. network) in
+  PCI slot order.
+  But that does not work with hot-swap card, CardBus cards and added drivers.
+  So instead we detect just the each chip table in slot order.
+
+  This routine takes a PCI ID table, scans the PCI bus, and calls the
+  associated attach/probe1 routine with the hardware already activated and
+  single I/O or memory address already mapped.
+
+  This routine will later be supplemented with CardBus and hot-swap PCI
+  support using the same table.  Thus the pci_chip_tbl[] should not be
+  marked as __initdata.
+*/
+
+#if LINUX_VERSION_CODE >= 0x20200
+/* Grrrr.. complex abstaction layers with negative benefit. */
+int pci_drv_register(struct drv_id_info *drv_id, void *initial_device)
+{
+       int chip_idx, cards_found = 0;
+       struct pci_dev *pdev = NULL;
+       struct pci_id_info *pci_tbl = drv_id->pci_dev_tbl;
+       struct drv_id_info *drv;
+       void *newdev;
+
+
+       /* Ignore a double-register attempt. */
+       for (drv = drv_list; drv; drv = drv->next)
+               if (drv == drv_id)
+                       return -EBUSY;
+
+       while ((pdev = pci_find_class(drv_id->pci_class, pdev)) != 0) {
+               u32 pci_id, pci_subsys_id, pci_class_rev;
+               u16 pci_command, new_command;
+               int pci_flags;
+               long pciaddr;                   /* Bus address. */
+               long ioaddr;                    /* Mapped address for this 
processor. */
+
+               pci_read_config_dword(pdev, PCI_VENDOR_ID, &pci_id);
+               /* Offset 0x2c is PCI_SUBSYSTEM_ID aka PCI_SUBSYSTEM_VENDOR_ID. 
*/
+               pci_read_config_dword(pdev, 0x2c, &pci_subsys_id);
+               pci_read_config_dword(pdev, PCI_REVISION_ID, &pci_class_rev);
+
+               if (msg_level > 3)
+                       printk(KERN_DEBUG "PCI ID %8.8x subsystem ID is 
%8.8x.\n",
+                                  pci_id, pci_subsys_id);
+               for (chip_idx = 0; pci_tbl[chip_idx].name; chip_idx++) {
+                       struct pci_id_info *chip = &pci_tbl[chip_idx];
+                       if ((pci_id & chip->id.pci_mask) == chip->id.pci
+                               && (pci_subsys_id&chip->id.subsystem_mask) == 
chip->id.subsystem
+                               && (pci_class_rev&chip->id.revision_mask) == 
chip->id.revision)
+                               break;
+               }
+               if (pci_tbl[chip_idx].name == 0)                /* Compiled 
out! */
+                       continue;
+
+               pci_flags = pci_tbl[chip_idx].pci_flags;
+#if LINUX_VERSION_CODE >= 0x2030C
+               /* Wow. A oversized, hard-to-use abstraction. Bogus. */
+               pciaddr = pdev->resource[(pci_flags >> 4) & 7].start;
+#else
+               pciaddr = pdev->base_address[(pci_flags >> 4) & 7];
+#if defined(__alpha__)                 /* Really any machine with 64 bit 
addressing. */
+               if (pci_flags & PCI_ADDR_64BITS)
+                       pciaddr |= 
((long)pdev->base_address[((pci_flags>>4)&7)+ 1]) << 32;
+#endif
+#endif
+               if (msg_level > 2)
+                       printk(KERN_INFO "Found %s at PCI address %#lx, mapped 
IRQ %d.\n",
+                                  pci_tbl[chip_idx].name, pciaddr, pdev->irq);
+
+               if ( ! (pci_flags & PCI_UNUSED_IRQ)  &&
+                        (pdev->irq == 0 || pdev->irq == 255)) {
+                       if (pdev->bus->number == 32)    /* Broken CardBus 
activation. */
+                               printk(KERN_WARNING "Resources for CardBus 
device '%s' have"
+                                          " not been allocated.\n"
+                                          KERN_WARNING "Activation has been 
delayed.\n",
+                                          pci_tbl[chip_idx].name);
+                       else
+                               printk(KERN_WARNING "PCI device '%s' was not 
assigned an "
+                                          "IRQ.\n"
+                                          KERN_WARNING "It will not be 
activated.\n",
+                                  pci_tbl[chip_idx].name);
+                       continue;
+               }
+               if ((pci_flags & PCI_BASE_ADDRESS_SPACE_IO)) {
+                       ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK;
+                       if (check_region(ioaddr, pci_tbl[chip_idx].io_size))
+                               continue;
+               } else if ((ioaddr = (long)ioremap(pciaddr & 
PCI_BASE_ADDRESS_MEM_MASK,
+                                                                               
   pci_tbl[chip_idx].io_size)) == 0) {
+                       printk(KERN_INFO "Failed to map PCI address %#lx for 
device "
+                                  "'%s'.\n", pciaddr, pci_tbl[chip_idx].name);
+                       continue;
+               }
+               if ( ! (pci_flags & PCI_NO_ACPI_WAKE))
+                       acpi_wake(pdev);
+               pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+               new_command = pci_command | (pci_flags & 7);
+               if (pci_command != new_command) {
+                       printk(KERN_INFO "  The PCI BIOS has not enabled the"
+                                  " device at %d/%d!  Updating PCI command 
%4.4x->%4.4x.\n",
+                                  pdev->bus->number, pdev->devfn, pci_command, 
new_command);
+                       pci_write_config_word(pdev, PCI_COMMAND, new_command);
+               }
+
+               newdev = drv_id->probe1(pdev, initial_device,
+                                                               ioaddr, 
pdev->irq, chip_idx, cards_found);
+               if (newdev == NULL)
+                       continue;
+               initial_device = 0;
+               cards_found++;
+               if (pci_flags & PCI_COMMAND_MASTER) {
+                       pci_set_master(pdev);
+                       if ( ! (pci_flags & PCI_NO_MIN_LATENCY)) {
+                               u8 pci_latency;
+                               pci_read_config_byte(pdev, PCI_LATENCY_TIMER, 
&pci_latency);
+                               if (pci_latency < min_pci_latency) {
+                                       printk(KERN_INFO "  PCI latency timer 
(CFLT) is "
+                                                  "unreasonably low at %d.  
Setting to %d clocks.\n",
+                                                  pci_latency, 
min_pci_latency);
+                                       pci_write_config_byte(pdev, 
PCI_LATENCY_TIMER,
+                                                                               
  min_pci_latency);
+                               }
+                       }
+               }
+               {
+                       struct dev_info *devp =
+                               kmalloc(sizeof(struct dev_info), GFP_KERNEL);
+                       if (devp == 0)
+                               continue;
+                       devp->next = dev_list;
+                       devp->dev = newdev;
+                       devp->drv_id = drv_id;
+                       dev_list = devp;
+               }
+       }
+
+       if (((drv_id->flags & PCI_HOTSWAP)
+                && register_hotswap_hook && (*register_hotswap_hook)(drv_id) 
== 0)
+               || cards_found) {
+               MOD_INC_USE_COUNT;
+               drv_id->next = drv_list;
+               drv_list = drv_id;
+               return 0;
+       } else
+               return -ENODEV;
+}
+#else
+int pci_drv_register(struct drv_id_info *drv_id, void *initial_device)
+{
+       int pci_index, cards_found = 0;
+       unsigned char pci_bus, pci_device_fn;
+       struct pci_dev *pdev;
+       struct pci_id_info *pci_tbl = drv_id->pci_dev_tbl;
+       void *newdev;
+
+       if ( ! pcibios_present())
+               return -ENODEV;
+
+       for (pci_index = 0; pci_index < 0xff; pci_index++) {
+               u32 pci_id, subsys_id, pci_class_rev;
+               u16 pci_command, new_command;
+               int chip_idx, irq, pci_flags;
+               long pciaddr;
+               long ioaddr;
+               u32 pci_busaddr;
+               u8 pci_irq_line;
+
+               if (pcibios_find_class (drv_id->pci_class, pci_index,
+                                                               &pci_bus, 
&pci_device_fn)
+                       != PCIBIOS_SUCCESSFUL)
+                       break;
+               pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                                                 
PCI_VENDOR_ID, &pci_id);
+               /* Offset 0x2c is PCI_SUBSYSTEM_ID aka PCI_SUBSYSTEM_VENDOR_ID. 
*/
+               pcibios_read_config_dword(pci_bus, pci_device_fn, 0x2c, 
&subsys_id);
+               pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                                                 
PCI_REVISION_ID, &pci_class_rev);
+
+               for (chip_idx = 0; pci_tbl[chip_idx].name; chip_idx++) {
+                       struct pci_id_info *chip = &pci_tbl[chip_idx];
+                       if ((pci_id & chip->id.pci_mask) == chip->id.pci
+                               && (subsys_id & chip->id.subsystem_mask) == 
chip->id.subsystem
+                               && (pci_class_rev&chip->id.revision_mask) == 
chip->id.revision)
+                               break;
+               }
+               if (pci_tbl[chip_idx].name == 0)                /* Compiled 
out! */
+                       continue;
+
+               pci_flags = pci_tbl[chip_idx].pci_flags;
+               pdev = pci_find_slot(pci_bus, pci_device_fn);
+               pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                                
PCI_INTERRUPT_LINE, &pci_irq_line);
+               irq = pci_irq_line;
+               pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                                                 ((pci_flags 
>> 2) & 0x1C) + 0x10,
+                                                                 &pci_busaddr);
+               pciaddr = pci_busaddr;
+#if defined(__alpha__)
+               if (pci_flags & PCI_ADDR_64BITS) {
+                       pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                                                         
((pci_flags >> 2) & 0x1C) + 0x14,
+                                                                         
&pci_busaddr);
+                       pciaddr |= ((long)pci_busaddr)<<32;
+               }
+#endif
+
+               if (msg_level > 2)
+                       printk(KERN_INFO "Found %s at PCI address %#lx, IRQ 
%d.\n",
+                                  pci_tbl[chip_idx].name, pciaddr, irq);
+
+               if ( ! (pci_flags & PCI_UNUSED_IRQ)  &&
+                        (irq == 0 || irq == 255)) {
+                       if (pci_bus == 32)      /* Broken CardBus activation. */
+                               printk(KERN_WARNING "Resources for CardBus 
device '%s' have"
+                                          " not been allocated.\n"
+                                          KERN_WARNING "It will not be 
activated.\n",
+                                          pci_tbl[chip_idx].name);
+                       else
+                               printk(KERN_WARNING "PCI device '%s' was not 
assigned an "
+                                          "IRQ.\n"
+                                          KERN_WARNING "It will not be 
activated.\n",
+                                  pci_tbl[chip_idx].name);
+                       continue;
+               }
+
+               if ((pciaddr & PCI_BASE_ADDRESS_SPACE_IO)) {
+                       ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK;
+                       if (check_region(ioaddr, pci_tbl[chip_idx].io_size))
+                               continue;
+               } else if ((ioaddr = (long)ioremap(pciaddr & 
PCI_BASE_ADDRESS_MEM_MASK,
+                                                                               
   pci_tbl[chip_idx].io_size)) == 0) {
+                       printk(KERN_INFO "Failed to map PCI address %#lx.\n",
+                                  pciaddr);
+                       continue;
+               }
+
+               if ( ! (pci_flags & PCI_NO_ACPI_WAKE))
+                       acpi_wake(pdev);
+               pcibios_read_config_word(pci_bus, pci_device_fn,
+                                                                PCI_COMMAND, 
&pci_command);
+               new_command = pci_command | (pci_flags & 7);
+               if (pci_command != new_command) {
+                       printk(KERN_INFO "  The PCI BIOS has not enabled the"
+                                  " device at %d/%d!  Updating PCI command 
%4.4x->%4.4x.\n",
+                                  pci_bus, pci_device_fn, pci_command, 
new_command);
+                       pcibios_write_config_word(pci_bus, pci_device_fn,
+                                                                         
PCI_COMMAND, new_command);
+               }
+
+               newdev = drv_id->probe1(pdev, initial_device,
+                                                          ioaddr, irq, 
chip_idx, cards_found);
+
+               if (newdev  && (pci_flags & PCI_COMMAND_MASTER)  &&
+                       ! (pci_flags & PCI_NO_MIN_LATENCY)) {
+                       u8 pci_latency;
+                       pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                                                        
PCI_LATENCY_TIMER, &pci_latency);
+                       if (pci_latency < min_pci_latency) {
+                               printk(KERN_INFO "  PCI latency timer (CFLT) is 
"
+                                          "unreasonably low at %d.  Setting to 
%d clocks.\n",
+                                          pci_latency, min_pci_latency);
+                               pcibios_write_config_byte(pci_bus, 
pci_device_fn,
+                                                                               
  PCI_LATENCY_TIMER, min_pci_latency);
+                       }
+               }
+               if (newdev) {
+                       struct dev_info *devp =
+                               kmalloc(sizeof(struct dev_info), GFP_KERNEL);
+                       if (devp) {
+                               devp->next = dev_list;
+                               devp->dev = newdev;
+                               devp->drv_id = drv_id;
+                               dev_list = devp;
+                       }
+               }
+               initial_device = 0;
+               cards_found++;
+       }
+
+       if (((drv_id->flags & PCI_HOTSWAP)
+                && register_hotswap_hook && (*register_hotswap_hook)(drv_id) 
== 0)
+               || cards_found) {
+               MOD_INC_USE_COUNT;
+               drv_id->next = drv_list;
+               drv_list = drv_id;
+               return 0;
+       } else
+               return cards_found ? 0 : -ENODEV;
+}
+#endif
+
+void pci_drv_unregister(struct drv_id_info *drv_id)
+{
+       struct drv_id_info **drvp;
+       struct dev_info **devip = &dev_list;
+
+       if (unregister_hotswap_hook)
+               (*unregister_hotswap_hook)(drv_id);
+
+       for (drvp = &drv_list; *drvp; drvp = &(*drvp)->next)
+               if (*drvp == drv_id) {
+                       *drvp = (*drvp)->next;
+                       MOD_DEC_USE_COUNT;
+                       break;
+               }
+       while (*devip) {
+               struct dev_info *thisdevi = *devip;
+               if (thisdevi->drv_id == drv_id) {
+                       *devip = thisdevi->next;
+                       kfree(thisdevi);
+               } else
+                       devip = &(*devip)->next;
+       }
+
+       return;
+}
+
+#if LINUX_VERSION_CODE < 0x20400
+/*
+  Search PCI configuration space for the specified capability registers.
+  Return the index, or 0 on failure.
+  The 2.4 kernel now includes this function.
+*/
+int pci_find_capability(struct pci_dev *pdev, int findtype)
+{
+       u16 pci_status, cap_type;
+       u8 pci_cap_idx;
+       int cap_idx;
+
+       pci_read_config_word(pdev, PCI_STATUS, &pci_status);
+       if ( ! (pci_status & PCI_STATUS_CAP_LIST))
+               return 0;
+       pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pci_cap_idx);
+       cap_idx = pci_cap_idx;
+       for (cap_idx = pci_cap_idx; cap_idx; cap_idx = (cap_type >> 8) & 0xff) {
+               pci_read_config_word(pdev, cap_idx, &cap_type);
+               if ((cap_type & 0xff) == findtype)
+                       return cap_idx;
+       }
+       return 0;
+}
+#endif
+
+/* Change a device from D3 (sleep) to D0 (active).
+   Return the old power state.
+   This is more complicated than you might first expect since most cards
+   forget all PCI config info during the transition! */
+int acpi_wake(struct pci_dev *pdev)
+{
+       u32 base[5], romaddr;
+       u16 pci_command, pwr_command;
+       u8  pci_latency, pci_cacheline, irq;
+       int i, pwr_cmd_idx = pci_find_capability(pdev, PCI_CAP_ID_PM);
+
+       if (pwr_cmd_idx == 0)
+               return 0;
+       pci_read_config_word(pdev, pwr_cmd_idx + 4, &pwr_command);
+       if ((pwr_command & 3) == 0)
+               return 0;
+       pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+       for (i = 0; i < 5; i++)
+               pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0 + i*4,
+                                                                 &base[i]);
+       pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &romaddr);
+       pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &pci_latency);
+       pci_read_config_byte( pdev, PCI_CACHE_LINE_SIZE, &pci_cacheline);
+       pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq);
+
+       pci_write_config_word(pdev, pwr_cmd_idx + 4, 0x0000);
+       for (i = 0; i < 5; i++)
+               if (base[i])
+                       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0 + i*4,
+                                                                          
base[i]);
+       pci_write_config_dword(pdev, PCI_ROM_ADDRESS, romaddr);
+       pci_write_config_byte( pdev, PCI_INTERRUPT_LINE, irq);
+       pci_write_config_byte( pdev, PCI_CACHE_LINE_SIZE, pci_cacheline);
+       pci_write_config_byte( pdev, PCI_LATENCY_TIMER, pci_latency);
+       pci_write_config_word( pdev, PCI_COMMAND, pci_command | 5);
+       return pwr_command & 3;
+}
+
+int acpi_set_pwr_state(struct pci_dev *pdev, enum acpi_pwr_state new_state)
+{
+       u16 pwr_command;
+       int pwr_cmd_idx = pci_find_capability(pdev, PCI_CAP_ID_PM);
+
+       if (pwr_cmd_idx == 0)
+               return 0;
+       pci_read_config_word(pdev, pwr_cmd_idx + 4, &pwr_command);
+       if ((pwr_command & 3) == ACPI_D3  &&  new_state != ACPI_D3)
+               acpi_wake(pdev);                /* The complicated sequence. */
+       pci_write_config_word(pdev, pwr_cmd_idx + 4,
+                                                         (pwr_command & ~3) | 
new_state);
+       return pwr_command & 3;
+}
+
+#if defined(CONFIG_PM)
+static int handle_pm_event(struct pm_dev *dev, int event, void *data)
+{
+       static int down = 0;
+       struct dev_info *devi;
+       int pwr_cmd = -1;
+
+       if (msg_level > 1)
+               printk(KERN_DEBUG "pci-scan: Handling power event %d for driver 
"
+                          "list %s...\n",
+                          event, drv_list->name);
+       switch (event) {
+       case PM_SUSPEND:
+               if (down) {
+                       printk(KERN_DEBUG "pci-scan: Received extra suspend 
event\n");
+                       break;
+               }
+               down = 1;
+               for (devi = dev_list; devi; devi = devi->next)
+                       if (devi->drv_id->pwr_event)
+                               devi->drv_id->pwr_event(devi->dev, DRV_SUSPEND);
+               break;
+       case PM_RESUME:
+               if (!down) {
+                       printk(KERN_DEBUG "pci-scan: Received bogus resume 
event\n");
+                       break;
+               }
+               for (devi = dev_list; devi; devi = devi->next) {
+                       if (devi->drv_id->pwr_event) {
+                               if (msg_level > 3)
+                                       printk(KERN_DEBUG "pci-scan: Calling 
resume for %s "
+                                                  "device.\n", 
devi->drv_id->name);
+                               devi->drv_id->pwr_event(devi->dev, DRV_RESUME);
+                       }
+               }
+               down = 0;
+               break;
+       case PM_SET_WAKEUP: pwr_cmd = DRV_PWR_WakeOn; break;
+       case PM_EJECT:          pwr_cmd = DRV_DETACH;   break;
+       default:
+               printk(KERN_DEBUG "pci-scan: Unknown power management event 
%d.\n",
+                          event);
+       }
+       if (pwr_cmd >= 0)
+               for (devi = dev_list; devi; devi = devi->next)
+                       if (devi->drv_id->pwr_event)
+                               devi->drv_id->pwr_event(devi->dev, pwr_cmd);
+
+       return 0;
+}
+
+#elif defined(CONFIG_APM)  &&  LINUX_VERSION_CODE < 0x20400 
+static int handle_apm_event(apm_event_t event)
+{
+       static int down = 0;
+       struct dev_info *devi;
+
+       if (msg_level > 1)
+               printk(KERN_DEBUG "pci-scan: Handling APM event %d for driver "
+                          "list %s...\n",
+                          event, drv_list->name);
+       return 0;
+       switch (event) {
+       case APM_SYS_SUSPEND:
+       case APM_USER_SUSPEND:
+               if (down) {
+                       printk(KERN_DEBUG "pci-scan: Received extra suspend 
event\n");
+                       break;
+               }
+               down = 1;
+               for (devi = dev_list; devi; devi = devi->next)
+                       if (devi->drv_id->pwr_event)
+                               devi->drv_id->pwr_event(devi->dev, DRV_SUSPEND);
+               break;
+       case APM_NORMAL_RESUME:
+       case APM_CRITICAL_RESUME:
+               if (!down) {
+                       printk(KERN_DEBUG "pci-scan: Received bogus resume 
event\n");
+                       break;
+               }
+               for (devi = dev_list; devi; devi = devi->next)
+                       if (devi->drv_id->pwr_event)
+                               devi->drv_id->pwr_event(devi->dev, DRV_RESUME);
+               down = 0;
+               break;
+       }
+       return 0;
+}
+#endif /* CONFIG_APM */
+
+#ifdef MODULE
+int init_module(void)
+{
+       if (msg_level)  /* Emit version even if no cards detected. */
+               printk(KERN_INFO "%s", version);
+
+#if defined(CONFIG_PM)
+       pm_register(PM_PCI_DEV, 0, &handle_pm_event);
+#elif defined(CONFIG_APM)  &&  LINUX_VERSION_CODE < 0x20400 
+       apm_register_callback(&handle_apm_event);
+#endif
+       return 0;
+}
+void cleanup_module(void)
+{
+#if defined(CONFIG_PM)
+       pm_unregister_all(&handle_pm_event);
+#elif defined(CONFIG_APM)  &&  LINUX_VERSION_CODE < 0x20400 
+       apm_unregister_callback(&handle_apm_event);
+#endif
+       if (dev_list != NULL)
+               printk(KERN_WARNING "pci-scan: Unfreed device references.\n");
+       return;
+}
+#endif
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -DEXPORT_SYMTAB -Wall 
-Wstrict-prototypes -O6 -c pci-scan.c"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
Index: linux/src/drivers/net/pci-scan.h
--- /dev/null
+++ linux/src/drivers/net/pci-scan.h
@@ -0,0 +1,90 @@
+#ifndef _PCI_SCAN_H
+#define _PCI_SCAN_H
+/*
+  version 1.02 $Version:$ $Date: 2001/03/18 21:35:59 $
+   Copyright 1999-2001 Donald Becker / Scyld Computing Corporation
+   This software is part of the Linux kernel.  It may be used and
+   distributed according to the terms of the GNU Public License,
+   incorporated herein by reference.
+*/
+
+/*
+  These are the structures in the table that drives the PCI probe routines.
+  Note the matching code uses a bitmask: more specific table entries should
+  be placed before "catch-all" entries.
+
+  The table must be zero terminated.
+*/
+enum pci_id_flags_bits {
+       /* Set PCI command register bits before calling probe1(). */
+       PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+       /* Read and map the single following PCI BAR. */
+       PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
+       PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
+       PCI_UNUSED_IRQ=0x800,
+};
+
+struct pci_id_info {
+       const char *name;
+       struct match_info {
+               int     pci, pci_mask, subsystem, subsystem_mask;
+               int revision, revision_mask;                            /* Only 
8 bits. */
+       } id;
+       enum pci_id_flags_bits pci_flags;
+       int io_size;                            /* Needed for I/O region check 
or ioremap(). */
+       int drv_flags;                          /* Driver use, intended as 
capability flags. */
+};
+
+enum drv_id_flags {
+       PCI_HOTSWAP=1, /* Leave module loaded for Cardbus-like chips. */
+};
+enum drv_pwr_action {
+       DRV_NOOP,                       /* No action. */
+       DRV_ATTACH,                     /* The driver may expect power ops. */
+       DRV_SUSPEND,            /* Machine suspending, next event RESUME or 
DETACH. */
+       DRV_RESUME,                     /* Resume from previous SUSPEND  */
+       DRV_DETACH,                     /* Card will-be/is gone. Valid from 
SUSPEND! */
+       DRV_PWR_WakeOn,         /* Put device in e.g. Wake-On-LAN mode. */
+       DRV_PWR_DOWN,           /* Go to lowest power mode. */
+       DRV_PWR_UP,                     /* Go to normal power mode. */
+};
+
+struct drv_id_info {
+       const char *name;                       /* Single-word driver name. */
+       int flags;
+       int pci_class;                          /* Typically 
PCI_CLASS_NETWORK_ETHERNET<<8. */
+       struct pci_id_info *pci_dev_tbl;
+       void *(*probe1)(struct pci_dev *pdev, void *dev_ptr,
+                                       long ioaddr, int irq, int table_idx, 
int fnd_cnt);
+       /* Optional, called for suspend, resume and detach. */
+       int (*pwr_event)(void *dev, int event);
+       /* Internal values. */
+       struct drv_id_info *next;
+       void *cb_ops;
+};
+
+/*  PCI scan and activate.
+       Scan PCI-like hardware, calling probe1(..,dev,..) on devices that match.
+       Returns -ENODEV, a negative number, if no cards are found. */
+
+extern int pci_drv_register(struct drv_id_info *drv_id, void *initial_device);
+extern void pci_drv_unregister(struct drv_id_info *drv_id);
+
+
+/*  ACPI routines.
+       Wake (change to ACPI D0 state) or set the ACPI power level of a sleeping
+       ACPI device.  Returns the old power state.  */
+
+int acpi_wake(struct pci_dev *pdev);
+enum  acpi_pwr_state {ACPI_D0, ACPI_D1, ACPI_D2, ACPI_D3};
+int acpi_set_pwr_state(struct pci_dev *pdev, enum acpi_pwr_state state);
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
+#endif
Index: linux/src/drivers/net/rtl8139.c
--- linux/src/drivers/net/rtl8139.c
+++ linux/src/drivers/net/rtl8139.c
@@ -1,34 +1,62 @@
 /* rtl8139.c: A RealTek RTL8129/8139 Fast Ethernet driver for Linux. */
 /*
-       Written 1997-1998 by Donald Becker.
-
-       This software may be used and distributed according to the terms
-       of the GNU Public License, incorporated herein by reference.
-    All other rights reserved.
+       Written and Copyright 1997-2003 by Donald Becker.
+       This software may be used and distributed according to the terms of
+       the GNU General Public License (GPL), incorporated herein by reference.
+       Drivers based on or derived from this code fall under the GPL and must
+       retain the authorship, copyright and license notice.  This file is not
+       a complete program and may only be used when the entire operating
+       system is licensed under the GPL.
 
        This driver is for boards based on the RTL8129 and RTL8139 PCI ethernet
        chips.
 
-       The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
-       Center of Excellence in Space Data and Information Sciences
-          Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+       The author may be reached as becker@scyld.com, or C/O
+       Scyld Computing Corporation
+       914 Bay Ridge Road, Suite 220
+       Annapolis MD 21403
 
        Support and updates available at
-       http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html
+       http://www.scyld.com/network/rtl8139.html
 
-       Twister-tuning code contributed by Kinston <shangh@realtek.com.tw>.
+       Twister-tuning table provided by Kinston <shangh@realtek.com.tw>.
 */
 
-static const char *version =
-"rtl8139.c:v0.99B 4/7/98 Donald Becker 
http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n";;
+/* These identify the driver base version and may not be removed. */
+static const char versionA[] =
+"rtl8139.c:v1.24 11/13/2003 Donald Becker, becker@scyld.com.\n";
+static const char versionB[] =
+" http://www.scyld.com/network/rtl8139.html\n";;
+
+#ifndef USE_MEM_OPS
+/* Note: Register access width and timing restrictions apply in MMIO mode.
+   This updated driver should nominally work, but I/O mode is better tested. */
+#define USE_IO_OPS
+#endif
+
+/* The user-configurable values.
+   These may be modified when a driver module is loaded.*/
+/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */
+static int debug = 2;
 
-/* A few user-configurable values. */
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 10;
+static int max_interrupt_work = 20;
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+   The RTL chips use a 64 element hash table based on the Ethernet CRC.  It
+   is efficient to update the hardware filter, but recalculating the table
+   for a long filter list is painful.  */
+static int multicast_filter_limit = 32;
+
+/* Used to pass the full-duplex flag, etc. */
+#define MAX_UNITS 8            /* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Operational parameters that are set at compile time. */
 
-/* Size of the in-memory receive ring. */
+/* Maximum size of the in-memory receive ring (smaller if no memory). */
 #define RX_BUF_LEN_IDX 2                       /* 0==8K, 1==16K, 2==32K, 
3==64K */
-#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
 /* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
 #define TX_BUF_SIZE    1536
 
@@ -39,68 +67,86 @@
 /* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024. */
 #define RX_FIFO_THRESH 4               /* Rx buffer level before first PCI 
xfer.  */
 #define RX_DMA_BURST   4               /* Maximum PCI burst, '4' is 256 bytes 
*/
-#define TX_DMA_BURST   4
+#define TX_DMA_BURST   4               /* Calculate as 16<<val. */
 
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  ((4000*HZ)/1000)
+#define TX_TIMEOUT  (6*HZ)
 
-#ifdef MODULE
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
+/* Allocation size of Rx buffers with full-sized Ethernet frames.
+   This is a cross-driver value that is not a limit,
+   but a way to keep a consistent allocation size among drivers.
+ */
+#define PKT_BUF_SZ             1536
+
+
+#ifndef __KERNEL__
+#define __KERNEL__
 #endif
-#include <linux/module.h>
+#if !defined(__OPTIMIZE__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+#include <linux/config.h>
+#if defined(CONFIG_SMP) && ! defined(__SMP__)
+#define __SMP__
+#endif
+#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
+#define MODVERSIONS
+#endif
+
 #include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
 #endif
+#include <linux/module.h>
 
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
-#include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
+#if LINUX_VERSION_CODE >= 0x20400
+#include <linux/slab.h>
+#else
 #include <linux/malloc.h>
+#endif
 #include <linux/interrupt.h>
 #include <linux/pci.h>
-#include <linux/bios32.h>
-#include <asm/processor.h>             /* Processor type for cache alignment. 
*/
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <asm/processor.h>             /* Processor type for cache alignment. 
*/
+#include <asm/bitops.h>
+#include <asm/io.h>
 
-#define RUN_AT(x) (jiffies + (x))
-
-#include <linux/delay.h>
-
-#if (LINUX_VERSION_CODE < 0x20123)
-#define test_and_set_bit(val, addr) set_bit(val, addr)
+#if LINUX_VERSION_CODE >= 0x20300
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= 0x20200
+#include <asm/spinlock.h>
 #endif
 
-/* The I/O extent. */
-#define RTL8129_TOTAL_SIZE 0x80
-
-#ifdef HAVE_DEVLIST
-struct netdev_entry rtl8139_drv =
-{"RTL8139", rtl8139_probe, RTL8129_TOTAL_SIZE, NULL};
+#ifdef INLINE_PCISCAN
+#include "k_compat.h"
+#else
+#include "pci-scan.h"
+#include "kern_compat.h"
 #endif
 
-static int rtl8129_debug = 1;
+#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE)
+char kernel_version[] = UTS_RELEASE;
+#endif
 
 /*
                                Theory of Operation
 
 I. Board Compatibility
 
-This device driver is designed for the RealTek RTL8129, the RealTek Fast
-Ethernet controllers for PCI.  This chip is used on a few clone boards.
+This device driver is designed for the RealTek RTL8129 series, the RealTek
+Fast Ethernet controllers for PCI and CardBus.  This chip is used on many
+low-end boards, sometimes with custom chip labels.
 
 
 II. Board-specific settings
@@ -121,15 +167,17 @@
 skbuffs.
 
 Comment: While it is theoretically possible to process many frames in place,
-any delay in Rx processing would cause us to drop frames.  More importantly,
-the Linux protocol stack is not designed to operate in this manner.
+any delay in Rx processing would block the Rx ring and cause us to drop
+frames.  It would be difficult to design a protocol stack where the data
+buffer could be recalled by the device driver.
 
 IIIb. Tx operation
 
-The RTL8129 uses a fixed set of four Tx descriptors in register space.
-In a stunningly bad design choice, Tx frames must be 32 bit aligned.  Linux
-aligns the IP header on word boundaries, and 14 byte ethernet header means
-that almost all frames will need to be copied to an alignment buffer.
+The RTL8129 uses a fixed set of four Tx descriptors in register space.  Tx
+frames must be 32 bit aligned.  Linux aligns the IP header on word
+boundaries, and 14 byte ethernet header means that almost all frames will
+need to be copied to an alignment buffer.  The driver statically allocates
+alignment the four alignment buffers at open() time.
 
 IVb. References
 
@@ -139,15 +187,80 @@
 IVc. Errata
 
 */
+
 
-#ifndef PCI_VENDOR_ID_REALTEK
-#define PCI_VENDOR_ID_REALTEK          0x10ec
-#endif
-#ifndef PCI_DEVICE_ID_REALTEK_8129
-#define PCI_DEVICE_ID_REALTEK_8129     0x8129
+static void *rtl8139_probe1(struct pci_dev *pdev, void *init_dev,
+                                                       long ioaddr, int irq, 
int chip_idx, int find_cnt);
+static int rtl_pwr_event(void *dev_instance, int event);
+
+enum chip_capability_flags {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02,
+                                                       HAS_LNK_CHNG=0x04, 
HAS_DESC=0x08};
+#ifdef USE_IO_OPS
+#define RTL8139_IOTYPE  PCI_USES_MASTER|PCI_USES_IO |PCI_ADDR0
+#else
+#define RTL8139_IOTYPE  PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR1
 #endif
-#ifndef PCI_DEVICE_ID_REALTEK_8139
-#define PCI_DEVICE_ID_REALTEK_8139     0x8139
+#define RTL8129_CAPS  HAS_MII_XCVR
+#define RTL8139_CAPS  HAS_CHIP_XCVR|HAS_LNK_CHNG
+#define RTL8139D_CAPS  HAS_CHIP_XCVR|HAS_LNK_CHNG|HAS_DESC
+
+/* Note: Update the marked constant in _attach() if the RTL8139B entry moves.*/
+static struct pci_id_info pci_tbl[] = {
+       {"RealTek RTL8139C+, 64 bit high performance",
+        { 0x813910ec, 0xffffffff, 0,0, 0x20, 0xff},
+        RTL8139_IOTYPE, 0x80, RTL8139D_CAPS, },
+       {"RealTek RTL8139C Fast Ethernet",
+        { 0x813910ec, 0xffffffff, 0,0, 0x10, 0xff},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"RealTek RTL8129 Fast Ethernet", { 0x812910ec, 0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8129_CAPS, },
+       {"RealTek RTL8139 Fast Ethernet", { 0x813910ec, 0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"RealTek RTL8139B PCI/CardBus",  { 0x813810ec, 0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"SMC1211TX EZCard 10/100 (RealTek RTL8139)", { 0x12111113, 
0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"Accton MPX5030 (RealTek RTL8139)", { 0x12111113, 0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"D-Link DFE-530TX+ (RealTek RTL8139C)",
+        { 0x13001186, 0xffffffff, 0x13011186, 0xffffffff,},
+        RTL8139_IOTYPE, 0x100, RTL8139_CAPS, },
+       {"D-Link DFE-538TX (RealTek RTL8139)", { 0x13001186, 0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"LevelOne FPC-0106Tx (RealTek RTL8139)", { 0x0106018a, 0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"Compaq HNE-300 (RealTek RTL8139c)", { 0x8139021b, 0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"Edimax EP-4103DL CardBus (RealTek RTL8139c)", { 0xab0613d1, 
0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"Siemens 1012v2 CardBus (RealTek RTL8139c)", { 0x101202ac, 
0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"Siemens 1020 PCI NIC (RealTek RTL8139c)", { 0x91301432, 0xffffffff,},
+        RTL8139_IOTYPE, 0x80, RTL8139_CAPS, },
+       {"D-Link DFE-690TXD CardBus (RealTek RTL8139C)", {0x13401186, 
0xffffffff,},
+        RTL8139_IOTYPE, 0x100, RTL8139_CAPS, },
+       {"D-Link 1300 series NIC (RealTek RTL8139C)", {0x13001186, 0xff00ffff,},
+        RTL8139_IOTYPE, 0x100, RTL8139_CAPS, },
+       {0,},                                           /* 0 terminated list. */
+};
+
+struct drv_id_info rtl8139_drv_id = {
+       "realtek", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_tbl,
+       rtl8139_probe1, rtl_pwr_event };
+
+#ifndef USE_IO_OPS
+#undef inb
+#undef inw
+#undef inl
+#undef outb
+#undef outw
+#undef outl
+#define inb readb
+#define inw readw
+#define inl readl
+#define outb writeb
+#define outw writew
+#define outl writel
 #endif
 
 /* The rest of these values should never change. */
@@ -157,7 +270,7 @@
 enum RTL8129_registers {
        MAC0=0,                                         /* Ethernet hardware 
address. */
        MAR0=8,                                         /* Multicast filter. */
-       TxStat0=0x10,                           /* Transmit status (Four 32bit 
registers). */
+       TxStatus0=0x10,                         /* Transmit status (Four 32bit 
registers). */
        TxAddr0=0x20,                           /* Tx descriptors (also four 
32bit). */
        RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36,
        ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A,
@@ -168,9 +281,10 @@
        Cfg9346=0x50, Config0=0x51, Config1=0x52,
        FlashReg=0x54, GPPinData=0x58, GPPinDir=0x59, MII_SMI=0x5A, HltClk=0x5B,
        MultiIntr=0x5C, TxSummary=0x60,
-       BMCR=0x62, BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, 
NWayExpansion=0x6A,
+       MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68,
+       NWayExpansion=0x6A,
        /* Undocumented registers, but required for proper operation. */
-       FIFOTMS=0x70,   /* FIFO Test Mode Select */
+       FIFOTMS=0x70,   /* FIFO Control and test. */
        CSCR=0x74,      /* Chip Status and Configuration Register. */
        PARA78=0x78, PARA7c=0x7c,       /* Magic transceiver parameter 
register. */
 };
@@ -194,281 +308,244 @@
        RxBadAlign=0x0002, RxStatusOK=0x0001,
 };
 
+/* Twister tuning parameters from RealTek.
+   Completely undocumented, but required to tune bad links. */
 enum CSCRBits {
        CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800,
        CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
        CSCR_LinkDownCmd=0x0f3c0,
-};     
-
-/* Twister tuning parameters from RealTek.  Completely undocumented. */
+};
+#define PARA78_default 0x78fa8388
+#define PARA7c_default 0xcb38de43                      /* param[0][3] */
+#define PARA7c_xxx             0xcb38de43
 unsigned long param[4][4]={
-       {0x0cb39de43,0x0cb39ce43,0x0fb38de03,0x0cb38de43},
-       {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
-       {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
-       {0x0bb39de43,0x0bb39ce43,0x0bb39ce83,0x0bb39ce83}
+       {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+       {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+       {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+       {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
 };
 
+#define PRIV_ALIGN     15      /* Desired alignment mask */
 struct rtl8129_private {
-       char devname[8];                        /* Used only for kernel 
debugging. */
-       const char *product_name;
-       struct device *next_module;
-       int chip_id;
-       int chip_revision;
-#if LINUX_VERSION_CODE > 0x20139
+       struct net_device *next_module;
+       void *priv_addr;                                        /* Unaligned 
address for kfree */
+
+       int chip_id, drv_flags;
+       struct pci_dev *pci_dev;
        struct net_device_stats stats;
-#else
-       struct enet_statistics stats;
-#endif
        struct timer_list timer;        /* Media selection timer. */
-       unsigned int cur_rx, cur_tx;            /* The next free and used 
entries */
-       unsigned int dirty_rx, dirty_tx;
+       int msg_level;
+       int max_interrupt_work;
+
+       /* Receive state. */
+       unsigned char *rx_ring;
+       unsigned int cur_rx;            /* Index into the Rx buffer of next Rx 
pkt. */
+       unsigned int rx_buf_len;        /* Size (8K 16K 32K or 64KB) of the Rx 
ring */
+
+       /* Transmit state. */
+       unsigned int cur_tx, dirty_tx, tx_flag;
+       unsigned long tx_full;                          /* The Tx queue is 
full. */
        /* The saved address of a sent-in-place packet/buffer, for skfree(). */
        struct sk_buff* tx_skbuff[NUM_TX_DESC];
        unsigned char *tx_buf[NUM_TX_DESC];     /* Tx bounce buffers */
-       unsigned char *rx_ring;
        unsigned char *tx_bufs;                         /* Tx bounce buffer 
region. */
-       unsigned char mc_filter[8];                     /* Current multicast 
filter. */
+
+       /* Receive filter state. */
+       unsigned int rx_config;
+       u32 mc_filter[2];                /* Multicast hash filter */
+       int cur_rx_mode;
+       int multicast_filter_limit;
+
+       /* Transceiver state. */
        char phys[4];                                           /* MII device 
addresses. */
-       int in_interrupt;                                       /* Alpha needs 
word-wide lock. */
-       unsigned int tx_full:1;                         /* The Tx queue is 
full. */
+       u16 advertising;                                        /* NWay media 
advertisement */
+       char twistie, twist_row, twist_col;     /* Twister tune state. */
+       u8      config1;
        unsigned int full_duplex:1;                     /* Full-duplex 
operation requested. */
-       unsigned int default_port:4;            /* Last dev->if_port value. */
+       unsigned int duplex_lock:1;
        unsigned int media2:4;                          /* Secondary monitored 
media port. */
        unsigned int medialock:1;                       /* Don't sense media 
type. */
        unsigned int mediasense:1;                      /* Media sensing in 
progress. */
+       unsigned int default_port;                      /* Last dev->if_port 
value. */
 };
 
-#ifdef MODULE
-/* Used to pass the full-duplex flag, etc. */
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-#if LINUX_VERSION_CODE > 0x20118
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+/* Donald Becker is the original author and copyright holder of this code.
+ * Changing the driver so that this information is removed and distributing
+ * the resulting binary driver is a violation of the license.
+ */
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("RealTek RTL8129/8139 Fast Ethernet driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+MODULE_LICENSE("GPL");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(multicast_filter_limit, "i");
 MODULE_PARM(max_interrupt_work, "i");
-#endif
-#endif
-
-static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq,
-                                                                        int 
chip_id, int options, int card_idx);
-static int rtl8129_open(struct device *dev);
-static int read_eeprom(int ioaddr, int location);
-static int mdio_read(int ioaddr, int phy_id, int location);
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Driver message level (0-31)");
+MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex");
+MODULE_PARM_DESC(full_duplex, "Non-zero to set forced full duplex.");
+MODULE_PARM_DESC(multicast_filter_limit,
+                                "Multicast addresses before switching to 
Rx-all-multicast");
+MODULE_PARM_DESC(max_interrupt_work,
+                                "Driver maximum events handled per interrupt");
+
+static int rtl8129_open(struct net_device *dev);
+static void rtl_hw_start(struct net_device *dev);
+static int read_eeprom(long ioaddr, int location, int addr_len);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int 
val);
 static void rtl8129_timer(unsigned long data);
-static void rtl8129_tx_timeout(struct device *dev);
-static void rtl8129_init_ring(struct device *dev);
-static int rtl8129_start_xmit(struct sk_buff *skb, struct device *dev);
-static int rtl8129_rx(struct device *dev);
+static void rtl8129_tx_timeout(struct net_device *dev);
+static void rtl8129_init_ring(struct net_device *dev);
+static int rtl8129_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static int rtl8129_rx(struct net_device *dev);
 static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs 
*regs);
-static int rtl8129_close(struct device *dev);
-static struct enet_statistics *rtl8129_get_stats(struct device *dev);
-static void set_rx_mode(struct device *dev);
+static void rtl_error(struct net_device *dev, int status, int link_status);
+static int rtl8129_close(struct net_device *dev);
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *rtl8129_get_stats(struct net_device *dev);
+static inline u32 ether_crc(int length, unsigned char *data);
+static void set_rx_mode(struct net_device *dev);
 
 
-#ifdef MODULE
 /* A list of all installed RTL8129 devices, for removing the driver module. */
-static struct device *root_rtl8129_dev = NULL;
-#endif
+static struct net_device *root_rtl8129_dev = NULL;
 
-int rtl8139_probe(struct device *dev)
+#ifndef MODULE
+int rtl8139_probe(struct net_device *dev)
 {
-       int cards_found = 0;
-       static int pci_index = 0;       /* Static, for multiple probe calls. */
-
-       /* Ideally we would detect all network cards in slot order.  That would
-          be best done a central PCI probe dispatch, which wouldn't work
-          well with the current structure.  So instead we detect just the
-          Rtl81*9 cards in slot order. */
-
-       if (pcibios_present()) {
-               unsigned char pci_bus, pci_device_fn;
-
-               for (;pci_index < 0xff; pci_index++) {
-                       u8 pci_irq_line, pci_latency;
-                       u16 pci_command, new_command, vendor, device;
-                       u32 pci_ioaddr;
-
-                       if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
-#ifdef REVERSE_PROBE_ORDER
-                                                                       0xff - 
pci_index,
-#else
-                                                                       
pci_index,
-#endif
-                                                                       
&pci_bus, &pci_device_fn)
-                               != PCIBIOS_SUCCESSFUL)
-                               break;
-                       pcibios_read_config_word(pci_bus, pci_device_fn,
-                                                                        
PCI_VENDOR_ID, &vendor);
-                       if (vendor != PCI_VENDOR_ID_REALTEK)
-                               continue;
-
-                       pcibios_read_config_word(pci_bus, pci_device_fn,
-                                                                        
PCI_DEVICE_ID, &device);
-                       pcibios_read_config_byte(pci_bus, pci_device_fn,
-                                                                        
PCI_INTERRUPT_LINE, &pci_irq_line);
-                       pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                                                         
PCI_BASE_ADDRESS_0, &pci_ioaddr);
-                       /* Remove I/O space marker in bit 0. */
-                       pci_ioaddr &= ~3;
-
-                       if (device != PCI_DEVICE_ID_REALTEK_8129
-                               &&  device != PCI_DEVICE_ID_REALTEK_8139) {
-                               printk(KERN_NOTICE "Unknown RealTek PCI 
ethernet chip type "
-                                          "%4.4x detected: not configured.\n", 
device);
-                               continue;
-                       }
-                       if (check_region(pci_ioaddr, RTL8129_TOTAL_SIZE))
-                               continue;
-
-                       /* Activate the card: fix for brain-damaged Win98 
BIOSes. */
-                       pcibios_read_config_word(pci_bus, pci_device_fn,
-                                                                        
PCI_COMMAND, &pci_command);
-                       new_command = pci_command | 
PCI_COMMAND_MASTER|PCI_COMMAND_IO;
-                       if (pci_command != new_command) {
-                               printk(KERN_INFO "  The PCI BIOS has not 
enabled this"
-                                          " device!  Updating PCI config 
%4.4x->%4.4x.\n",
-                                          pci_command, new_command);
-                               pcibios_write_config_word(pci_bus, 
pci_device_fn,
-                                                                               
  PCI_COMMAND, new_command);
-                       }
-
-#ifdef MODULE
-                       dev = rtl8129_probe1(dev, pci_ioaddr, pci_irq_line, 
device,
-                                                                
options[cards_found], cards_found);
-#else
-                       dev = rtl8129_probe1(dev, pci_ioaddr, pci_irq_line, 
device,
-                                                                dev ? 
dev->mem_start : 0, -1);
-#endif
-
-                       if (dev) {
-                               pcibios_read_config_byte(pci_bus, pci_device_fn,
-                                                                               
 PCI_LATENCY_TIMER, &pci_latency);
-                               if (pci_latency < 32) {
-                                       printk(KERN_NOTICE"  PCI latency timer 
(CFLT) is "
-                                                  "unreasonably low at %d.  
Setting to 64 clocks.\n",
-                                                  pci_latency);
-                                       pcibios_write_config_byte(pci_bus, 
pci_device_fn,
-                                                                               
          PCI_LATENCY_TIMER, 64);
-                               } else if (rtl8129_debug > 1)
-                                       printk(KERN_INFO"  PCI latency timer 
(CFLT) is %#x.\n",
-                                                  pci_latency);
-                               dev = 0;
-                               cards_found++;
-                       }
-               }
-       }
+       static int did_version = 0;                     /* Already printed 
version info. */
 
-#if defined (MODULE)
-       return cards_found;
-#else
-       return cards_found ? 0 : -ENODEV;
-#endif
+       if (debug >= NETIF_MSG_DRV      /* Emit version even if no cards 
detected. */
+               &&  did_version++ == 0)
+               printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+       return pci_drv_register(&rtl8139_drv_id, dev);
 }
+#endif
 
-static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq,
-                                                                  int chip_id, 
int options, int card_idx)
+static void *rtl8139_probe1(struct pci_dev *pdev, void *init_dev,
+                                                       long ioaddr, int irq, 
int chip_idx, int found_cnt)
 {
-       static int did_version = 0;                     /* Already printed 
version info. */
-       struct rtl8129_private *tp;
-       int i;
+       struct net_device *dev;
+       struct rtl8129_private *np;
+       void *priv_mem;
+       int i, option = found_cnt < MAX_UNITS ? options[found_cnt] : 0;
+       int config1;
+
+       dev = init_etherdev(init_dev, 0);
+       if (!dev)
+               return NULL;
 
-       if (rtl8129_debug > 0  &&  did_version++ == 0)
-               printk(KERN_INFO "%s", version);
-
-       dev = init_etherdev(dev, 0);
-
-       printk(KERN_INFO "%s: RealTek RTL%x at %#3x, IRQ %d, ",
-                  dev->name, chip_id, ioaddr, irq);
+       printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ",
+                  dev->name, pci_tbl[chip_idx].name, ioaddr, irq);
 
        /* Bring the chip out of low-power mode. */
-       outb(0x00, ioaddr + Config1);
-
-       /* Perhaps this should be read from the EEPROM? */
-       for (i = 0; i < 6; i++)
-               dev->dev_addr[i] = inb(ioaddr + MAC0 + i);
+       config1 = inb(ioaddr + Config1);
+       if (pci_tbl[chip_idx].drv_flags & HAS_MII_XCVR)                 /* 
rtl8129 chip */
+               outb(config1 & ~0x03, ioaddr + Config1);
+
+       {
+               int addr_len = read_eeprom(ioaddr, 0, 8) == 0x8129 ? 8 : 6;
+               for (i = 0; i < 3; i++)
+                       ((u16 *)(dev->dev_addr))[i] =
+                               le16_to_cpu(read_eeprom(ioaddr, i+7, addr_len));
+       }
 
        for (i = 0; i < 5; i++)
                printk("%2.2x:", dev->dev_addr[i]);
        printk("%2.2x.\n", dev->dev_addr[i]);
 
-       if (rtl8129_debug > 1) {
-               printk(KERN_INFO "%s: EEPROM contents\n", dev->name);
-               for (i = 0; i < 64; i++)
-                       printk(" %4.4x%s", read_eeprom(ioaddr, i),
-                                  i%16 == 15 ? "\n"KERN_INFO : "");
-       }
+       /* Make certain elements e.g. descriptor lists are aligned. */
+       priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL);
+       /* Check for the very unlikely case of no memory. */
+       if (priv_mem == NULL)
+               return NULL;
 
        /* We do a request_region() to register /proc/ioports info. */
-       request_region(ioaddr, RTL8129_TOTAL_SIZE, "RealTek RTL8129/39 Fast 
Ethernet");
+       request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
 
        dev->base_addr = ioaddr;
        dev->irq = irq;
 
-       /* Some data structures must be quadword aligned. */
-       tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
-       memset(tp, 0, sizeof(*tp));
-       dev->priv = tp;
+       dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN);
+       memset(np, 0, sizeof(*np));
+       np->priv_addr = priv_mem;
 
-#ifdef MODULE
-       tp->next_module = root_rtl8129_dev;
+       np->next_module = root_rtl8129_dev;
        root_rtl8129_dev = dev;
-#endif
 
-       tp->chip_id = chip_id;
+       np->pci_dev = pdev;
+       np->chip_id = chip_idx;
+       np->drv_flags = pci_tbl[chip_idx].drv_flags;
+       np->msg_level = (1 << debug) - 1;
+       np->max_interrupt_work = max_interrupt_work;
+       np->multicast_filter_limit = multicast_filter_limit;
+
+       np->config1 = config1;
 
        /* Find the connected MII xcvrs.
           Doing this in open() would allow detecting external xcvrs later, but
           takes too much time. */
-       if (chip_id == 0x8129) {
-               int phy, phy_idx;
-               for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < 
sizeof(tp->phys);
-                        phy++) {
-                       int mii_status = mdio_read(ioaddr, phy, 1);
-
-                       if (mii_status != 0xffff  && mii_status != 0x0000) {
-                               tp->phys[phy_idx++] = phy;
-                               printk(KERN_INFO "%s: MII transceiver found at 
address %d.\n",
-                                          dev->name, phy);
+       if (np->drv_flags & HAS_MII_XCVR) {
+               int phy, phy_idx = 0;
+               for (phy = 0; phy < 32 && phy_idx < sizeof(np->phys); phy++) {
+                       int mii_status = mdio_read(dev, phy, 1);
+                       if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+                               np->phys[phy_idx++] = phy;
+                               np->advertising = mdio_read(dev, phy, 4);
+                               printk(KERN_INFO "%s: MII transceiver %d status 
0x%4.4x "
+                                          "advertising %4.4x.\n",
+                                          dev->name, phy, mii_status, 
np->advertising);
                        }
                }
                if (phy_idx == 0) {
                        printk(KERN_INFO "%s: No MII transceivers found!  
Assuming SYM "
                                   "transceiver.\n",
                                   dev->name);
-                       tp->phys[0] = -1;
+                       np->phys[0] = 32;
                }
-       } else {
-                       tp->phys[0] = -1;
-       }
+       } else
+               np->phys[0] = 32;
 
        /* Put the chip into low-power mode. */
        outb(0xC0, ioaddr + Cfg9346);
-       outb(0x03, ioaddr + Config1);
+       if (np->drv_flags & HAS_MII_XCVR)                       /* rtl8129 chip 
*/
+               outb(0x03, ioaddr + Config1);
+
        outb('H', ioaddr + HltClk);             /* 'R' would leave the clock 
running. */
 
        /* The lower four bits are the media type. */
-       if (options > 0) {
-               tp->full_duplex = (options & 16) ? 1 : 0;
-               tp->default_port = options & 15;
-               if (tp->default_port)
-                       tp->medialock = 1;
+       if (option > 0) {
+               np->full_duplex = (option & 0x220) ? 1 : 0;
+               np->default_port = option & 0x330;
+               if (np->default_port)
+                       np->medialock = 1;
+       }
+
+       if (found_cnt < MAX_UNITS  &&  full_duplex[found_cnt] > 0)
+               np->full_duplex = full_duplex[found_cnt];
+
+       if (np->full_duplex) {
+               printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", 
dev->name);
+               /* Changing the MII-advertised media might prevent 
re-connection. */
+               np->duplex_lock = 1;
+       }
+       if (np->default_port) {
+               printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n",
+                          (option & 0x300 ? 100 : 10),
+                          (option & 0x220 ? "full" : "half"));
+               mdio_write(dev, np->phys[0], 0,
+                                  ((option & 0x300) ? 0x2000 : 0) |    /* 
100mbps? */
+                                  ((option & 0x220) ? 0x0100 : 0)); /* Full 
duplex? */
        }
-#ifdef MODULE
-       if (card_idx >= 0) {
-               if (full_duplex[card_idx] >= 0)
-                       tp->full_duplex = full_duplex[card_idx];
-       }
-#endif
 
-       /* The Rtl8129-specific entries in the device structure. */
+       /* The rtl81x9-specific entries in the device structure. */
        dev->open = &rtl8129_open;
        dev->hard_start_xmit = &rtl8129_start_xmit;
        dev->stop = &rtl8129_close;
        dev->get_stats = &rtl8129_get_stats;
        dev->set_multicast_list = &set_rx_mode;
+       dev->do_ioctl = &mii_ioctl;
 
        return dev;
 }
@@ -485,48 +562,43 @@
 #define EE_ENB                 (0x80 | EE_CS)
 
 /* Delay between EEPROM clock transitions.
-   No extra delay is needed with 33Mhz PCI, but 66Mhz is untested.
+   No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
  */
 
-#ifdef _LINUX_DELAY_H
-#define eeprom_delay(nanosec)  udelay(1)
-#else
-#define eeprom_delay(nanosec)  do { ; } while (0)
-#endif
+#define eeprom_delay() inl(ee_addr)
 
 /* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD   (5 << 6)
-#define EE_READ_CMD            (6 << 6)
-#define EE_ERASE_CMD   (7 << 6)
+#define EE_WRITE_CMD   (5)
+#define EE_READ_CMD            (6)
+#define EE_ERASE_CMD   (7)
 
-static int read_eeprom(int ioaddr, int location)
+static int read_eeprom(long ioaddr, int location, int addr_len)
 {
        int i;
        unsigned retval = 0;
-       int ee_addr = ioaddr + Cfg9346;
-       int read_cmd = location | EE_READ_CMD;
+       long ee_addr = ioaddr + Cfg9346;
+       int read_cmd = location | (EE_READ_CMD << addr_len);
 
        outb(EE_ENB & ~EE_CS, ee_addr);
        outb(EE_ENB, ee_addr);
 
        /* Shift the read command bits out. */
-       for (i = 10; i >= 0; i--) {
+       for (i = 4 + addr_len; i >= 0; i--) {
                int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
                outb(EE_ENB | dataval, ee_addr);
-               eeprom_delay(100);
+               eeprom_delay();
                outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
-               eeprom_delay(150);
-               outb(EE_ENB | dataval, ee_addr);        /* Finish EEPROM a 
clock tick. */
-               eeprom_delay(250);
+               eeprom_delay();
        }
        outb(EE_ENB, ee_addr);
+       eeprom_delay();
 
        for (i = 16; i > 0; i--) {
                outb(EE_ENB | EE_SHIFT_CLK, ee_addr);
-               eeprom_delay(100);
+               eeprom_delay();
                retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 
0);
                outb(EE_ENB, ee_addr);
-               eeprom_delay(100);
+               eeprom_delay();
        }
 
        /* Terminate the EEPROM access. */
@@ -544,288 +616,402 @@
 #define MDIO_DATA_OUT  0x04
 #define MDIO_DATA_IN   0x02
 #define MDIO_CLK               0x01
-#ifdef _LINUX_DELAY_H
-#define mdio_delay()   udelay(1) /* Really 400ns. */
-#else
-#define mdio_delay()   do { ; } while (0)
-#endif
+#define MDIO_WRITE0 (MDIO_DIR)
+#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
+
+#define mdio_delay(mdio_addr)  inb(mdio_addr)
+
+static char mii_2_8139_map[8] = {MII_BMCR, MII_BMSR, 0, 0, NWayAdvert,
+                                                                NWayLPAR, 
NWayExpansion, 0 };
 
 /* Syncronize the MII management interface by shifting 32 one bits out. */
-static void mdio_sync(int ioaddr)
+static void mdio_sync(long mdio_addr)
 {
        int i;
-       int mdio_addr = ioaddr + MII_SMI;
 
        for (i = 32; i >= 0; i--) {
-               outb(MDIO_DIR | MDIO_DATA_OUT, mdio_addr);
-               mdio_delay();
-               outb(MDIO_DIR | MDIO_DATA_OUT | MDIO_CLK, mdio_addr);
-               mdio_delay();
+               outb(MDIO_WRITE1, mdio_addr);
+               mdio_delay(mdio_addr);
+               outb(MDIO_WRITE1 | MDIO_CLK, mdio_addr);
+               mdio_delay(mdio_addr);
        }
        return;
 }
-static int mdio_read(int ioaddr, int phy_id, int location)
+static int mdio_read(struct net_device *dev, int phy_id, int location)
 {
-       int i;
-       int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+       long mdio_addr = dev->base_addr + MII_SMI;
+       int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
        int retval = 0;
-       int mdio_addr = ioaddr + MII_SMI;
+       int i;
 
-       mdio_sync(ioaddr);
+       if (phy_id > 31) {      /* Really a 8139.  Use internal registers. */
+               return location < 8 && mii_2_8139_map[location] ?
+                       inw(dev->base_addr + mii_2_8139_map[location]) : 0;
+       }
+       mdio_sync(mdio_addr);
        /* Shift the read command bits out. */
        for (i = 15; i >= 0; i--) {
-               int dataval =
-                 (read_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
+               int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
 
                outb(MDIO_DIR | dataval, mdio_addr);
-               mdio_delay();
+               mdio_delay(mdio_addr);
                outb(MDIO_DIR | dataval | MDIO_CLK, mdio_addr);
-               mdio_delay();
+               mdio_delay(mdio_addr);
        }
 
        /* Read the two transition, 16 data, and wire-idle bits. */
        for (i = 19; i > 0; i--) {
                outb(0, mdio_addr);
-               mdio_delay();
+               mdio_delay(mdio_addr);
                retval = (retval << 1) | ((inb(mdio_addr) & MDIO_DATA_IN) ? 1 : 
0);
                outb(MDIO_CLK, mdio_addr);
-               mdio_delay();
+               mdio_delay(mdio_addr);
        }
        return (retval>>1) & 0xffff;
 }
-
-static int
-rtl8129_open(struct device *dev)
+
+static void mdio_write(struct net_device *dev, int phy_id, int location,
+                                          int value)
 {
-       struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-       int ioaddr = dev->base_addr;
+       long mdio_addr = dev->base_addr + MII_SMI;
+       int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
        int i;
-       int full_duplex = 0;
 
-       /* Soft reset the chip. */
-       outb(CmdReset, ioaddr + ChipCmd);
+       if (phy_id > 31) {      /* Really a 8139.  Use internal registers. */
+               long ioaddr = dev->base_addr;
+               if (location == 0) {
+                       outb(0xC0, ioaddr + Cfg9346);
+                       outw(value, ioaddr + MII_BMCR);
+                       outb(0x00, ioaddr + Cfg9346);
+               } else if (location < 8  &&  mii_2_8139_map[location])
+                       outw(value, ioaddr + mii_2_8139_map[location]);
+               return;
+       }
+       mdio_sync(mdio_addr);
+
+       /* Shift the command bits out. */
+       for (i = 31; i >= 0; i--) {
+               int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+               outb(dataval, mdio_addr);
+               mdio_delay(mdio_addr);
+               outb(dataval | MDIO_CLK, mdio_addr);
+               mdio_delay(mdio_addr);
+       }
+       /* Clear out extra bits. */
+       for (i = 2; i > 0; i--) {
+               outb(0, mdio_addr);
+               mdio_delay(mdio_addr);
+               outb(MDIO_CLK, mdio_addr);
+               mdio_delay(mdio_addr);
+       }
+       return;
+}
 
+
+static int rtl8129_open(struct net_device *dev)
+{
+       struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int rx_buf_len_idx;
+
+       MOD_INC_USE_COUNT;
        if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ, dev->name, 
dev)) {
+               MOD_DEC_USE_COUNT;
                return -EAGAIN;
        }
 
-       MOD_INC_USE_COUNT;
+       /* The Rx ring allocation size is 2^N + delta, which is worst-case for
+          the kernel binary-buddy allocation.  We allocate the Tx bounce 
buffers
+          at the same time to use some of the otherwise wasted space.
+          The delta of +16 is required for dribble-over because the receiver 
does
+          not wrap when the packet terminates just beyond the end of the ring. 
*/
+       rx_buf_len_idx = RX_BUF_LEN_IDX;
+       do {
+               tp->rx_buf_len = 8192 << rx_buf_len_idx;
+               tp->rx_ring = kmalloc(tp->rx_buf_len + 16 +
+                                                         (TX_BUF_SIZE * 
NUM_TX_DESC), GFP_KERNEL);
+       } while (tp->rx_ring == NULL  &&  --rx_buf_len_idx >= 0);
 
-       tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL);
-       tp->rx_ring = kmalloc(RX_BUF_LEN + 16, GFP_KERNEL);
-       if (tp->tx_bufs == NULL ||  tp->rx_ring == NULL) {
-               free_irq (dev->irq, dev);
-               if (tp->tx_bufs)
-                       kfree(tp->tx_bufs);
-               if (rtl8129_debug > 0)
+       if (tp->rx_ring == NULL) {
+               if (debug > 0)
                        printk(KERN_ERR "%s: Couldn't allocate a %d byte 
receive ring.\n",
-                                  dev->name, RX_BUF_LEN);
+                                  dev->name, tp->rx_buf_len);
+               MOD_DEC_USE_COUNT;
                return -ENOMEM;
        }
+       tp->tx_bufs = tp->rx_ring + tp->rx_buf_len + 16;
+
        rtl8129_init_ring(dev);
+       tp->full_duplex = tp->duplex_lock;
+       tp->tx_flag = (TX_FIFO_THRESH<<11) & 0x003f0000;
+       tp->rx_config =
+               (RX_FIFO_THRESH << 13) | (rx_buf_len_idx << 11) | 
(RX_DMA_BURST<<8);
+
+       rtl_hw_start(dev);
+       netif_start_tx_queue(dev);
+
+       if (tp->msg_level & NETIF_MSG_IFUP)
+               printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %#lx IRQ %d"
+                          " GP Pins %2.2x %s-duplex.\n",
+                          dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData),
+                          tp->full_duplex ? "full" : "half");
 
+       /* Set the timer to switch to check for link beat and perhaps switch
+          to an alternate media type. */
+       init_timer(&tp->timer);
+       tp->timer.expires = jiffies + 3*HZ;
+       tp->timer.data = (unsigned long)dev;
+       tp->timer.function = &rtl8129_timer;
+       add_timer(&tp->timer);
+
+       return 0;
+}
+
+/* Start the hardware at open or resume. */
+static void rtl_hw_start(struct net_device *dev)
+{
+       struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int i;
+
+       /* Soft reset the chip. */
+       outb(CmdReset, ioaddr + ChipCmd);
        /* Check that the chip has finished the reset. */
        for (i = 1000; i > 0; i--)
                if ((inb(ioaddr + ChipCmd) & CmdReset) == 0)
                        break;
+       /* Restore our idea of the MAC address. */
+       outb(0xC0, ioaddr + Cfg9346);
+       outl(cpu_to_le32(*(u32*)(dev->dev_addr + 0)), ioaddr + MAC0 + 0);
+       outl(cpu_to_le32(*(u32*)(dev->dev_addr + 4)), ioaddr + MAC0 + 4);
 
-       for (i = 0; i < 6; i++)
-               outb(dev->dev_addr[i], ioaddr + MAC0 + i);
+       /* Hmmm, do these belong here? */
+       tp->cur_rx = 0;
 
        /* Must enable Tx/Rx before setting transfer thresholds! */
        outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
-       outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | 
(RX_DMA_BURST<<8),
-                ioaddr + RxConfig);
-       outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig);
-
-       full_duplex = tp->full_duplex;
-       if (tp->phys[0] >= 0  ||  tp->chip_id == 0x8139) {
-               u16 mii_reg5;
-               if (tp->chip_id == 0x8139)
-                       mii_reg5 = inw(ioaddr + NWayLPAR);
-               else
-                       mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
+       outl(tp->rx_config, ioaddr + RxConfig);
+       /* Check this value: the documentation contradicts ifself.  Is the
+          IFG correct with bit 28:27 zero, or with |0x03000000 ? */
+       outl((TX_DMA_BURST<<8), ioaddr + TxConfig);
+
+       /* This is check_duplex() */
+       if (tp->phys[0] >= 0  ||  (tp->drv_flags & HAS_MII_XCVR)) {
+               u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5);
                if (mii_reg5 == 0xffff)
                        ;                                       /* Not there */
                else if ((mii_reg5 & 0x0100) == 0x0100
                                 || (mii_reg5 & 0x00C0) == 0x0040)
-                       full_duplex = 1;
-               if (rtl8129_debug > 1)
+                       tp->full_duplex = 1;
+               if (tp->msg_level & NETIF_MSG_LINK)
                        printk(KERN_INFO"%s: Setting %s%s-duplex based on"
                                   " auto-negotiated partner ability %4.4x.\n", 
dev->name,
                                   mii_reg5 == 0 ? "" :
                                   (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ",
-                                  full_duplex ? "full" : "half", mii_reg5);
+                                  tp->full_duplex ? "full" : "half", mii_reg5);
        }
 
-       outb(0xC0, ioaddr + Cfg9346);
-       outb(full_duplex ? 0x60 : 0x20, ioaddr + Config1);
+       if (tp->drv_flags & HAS_MII_XCVR)                       /* rtl8129 chip 
*/
+               outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1);
        outb(0x00, ioaddr + Cfg9346);
 
        outl(virt_to_bus(tp->rx_ring), ioaddr + RxBuf);
-
        /* Start the chip's Tx and Rx process. */
        outl(0, ioaddr + RxMissed);
        set_rx_mode(dev);
-
        outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
-
-       dev->tbusy = 0;
-       dev->interrupt = 0;
-       dev->start = 1;
-
        /* Enable all known interrupts by setting the interrupt mask. */
        outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
-               | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask);
-
-       if (rtl8129_debug > 1)
-               printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %4.4x IRQ %d"
-                          " GP Pins %2.2x %s-duplex.\n",
-                          dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData),
-                          full_duplex ? "full" : "half");
-
-       /* Set the timer to switch to check for link beat and perhaps switch
-          to an alternate media type. */
-       init_timer(&tp->timer);
-       tp->timer.expires = RUN_AT((24*HZ)/10);                 /* 2.4 sec. */
-       tp->timer.data = (unsigned long)dev;
-       tp->timer.function = &rtl8129_timer;                            /* 
timer handler */
-       add_timer(&tp->timer);
+                | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask);
 
-       return 0;
 }
 
 static void rtl8129_timer(unsigned long data)
 {
-       struct device *dev = (struct device *)data;
-       struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-       int ioaddr = dev->base_addr;
-       int next_tick = 0;
-
-       if (tp->chip_id == 0x8139) {
-               u16 mii_reg5 = inw(ioaddr + NWayLPAR);
-               if ((mii_reg5 & 0x0100) == 0x0100
-                       || (mii_reg5 & 0x00C0) == 0x0040)
-                       if ( ! tp->full_duplex) {
-                               tp->full_duplex = 1;
-                               if (rtl8129_debug > 0)
-                                       printk(KERN_INFO "%s: Switching to 
full-duplex based on "
-                                                  "link partner ability of 
%4.4x.\n",
-                                                  dev->name, mii_reg5);
+       struct net_device *dev = (struct net_device *)data;
+       struct rtl8129_private *np = (struct rtl8129_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int next_tick = 60*HZ;
+       int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+
+       if (! np->duplex_lock  &&  mii_reg5 != 0xffff) {
+               int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+               if (np->full_duplex != duplex) {
+                       np->full_duplex = duplex;
+                       printk(KERN_INFO "%s: Using %s-duplex based on MII #%d 
link"
+                                  " partner ability of %4.4x.\n", dev->name,
+                                  np->full_duplex ? "full" : "half", 
np->phys[0], mii_reg5);
+                       if (np->drv_flags & HAS_MII_XCVR) {
                                outb(0xC0, ioaddr + Cfg9346);
-                               outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + 
Config1);
+                               outb(np->full_duplex ? 0x60 : 0x20, ioaddr + 
Config1);
                                outb(0x00, ioaddr + Cfg9346);
                        }
+               }
        }
-       if (rtl8129_debug > 2) {
-               if (tp->chip_id == 0x8129)
+#if LINUX_VERSION_CODE < 0x20300
+       /* Check for bogusness. */
+       if (inw(ioaddr + IntrStatus) & (TxOK | RxOK)) {
+               int status = inw(ioaddr + IntrStatus);                  /* 
Double check */
+               if (status & (TxOK | RxOK)  &&  ! dev->interrupt) {
+                       printk(KERN_ERR "%s: RTL8139 Interrupt line blocked, 
status %x.\n",
+                                  dev->name, status);
+                       rtl8129_interrupt(dev->irq, dev, 0);
+               }
+       }
+       if (dev->tbusy  &&  jiffies - dev->trans_start >= 2*TX_TIMEOUT)
+               rtl8129_tx_timeout(dev);
+#else
+       if (netif_queue_paused(dev)  &&
+               np->cur_tx - np->dirty_tx > 1  &&
+               (jiffies - dev->trans_start) > TX_TIMEOUT) {
+               rtl8129_tx_timeout(dev);
+       }
+#endif
+
+#if defined(RTL_TUNE_TWISTER)
+       /* This is a complicated state machine to configure the "twister" for
+          impedance/echos based on the cable length.
+          All of this is magic and undocumented.
+          */
+       if (np->twistie) switch(np->twistie) {
+       case 1: {
+               if (inw(ioaddr + CSCR) & CSCR_LinkOKBit) {
+                       /* We have link beat, let us tune the twister. */
+                       outw(CSCR_LinkDownOffCmd, ioaddr + CSCR);
+                       np->twistie = 2;        /* Change to state 2. */
+                       next_tick = HZ/10;
+               } else {
+                       /* Just put in some reasonable defaults for when beat 
returns. */
+                       outw(CSCR_LinkDownCmd, ioaddr + CSCR);
+                       outl(0x20,ioaddr + FIFOTMS);    /* Turn on cable test 
mode. */
+                       outl(PARA78_default ,ioaddr + PARA78);
+                       outl(PARA7c_default ,ioaddr + PARA7c);
+                       np->twistie = 0;        /* Bail from future actions. */
+               }
+       } break;
+       case 2: {
+               /* Read how long it took to hear the echo. */
+               int linkcase = inw(ioaddr + CSCR) & CSCR_LinkStatusBits;
+               if (linkcase == 0x7000) np->twist_row = 3;
+               else if (linkcase == 0x3000) np->twist_row = 2;
+               else if (linkcase == 0x1000) np->twist_row = 1;
+               else np->twist_row = 0;
+               np->twist_col = 0;
+               np->twistie = 3;        /* Change to state 2. */
+               next_tick = HZ/10;
+       } break;
+       case 3: {
+               /* Put out four tuning parameters, one per 100msec. */
+               if (np->twist_col == 0) outw(0, ioaddr + FIFOTMS);
+               outl(param[(int)np->twist_row][(int)np->twist_col], ioaddr + 
PARA7c);
+               next_tick = HZ/10;
+               if (++np->twist_col >= 4) {
+                       /* For short cables we are done.
+                          For long cables (row == 3) check for mistune. */
+                       np->twistie = (np->twist_row == 3) ? 4 : 0;
+               }
+       } break;
+       case 4: {
+               /* Special case for long cables: check for mistune. */
+               if ((inw(ioaddr + CSCR) & CSCR_LinkStatusBits) == 0x7000) {
+                       np->twistie = 0;
+                       break;
+               } else {
+                       outl(0xfb38de03, ioaddr + PARA7c);
+                       np->twistie = 5;
+                       next_tick = HZ/10;
+               }
+       } break;
+       case 5: {
+               /* Retune for shorter cable (column 2). */
+               outl(0x20,ioaddr + FIFOTMS);
+               outl(PARA78_default,  ioaddr + PARA78);
+               outl(PARA7c_default,  ioaddr + PARA7c);
+               outl(0x00,ioaddr + FIFOTMS);
+               np->twist_row = 2;
+               np->twist_col = 0;
+               np->twistie = 3;
+               next_tick = HZ/10;
+       } break;
+       }
+#endif
+
+       if (np->msg_level & NETIF_MSG_TIMER) {
+               if (np->drv_flags & HAS_MII_XCVR)
                        printk(KERN_DEBUG"%s: Media selection tick, GP pins 
%2.2x.\n",
                                   dev->name, inb(ioaddr + GPPinData));
                else
                        printk(KERN_DEBUG"%s: Media selection tick, Link 
partner %4.4x.\n",
                                   dev->name, inw(ioaddr + NWayLPAR));
-               printk(KERN_DEBUG"%s:  Other registers are IntMask %4.4x 
IntStatus %4.4x"
-                          " RxStatus %4.4x.\n",
+               printk(KERN_DEBUG"%s:  Other registers are IntMask %4.4x "
+                          "IntStatus %4.4x RxStatus %4.4x.\n",
                           dev->name, inw(ioaddr + IntrMask), inw(ioaddr + 
IntrStatus),
-                          inl(ioaddr + RxEarlyStatus));
+                          (int)inl(ioaddr + RxEarlyStatus));
                printk(KERN_DEBUG"%s:  Chip config %2.2x %2.2x.\n",
                           dev->name, inb(ioaddr + Config0), inb(ioaddr + 
Config1));
        }
 
-       if (next_tick) {
-               tp->timer.expires = RUN_AT(next_tick);
-               add_timer(&tp->timer);
-       }
+       np->timer.expires = jiffies + next_tick;
+       add_timer(&np->timer);
 }
 
-static void rtl8129_tx_timeout(struct device *dev)
+static void rtl8129_tx_timeout(struct net_device *dev)
 {
        struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-       int ioaddr = dev->base_addr;
-       int i;
+       long ioaddr = dev->base_addr;
+       int status = inw(ioaddr + IntrStatus);
+       int mii_reg, i;
+
+       /* Could be wrapped with if (tp->msg_level & NETIF_MSG_TX_ERR) */
+       printk(KERN_ERR "%s: Transmit timeout, status %2.2x %4.4x "
+                  "media %2.2x.\n",
+                  dev->name, inb(ioaddr + ChipCmd), status, inb(ioaddr + 
GPPinData));
+
+       if (status & (TxOK | RxOK)) {
+               printk(KERN_ERR "%s: RTL8139 Interrupt line blocked, status 
%x.\n",
+                          dev->name, status);
+       }
 
        /* Disable interrupts by clearing the interrupt mask. */
        outw(0x0000, ioaddr + IntrMask);
-
-       if (rtl8129_debug > 0)
-               printk(KERN_WARNING "%s: Transmit timeout, status %2.2x 
%4.4x.\n",
-                          dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + 
IntrStatus));
        /* Emit info to figure out what went wrong. */
+       printk(KERN_DEBUG "%s: Tx queue start entry %d  dirty entry %d%s.\n",
+                  dev->name, tp->cur_tx, tp->dirty_tx, tp->tx_full ? ", full" 
: "");
        for (i = 0; i < NUM_TX_DESC; i++)
-               printk(KERN_DEBUG"%s:  Tx descriptor %d is %8.8x.%s\n",
-                          dev->name, i, inl(ioaddr + TxStat0 + i*4),
+               printk(KERN_DEBUG "%s:  Tx descriptor %d is %8.8x.%s\n",
+                          dev->name, i, (int)inl(ioaddr + TxStatus0 + i*4),
                           i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : 
"");
-       if (tp->chip_id == 0x8129) {
-               int mii_reg;
-               printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, 
tp->phys[0]);
-               for (mii_reg = 0; mii_reg < 8; mii_reg++)
-                       printk(" %4.4x", mdio_read(ioaddr, tp->phys[0], 
mii_reg));
-               printk(".\n");
-       } else {
-               printk(KERN_DEBUG"%s: MII status register is %4.4x.\n",
-                          dev->name, inw(ioaddr + BMSR));
-       }
-
-       /* Soft reset the chip. */
-       outb(CmdReset, ioaddr + ChipCmd);
-       for (i = 0; i < 6; i++)
-               outb(dev->dev_addr[i], ioaddr + MAC0 + i);
-
-       {                                                       /* Save the 
unsent Tx packets. */
-               struct sk_buff *saved_skb[NUM_TX_DESC], *skb;
-               int j = 0;
-               for (; tp->cur_tx - tp->dirty_tx > 0 ; tp->dirty_tx++)
-                       saved_skb[j++] = tp->tx_skbuff[tp->dirty_tx % 
NUM_TX_DESC];
-               tp->dirty_tx = tp->cur_tx = 0;
-
-               for (i = 0; i < j; i++) {
-                       skb = tp->tx_skbuff[i] = saved_skb[i];
-                       if ((long)skb->data & 3) {              /* Must use 
alignment buffer. */
-                               memcpy(tp->tx_buf[i], skb->data, skb->len);
-                               outl(virt_to_bus(tp->tx_buf[i]), ioaddr + 
TxAddr0 + i*4);
-                       } else
-                               outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + 
i*4);
-                       /* Note: the chip doesn't have auto-pad! */
-                       outl(((TX_FIFO_THRESH<<11) & 0x003f0000) |
-                                (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
-                                ioaddr + TxStat0 + i*4);
-               }
-               tp->cur_tx = i;
-               while (i < NUM_TX_DESC)
+       printk(KERN_DEBUG "%s: MII #%d registers are:", dev->name, tp->phys[0]);
+       for (mii_reg = 0; mii_reg < 8; mii_reg++)
+               printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg));
+       printk(".\n");
+
+       /* Stop a shared interrupt from scavenging while we are. */
+       tp->dirty_tx = tp->cur_tx = 0;
+       /* Dump the unsent Tx packets. */
+       for (i = 0; i < NUM_TX_DESC; i++) {
+               if (tp->tx_skbuff[i]) {
+                       dev_free_skb(tp->tx_skbuff[i]);
                        tp->tx_skbuff[i] = 0;
-               if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
-                       dev->tbusy = 0;
-               } else {
-                       tp->tx_full = 1;
+                       tp->stats.tx_dropped++;
                }
        }
-
-       /* Must enable Tx/Rx before setting transfer thresholds! */
-       set_rx_mode(dev);
-       outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
-       outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | 
(RX_DMA_BURST<<8),
-                ioaddr + RxConfig);
-       outl((TX_DMA_BURST<<8), ioaddr + TxConfig);
-
-       dev->trans_start = jiffies;
-       tp->stats.tx_errors++;
-       /* Enable all known interrupts by setting the interrupt mask. */
-       outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
-                | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask);
+       rtl_hw_start(dev);
+       netif_unpause_tx_queue(dev);
+       tp->tx_full = 0;
        return;
 }
 
 
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 static void
-rtl8129_init_ring(struct device *dev)
+rtl8129_init_ring(struct net_device *dev)
 {
        struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
        int i;
 
        tp->tx_full = 0;
-       tp->cur_rx = tp->cur_tx = 0;
-       tp->dirty_rx = tp->dirty_tx = 0;
+       tp->dirty_tx = tp->cur_tx = 0;
 
        for (i = 0; i < NUM_TX_DESC; i++) {
                tp->tx_skbuff[i] = 0;
@@ -834,18 +1020,16 @@
 }
 
 static int
-rtl8129_start_xmit(struct sk_buff *skb, struct device *dev)
+rtl8129_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-       int ioaddr = dev->base_addr;
+       long ioaddr = dev->base_addr;
        int entry;
 
-       /* Block a timer-based transmit from overlapping.  This could better be
-          done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
-       if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
-               if (jiffies - dev->trans_start < TX_TIMEOUT)
-                       return 1;
-               rtl8129_tx_timeout(dev);
+       if (netif_pause_tx_queue(dev) != 0) {
+               /* This watchdog code is redundant with the media monitor 
timer. */
+               if (jiffies - dev->trans_start > TX_TIMEOUT)
+                       rtl8129_tx_timeout(dev);
                return 1;
        }
 
@@ -859,20 +1043,26 @@
        } else
                outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + entry*4);
        /* Note: the chip doesn't have auto-pad! */
-       outl(((TX_FIFO_THRESH<<11) & 0x003f0000) |
-                (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
-                ioaddr + TxStat0 + entry*4);
+       outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),
+                ioaddr + TxStatus0 + entry*4);
 
-       if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
-               dev->tbusy = 0;
-       } else {
-               tp->tx_full = 1;
-       }
+       /* There is a race condition here -- we might read dirty_tx, take an
+          interrupt that clears the Tx queue, and only then set tx_full.
+          So we do this in two phases. */
+       if (++tp->cur_tx - tp->dirty_tx >= NUM_TX_DESC) {
+               set_bit(0, &tp->tx_full);
+               if (tp->cur_tx - (volatile unsigned int)tp->dirty_tx < 
NUM_TX_DESC) {
+                       clear_bit(0, &tp->tx_full);
+                       netif_unpause_tx_queue(dev);
+               } else
+                       netif_stop_tx_queue(dev);
+       } else
+               netif_unpause_tx_queue(dev);
 
        dev->trans_start = jiffies;
-       if (rtl8129_debug > 4)
-               printk(KERN_DEBUG"%s: Queued Tx packet at %p size %ld to slot 
%d.\n",
-                          dev->name, skb->data, skb->len, entry);
+       if (tp->msg_level & NETIF_MSG_TX_QUEUED)
+               printk(KERN_DEBUG"%s: Queued Tx packet at %p size %d to slot 
%d.\n",
+                          dev->name, skb->data, (int)skb->len, entry);
 
        return 0;
 }
@@ -881,31 +1071,32 @@
    after the Tx thread. */
 static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs 
*regs)
 {
-       struct device *dev = (struct device *)dev_instance;
-       struct rtl8129_private *tp;
-       int ioaddr, boguscnt = max_interrupt_work;
-       int status;
-
-       if (dev == NULL) {
-               printk (KERN_ERR"rtl8139_interrupt(): IRQ %d for unknown 
device.\n",
-                               irq);
-               return;
-       }
-
-       ioaddr = dev->base_addr;
-       tp = (struct rtl8129_private *)dev->priv;
-       if (test_and_set_bit(0, (void*)&tp->in_interrupt)) {
-               printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", 
dev->name);
+       struct net_device *dev = (struct net_device *)dev_instance;
+       struct rtl8129_private *np = (struct rtl8129_private *)dev->priv;
+       struct rtl8129_private *tp = np;
+       int boguscnt = np->max_interrupt_work;
+       long ioaddr = dev->base_addr;
+       int link_changed = 0;           /* Grrr, avoid bogus "uninitialized" 
warning */
+
+#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x20123
+       /* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+       if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+               printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt 
handler.\n",
+                          dev->name);
+               dev->interrupt = 0;     /* Avoid halting machine. */
                return;
        }
-       dev->interrupt = 1;
+#endif
 
        do {
-               status = inw(ioaddr + IntrStatus);
-               /* Acknowledge all of the current interrupt sources ASAP. */
+               int status = inw(ioaddr + IntrStatus);
+               /* Acknowledge all of the current interrupt sources ASAP, but
+                  an first get an additional status bit from CSCR. */
+               if (status & RxUnderrun)
+                       link_changed = inw(ioaddr+CSCR) & CSCR_LinkChangeBit;
                outw(status, ioaddr + IntrStatus);
 
-               if (rtl8129_debug > 4)
+               if (tp->msg_level & NETIF_MSG_INTR)
                        printk(KERN_DEBUG"%s: interrupt  status=%#4.4x new 
intstat=%#4.4x.\n",
                                   dev->name, status, inw(ioaddr + IntrStatus));
 
@@ -917,27 +1108,25 @@
                        rtl8129_rx(dev);
 
                if (status & (TxOK | TxErr)) {
-                       unsigned int dirty_tx;
+                       unsigned int dirty_tx = tp->dirty_tx;
 
-                       for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; 
dirty_tx++) {
+                       while (tp->cur_tx - dirty_tx > 0) {
                                int entry = dirty_tx % NUM_TX_DESC;
-                               int txstatus = inl(ioaddr + TxStat0 + entry*4);
+                               int txstatus = inl(ioaddr + TxStatus0 + 
entry*4);
 
-                               if ( ! (txstatus & TxHostOwns))
+                               if ( ! (txstatus & (TxStatOK | TxUnderrun | 
TxAborted)))
                                        break;                  /* It still 
hasn't been Txed */
 
                                /* Note: TxCarrierLost is always asserted at 
100mbps. */
                                if (txstatus & (TxOutOfWindow | TxAborted)) {
                                        /* There was an major error, log it. */
-#ifndef final_version
-                                       if (rtl8129_debug > 1)
+                                       if (tp->msg_level & NETIF_MSG_TX_ERR)
                                                printk(KERN_NOTICE"%s: Transmit 
error, Tx status %8.8x.\n",
                                                           dev->name, txstatus);
-#endif
                                        tp->stats.tx_errors++;
                                        if (txstatus&TxAborted) {
                                                tp->stats.tx_aborted_errors++;
-                                               
outl((TX_DMA_BURST<<8)|0x03000001, ioaddr + TxConfig);
+                                               outl(TX_DMA_BURST << 8, ioaddr 
+ TxConfig);
                                        }
                                        if (txstatus&TxCarrierLost) 
tp->stats.tx_carrier_errors++;
                                        if (txstatus&TxOutOfWindow) 
tp->stats.tx_window_errors++;
@@ -946,11 +1135,13 @@
                                                tp->stats.collisions16++;
 #endif
                                } else {
-#ifdef ETHER_STATS
-                                       /* No count for tp->stats.tx_deferred */
-#endif
+                                       if (tp->msg_level & NETIF_MSG_TX_DONE)
+                                               printk(KERN_DEBUG "%s: Transmit 
done, Tx status"
+                                                          " %8.8x.\n", 
dev->name, txstatus);
                                        if (txstatus & TxUnderrun) {
-                                               /* Todo: increase the Tx FIFO 
threshold. */
+                                               /* Add 64 to the Tx FIFO 
threshold. */
+                                               if (tp->tx_flag <  0x00300000)
+                                                       tp->tx_flag += 
0x00020000;
                                                tp->stats.tx_fifo_errors++;
                                        }
                                        tp->stats.collisions += (txstatus >> 
24) & 15;
@@ -961,48 +1152,34 @@
                                }
 
                                /* Free the original skb. */
-                               dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE);
+                               dev_free_skb_irq(tp->tx_skbuff[entry]);
                                tp->tx_skbuff[entry] = 0;
+                               if (test_bit(0, &tp->tx_full)) {
+                                       /* The ring is no longer full, clear 
tbusy. */
+                                       clear_bit(0, &tp->tx_full);
+                                       netif_resume_tx_queue(dev);
+                               }
+                               dirty_tx++;
                        }
 
 #ifndef final_version
                        if (tp->cur_tx - dirty_tx > NUM_TX_DESC) {
                                printk(KERN_ERR"%s: Out-of-sync dirty pointer, 
%d vs. %d, full=%d.\n",
-                                          dev->name, dirty_tx, tp->cur_tx, 
tp->tx_full);
+                                          dev->name, dirty_tx, tp->cur_tx, 
(int)tp->tx_full);
                                dirty_tx += NUM_TX_DESC;
                        }
 #endif
-
-                       if (tp->tx_full && dev->tbusy
-                               && dirty_tx > tp->cur_tx - NUM_TX_DESC) {
-                               /* The ring is no longer full, clear tbusy. */
-                               tp->tx_full = 0;
-                               dev->tbusy = 0;
-                               mark_bh(NET_BH);
-                       }
-
                        tp->dirty_tx = dirty_tx;
                }
 
                /* Check uncommon events with one test. */
                if (status & (PCIErr|PCSTimeout 
|RxUnderrun|RxOverflow|RxFIFOOver
                                          |TxErr|RxErr)) {
-                       /* Update the error count. */
-                       tp->stats.rx_missed_errors += inl(ioaddr + RxMissed);
-                       outl(0, ioaddr + RxMissed);
-
-                       if (status & (RxUnderrun | RxOverflow | RxErr | 
RxFIFOOver))
-                               tp->stats.rx_errors++;
-
-                       if (status & (PCSTimeout)) tp->stats.rx_length_errors++;
-                       if (status & (RxUnderrun|RxFIFOOver)) 
tp->stats.rx_fifo_errors++;
-                       if (status & RxOverflow) {
-                               tp->stats.rx_over_errors++;
-                               tp->cur_rx = inw(ioaddr + RxBufAddr) % 
RX_BUF_LEN;
-                               outw(tp->cur_rx - 16, ioaddr + RxBufPtr);
-                       }
-                       /* Error sources cleared above. */
+                       if (status == 0xffff)                   /* Missing 
chip! */
+                               break;
+                       rtl_error(dev, status, link_changed);
                }
+
                if (--boguscnt < 0) {
                        printk(KERN_WARNING"%s: Too much work at interrupt, "
                                   "IntrStatus=0x%4.4x.\n",
@@ -1013,55 +1190,65 @@
                }
        } while (1);
 
-       if (rtl8129_debug > 3)
+       if (tp->msg_level & NETIF_MSG_INTR)
                printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n",
-                          dev->name, inl(ioaddr + IntrStatus));
+                          dev->name, inw(ioaddr + IntrStatus));
 
-       dev->interrupt = 0;
-       clear_bit(0, (void*)&tp->in_interrupt);
+#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x20123
+       clear_bit(0, (void*)&dev->interrupt);
+#endif
        return;
 }
 
 /* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
    field alignments and semantics. */
-static int
-rtl8129_rx(struct device *dev)
+static int rtl8129_rx(struct net_device *dev)
 {
        struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-       int ioaddr = dev->base_addr;
+       long ioaddr = dev->base_addr;
        unsigned char *rx_ring = tp->rx_ring;
        u16 cur_rx = tp->cur_rx;
 
-       if (rtl8129_debug > 4)
+       if (tp->msg_level & NETIF_MSG_RX_STATUS)
                printk(KERN_DEBUG"%s: In rtl8129_rx(), current %4.4x BufAddr 
%4.4x,"
                           " free to %4.4x, Cmd %2.2x.\n",
                           dev->name, cur_rx, inw(ioaddr + RxBufAddr),
                           inw(ioaddr + RxBufPtr), inb(ioaddr + ChipCmd));
 
-       while ((inb(ioaddr + ChipCmd) & 1) == 0) {
-               int ring_offset = cur_rx % RX_BUF_LEN;
-               u32 rx_status = *(u32*)(rx_ring + ring_offset);
-               int rx_size = rx_status >> 16;
+       while ((inb(ioaddr + ChipCmd) & RxBufEmpty) == 0) {
+               int ring_offset = cur_rx % tp->rx_buf_len;
+               u32 rx_status = le32_to_cpu(*(u32*)(rx_ring + ring_offset));
+               int rx_size = rx_status >> 16;                          /* 
Includes the CRC. */
 
-               if (rtl8129_debug > 4) {
+               if (tp->msg_level & NETIF_MSG_RX_STATUS) {
                        int i;
-                       printk(KERN_DEBUG"%s:  rtl8129_rx() status %4.4x, size 
%4.4x, cur %4.4x.\n",
+                       printk(KERN_DEBUG"%s:  rtl8129_rx() status %4.4x, size 
%4.4x,"
+                                  " cur %4.4x.\n",
                                   dev->name, rx_status, rx_size, cur_rx);
                        printk(KERN_DEBUG"%s: Frame contents ", dev->name);
                        for (i = 0; i < 70; i++)
                                printk(" %2.2x", rx_ring[ring_offset + i]);
                        printk(".\n");
                }
-               if (rx_status & RxTooLong) {
-                       if (rtl8129_debug > 0)
-                               printk(KERN_NOTICE"%s: Oversized Ethernet 
frame, status %4.4x!\n",
-                                          dev->name, rx_status);
-                       tp->stats.rx_length_errors++;
-               } else if (rx_status &
-                                  
(RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) {
-                       if (rtl8129_debug > 1)
+               if (rx_status & 
(RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) {
+                       if (tp->msg_level & NETIF_MSG_RX_ERR)
                                printk(KERN_DEBUG"%s: Ethernet frame had 
errors,"
-                                          " status %4.4x.\n", dev->name, 
rx_status);
+                                          " status %8.8x.\n", dev->name, 
rx_status);
+                       if (rx_status == 0xffffffff) {
+                               printk(KERN_NOTICE"%s: Invalid receive status 
at ring "
+                                          "offset %4.4x\n", dev->name, 
ring_offset);
+                               rx_status = 0;
+                       }
+                       if (rx_status & RxTooLong) {
+                               if (tp->msg_level & NETIF_MSG_DRV)
+                                       printk(KERN_NOTICE"%s: Oversized 
Ethernet frame, status"
+                                                  " %4.4x!\n",
+                                                  dev->name, rx_status);
+                               /* A.C.: The chip hangs here.
+                                  This should never occur, which means that we 
are screwed
+                                  when it does.
+                                */
+                       }
                        tp->stats.rx_errors++;
                        if (rx_status & (RxBadSymbol|RxBadAlign))
                                tp->stats.rx_frame_errors++;
@@ -1070,15 +1257,18 @@
                        /* Reset the receiver, based on RealTek recommendation. 
(Bug?) */
                        tp->cur_rx = 0;
                        outb(CmdTxEnb, ioaddr + ChipCmd);
+                       /* A.C.: Reset the multicast list. */
+                       set_rx_mode(dev);
                        outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
-                       outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) |
-                                (RX_DMA_BURST<<8), ioaddr + RxConfig);
                } else {
                        /* Malloc up new buffer, compatible with net-2e. */
                        /* Omit the four octet CRC from the length. */
                        struct sk_buff *skb;
+                       int pkt_size = rx_size - 4;
 
-                       skb = dev_alloc_skb(rx_size + 2);
+                       /* Allocate a common-sized skbuff if we are close. */
+                       skb = dev_alloc_skb(1400 < pkt_size && pkt_size < 
PKT_BUF_SZ-2 ?
+                                                               PKT_BUF_SZ : 
pkt_size + 2);
                        if (skb == NULL) {
                                printk(KERN_WARNING"%s: Memory squeeze, 
deferring packet.\n",
                                           dev->name);
@@ -1089,13 +1279,14 @@
                        }
                        skb->dev = dev;
                        skb_reserve(skb, 2);    /* 16 byte align the IP fields. 
*/
-                       if (ring_offset+rx_size+4 > RX_BUF_LEN) {
-                               int semi_count = RX_BUF_LEN - ring_offset - 4;
+                       if (ring_offset + rx_size > tp->rx_buf_len) {
+                               int semi_count = tp->rx_buf_len - ring_offset - 
4;
+                               /* This could presumably use two calls to 
copy_and_sum()? */
                                memcpy(skb_put(skb, semi_count), 
&rx_ring[ring_offset + 4],
                                           semi_count);
-                               memcpy(skb_put(skb, rx_size-semi_count), 
rx_ring,
-                                          rx_size-semi_count);
-                               if (rtl8129_debug > 4) {
+                               memcpy(skb_put(skb, pkt_size-semi_count), 
rx_ring,
+                                          pkt_size-semi_count);
+                               if (tp->msg_level & NETIF_MSG_PKTDATA) {
                                        int i;
                                        printk(KERN_DEBUG"%s:  Frame wrap @%d",
                                                   dev->name, semi_count);
@@ -1104,22 +1295,23 @@
                                        printk(".\n");
                                        memset(rx_ring, 0xcc, 16);
                                }
-                       } else
-                               memcpy(skb_put(skb, rx_size), 
&rx_ring[ring_offset + 4],
-                                          rx_size);
+                       } else {
+                               eth_copy_and_sum(skb, &rx_ring[ring_offset + 4],
+                                                                pkt_size, 0);
+                               skb_put(skb, pkt_size);
+                       }
                        skb->protocol = eth_type_trans(skb, dev);
                        netif_rx(skb);
 #if LINUX_VERSION_CODE > 0x20119
-                       tp->stats.rx_bytes += rx_size;
+                       tp->stats.rx_bytes += pkt_size;
 #endif
                        tp->stats.rx_packets++;
                }
 
-               cur_rx += rx_size + 4;
-               cur_rx = (cur_rx + 3) & ~3;
+               cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
                outw(cur_rx - 16, ioaddr + RxBufPtr);
        }
-       if (rtl8129_debug > 4)
+       if (tp->msg_level & NETIF_MSG_RX_STATUS)
                printk(KERN_DEBUG"%s: Done rtl8129_rx(), current %4.4x BufAddr 
%4.4x,"
                           " free to %4.4x, Cmd %2.2x.\n",
                           dev->name, cur_rx, inw(ioaddr + RxBufAddr),
@@ -1128,17 +1320,77 @@
        return 0;
 }
 
+/* Error and abnormal or uncommon events handlers. */
+static void rtl_error(struct net_device *dev, int status, int link_changed)
+{
+       struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+
+       if (tp->msg_level & NETIF_MSG_LINK)
+               printk(KERN_NOTICE"%s: Abnormal interrupt, status %8.8x.\n",
+                          dev->name, status);
+
+       /* Update the error count. */
+       tp->stats.rx_missed_errors += inl(ioaddr + RxMissed);
+       outl(0, ioaddr + RxMissed);
+
+       if (status & RxUnderrun){
+               /* This might actually be a link change event. */
+               if ((tp->drv_flags & HAS_LNK_CHNG)  &&  link_changed) {
+                       /* Really link-change on new chips. */
+                       int lpar = inw(ioaddr + NWayLPAR);
+                       int duplex = (lpar&0x0100) || (lpar & 0x01C0) == 0x0040
+                               || tp->duplex_lock;
+                       /* Do not use MII_BMSR as that clears sticky bit. */
+                       if (inw(ioaddr + GPPinData) & 0x0004) {
+                               netif_link_down(dev);
+                       } else
+                               netif_link_up(dev);
+                       if (tp->msg_level & NETIF_MSG_LINK)
+                               printk(KERN_DEBUG "%s: Link changed, link 
partner "
+                                          "%4.4x new duplex %d.\n",
+                                          dev->name, lpar, duplex);
+                       tp->full_duplex = duplex;
+                       /* Only count as errors with no link change. */
+                       status &= ~RxUnderrun;
+               } else {
+                       /* If this does not work, we will do rtl_hw_start(dev); 
*/
+                       outb(CmdTxEnb, ioaddr + ChipCmd);
+                       set_rx_mode(dev);       /* Reset the multicast list. */
+                       outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
+
+                       tp->stats.rx_errors++;
+                       tp->stats.rx_fifo_errors++;
+               }
+       }
+       
+       if (status & (RxOverflow | RxErr | RxFIFOOver)) tp->stats.rx_errors++;
+       if (status & (PCSTimeout)) tp->stats.rx_length_errors++;
+       if (status & RxFIFOOver) tp->stats.rx_fifo_errors++;
+       if (status & RxOverflow) {
+               tp->stats.rx_over_errors++;
+               tp->cur_rx = inw(ioaddr + RxBufAddr) % tp->rx_buf_len;
+               outw(tp->cur_rx - 16, ioaddr + RxBufPtr);
+       }
+       if (status & PCIErr) {
+               u32 pci_cmd_status;
+               pci_read_config_dword(tp->pci_dev, PCI_COMMAND, 
&pci_cmd_status);
+
+               printk(KERN_ERR "%s: PCI Bus error %4.4x.\n",
+                          dev->name, pci_cmd_status);
+       }
+}
+
 static int
-rtl8129_close(struct device *dev)
+rtl8129_close(struct net_device *dev)
 {
-       int ioaddr = dev->base_addr;
+       long ioaddr = dev->base_addr;
        struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
        int i;
 
-       dev->start = 0;
-       dev->tbusy = 1;
+       netif_stop_tx_queue(dev);
 
-       if (rtl8129_debug > 1)
+       if (tp->msg_level & NETIF_MSG_IFDOWN)
                printk(KERN_DEBUG"%s: Shutting down ethercard, status was 
0x%4.4x.\n",
                           dev->name, inw(ioaddr + IntrStatus));
 
@@ -1158,15 +1410,15 @@
 
        for (i = 0; i < NUM_TX_DESC; i++) {
                if (tp->tx_skbuff[i])
-                       dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
+                       dev_free_skb(tp->tx_skbuff[i]);
                tp->tx_skbuff[i] = 0;
        }
        kfree(tp->rx_ring);
-       kfree(tp->tx_bufs);
+       tp->rx_ring = 0;
 
        /* Green! Put the chip in low-power mode. */
        outb(0xC0, ioaddr + Cfg9346);
-       outb(0x03, ioaddr + Config1);
+       outb(tp->config1 | 0x03, ioaddr + Config1);
        outb('H', ioaddr + HltClk);             /* 'R' would leave the clock 
running. */
 
        MOD_DEC_USE_COUNT;
@@ -1174,13 +1426,69 @@
        return 0;
 }
 
-static struct enet_statistics *
-rtl8129_get_stats(struct device *dev)
+/*
+  Handle user-level ioctl() calls.
+  We must use two numeric constants as the key because some clueless person
+  changed value for the symbolic name.
+*/
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct rtl8129_private *np = (struct rtl8129_private *)dev->priv;
+       u16 *data = (u16 *)&rq->ifr_data;
+       u32 *data32 = (void *)&rq->ifr_data;
+
+       switch(cmd) {
+       case 0x8947: case 0x89F0:
+               /* SIOCGMIIPHY: Get the address of the PHY in use. */
+               data[0] = np->phys[0] & 0x3f;
+               /* Fall Through */
+       case 0x8948: case 0x89F1:
+               /* SIOCGMIIREG: Read the specified MII register. */
+               data[3] = mdio_read(dev, data[0], data[1] & 0x1f);
+               return 0;
+       case 0x8949: case 0x89F2:
+               /* SIOCSMIIREG: Write the specified MII register */
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               if (data[0] == np->phys[0]) {
+                       u16 value = data[2];
+                       switch (data[1]) {
+                       case 0:
+                               /* Check for autonegotiation on or reset. */
+                               np->medialock = (value & 0x9000) ? 0 : 1;
+                               if (np->medialock)
+                                       np->full_duplex = (value & 0x0100) ? 1 
: 0;
+                               break;
+                       case 4: np->advertising = value; break;
+                       }
+               }
+               mdio_write(dev, data[0], data[1] & 0x1f, data[2]);
+               return 0;
+       case SIOCGPARAMS:
+               data32[0] = np->msg_level;
+               data32[1] = np->multicast_filter_limit;
+               data32[2] = np->max_interrupt_work;
+               data32[3] = 0;                  /* No rx_copybreak, always 
copy. */
+               return 0;
+       case SIOCSPARAMS:
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+               np->msg_level = data32[0];
+               np->multicast_filter_limit = data32[1];
+               np->max_interrupt_work = data32[2];
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static struct net_device_stats *
+rtl8129_get_stats(struct net_device *dev)
 {
        struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-       int ioaddr = dev->base_addr;
+       long ioaddr = dev->base_addr;
 
-       if (dev->start) {
+       if (netif_running(dev)) {
                tp->stats.rx_missed_errors += inl(ioaddr + RxMissed);
                outl(0, ioaddr + RxMissed);
        }
@@ -1189,100 +1497,236 @@
 }
 
 /* Set or clear the multicast filter for this adaptor.
-   Note that we only use exclusion around actually queueing the
-   new frame, not around filling tp->setup_frame.  This is non-deterministic
-   when re-entered but still correct. */
-
-/* The little-endian AUTODIN II ethernet CRC calculation.
-   N.B. Do not use for bulk data, use a table-based routine instead.
-   This is common code and should be moved to net/core/crc.c */
-static unsigned const ethernet_polynomial_le = 0xedb88320U;
-static inline unsigned ether_crc_le(int length, unsigned char *data)
+   This routine is not state sensitive and need not be SMP locked. */
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
 {
-       unsigned int crc = 0xffffffff;  /* Initial value. */
-       while(--length >= 0) {
+       int crc = -1;
+
+       while (--length >= 0) {
                unsigned char current_octet = *data++;
                int bit;
-               for (bit = 8; --bit >= 0; current_octet >>= 1) {
-                       if ((crc ^ current_octet) & 1) {
-                               crc >>= 1;
-                               crc ^= ethernet_polynomial_le;
-                       } else
-                               crc >>= 1;
-               }
+               for (bit = 0; bit < 8; bit++, current_octet >>= 1)
+                       crc = (crc << 1) ^
+                               ((crc < 0) ^ (current_octet & 1) ? 
ethernet_polynomial : 0);
        }
        return crc;
 }
 
-static void set_rx_mode(struct device *dev)
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+       AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08,
+       AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,
+};
+
+static void set_rx_mode(struct net_device *dev)
 {
-       int ioaddr = dev->base_addr;
        struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv;
-       unsigned char mc_filter[8];              /* Multicast hash filter */
-       int i;
+       long ioaddr = dev->base_addr;
+       u32 mc_filter[2];                /* Multicast hash filter */
+       int i, rx_mode;
+
+       if (tp->msg_level & NETIF_MSG_RXFILTER)
+               printk(KERN_DEBUG"%s:   set_rx_mode(%4.4x) done -- Rx config 
%8.8x.\n",
+                          dev->name, dev->flags, (int)inl(ioaddr + RxConfig));
 
-       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+       /* Note: do not reorder, GCC is clever about common statements. */
+       if (dev->flags & IFF_PROMISC) {
                /* Unconditionally log net taps. */
                printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name);
-               memset(mc_filter, 0xff, sizeof(mc_filter));
-               outb(0x0F, ioaddr + RxConfig);
-       } else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
+               rx_mode = 
AcceptBroadcast|AcceptMulticast|AcceptMyPhys|AcceptAllPhys;
+               mc_filter[1] = mc_filter[0] = 0xffffffff;
+       } else if ((dev->mc_count > tp->multicast_filter_limit)
+                          || (dev->flags & IFF_ALLMULTI)) {
                /* Too many to filter perfectly -- accept all multicasts. */
-               memset(mc_filter, 0xff, sizeof(mc_filter));
-               outb(0x0E, ioaddr + RxConfig);
-       } else if (dev->mc_count == 0) {
-               outb(0x0A, ioaddr + RxConfig);
-               return;
+               rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+               mc_filter[1] = mc_filter[0] = 0xffffffff;
        } else {
                struct dev_mc_list *mclist;
-
-               memset(mc_filter, 0, sizeof(mc_filter));
+               rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+               mc_filter[1] = mc_filter[0] = 0;
                for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
                         i++, mclist = mclist->next)
-                       set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
-                                       mc_filter);
-       }
-       /* ToDo: perhaps we need to stop the Tx and Rx process here? */
-       if (memcmp(mc_filter, tp->mc_filter, sizeof(mc_filter))) {
-               for (i = 0; i < 2; i++)
-                       outl(((u32 *)mc_filter)[i], ioaddr + MAR0 + i*4);
-               memcpy(tp->mc_filter, mc_filter, sizeof(mc_filter));
+                       set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, 
mc_filter);
        }
-       if (rtl8129_debug > 3)
-               printk(KERN_DEBUG"%s:   set_rx_mode(%4.4x) done -- Rx config 
%8.8x.\n",
-                          dev->name, dev->flags, inl(ioaddr + RxConfig));
+       /* We can safely update without stopping the chip. */
+       outl(tp->rx_config | rx_mode, ioaddr + RxConfig);
+       tp->mc_filter[0] = mc_filter[0];
+       tp->mc_filter[1] = mc_filter[1];
+       outl(mc_filter[0], ioaddr + MAR0 + 0);
+       outl(mc_filter[1], ioaddr + MAR0 + 4);
        return;
 }
 
-#ifdef MODULE
 
-/* An additional parameter that may be passed in... */
-static int debug = -1;
+static int rtl_pwr_event(void *dev_instance, int event)
+{
+       struct net_device *dev = dev_instance;
+       struct rtl8129_private *np = (struct rtl8129_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+
+       if (np->msg_level & NETIF_MSG_LINK)
+               printk("%s: Handling power event %d.\n", dev->name, event);
+       switch(event) {
+       case DRV_ATTACH:
+               MOD_INC_USE_COUNT;
+               break;
+       case DRV_SUSPEND:
+               netif_device_detach(dev);
+               /* Disable interrupts, stop Tx and Rx. */
+               outw(0x0000, ioaddr + IntrMask);
+               outb(0x00, ioaddr + ChipCmd);
+               /* Update the error counts. */
+               np->stats.rx_missed_errors += inl(ioaddr + RxMissed);
+               outl(0, ioaddr + RxMissed);
+               break;
+       case DRV_RESUME:
+               netif_device_attach(dev);
+               rtl_hw_start(dev);
+               break;
+       case DRV_DETACH: {
+               struct net_device **devp, **next;
+               if (dev->flags & IFF_UP) {
+                       dev_close(dev);
+                       dev->flags &= ~(IFF_UP|IFF_RUNNING);
+               }
+               unregister_netdev(dev);
+               release_region(dev->base_addr, pci_tbl[np->chip_id].io_size);
+#ifndef USE_IO_OPS
+               iounmap((char *)dev->base_addr);
+#endif
+               for (devp = &root_rtl8129_dev; *devp; devp = next) {
+                       next = &((struct rtl8129_private 
*)(*devp)->priv)->next_module;
+                       if (*devp == dev) {
+                               *devp = *next;
+                               break;
+                       }
+               }
+               if (np->priv_addr)
+                       kfree(np->priv_addr);
+               kfree(dev);
+               MOD_DEC_USE_COUNT;
+               break;
+       }
+       }
 
-int
-init_module(void)
+       return 0;
+}
+
+#ifdef CARDBUS
+
+#include <pcmcia/driver_ops.h>
+
+static dev_node_t *rtl8139_attach(dev_locator_t *loc)
 {
-       int cards_found;
+       struct net_device *dev;
+       u16 dev_id;
+       u32 pciaddr;
+       u8 bus, devfn, irq;
+       long hostaddr;
+       /* Note: the chip index should match the 8139B pci_tbl[] entry. */
+       int chip_idx = 2;
+
+       if (loc->bus != LOC_PCI) return NULL;
+       bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
+       printk(KERN_DEBUG "rtl8139_attach(bus %d, function %d)\n", bus, devfn);
+#ifdef USE_IO_OPS
+       pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &pciaddr);
+       hostaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK;
+#else
+       pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_1, &pciaddr);
+       hostaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK,
+                                                        
pci_tbl[chip_idx].io_size);
+#endif
+       pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
+       pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
+       if (hostaddr == 0 || irq == 0) {
+               printk(KERN_ERR "The %s interface at %d/%d was not assigned an 
%s.\n"
+                          KERN_ERR "  It will not be activated.\n",
+                          pci_tbl[chip_idx].name, bus, devfn,
+                          hostaddr == 0 ? "address" : "IRQ");
+               return NULL;
+       }
+       dev = rtl8139_probe1(pci_find_slot(bus, devfn), NULL,
+                                                hostaddr, irq, chip_idx, 0);
+       if (dev) {
+               dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
+               strcpy(node->dev_name, dev->name);
+               node->major = node->minor = 0;
+               node->next = NULL;
+               MOD_INC_USE_COUNT;
+               return node;
+       }
+       return NULL;
+}
 
-       if (debug >= 0)
-               rtl8129_debug = debug;
+static void rtl8139_detach(dev_node_t *node)
+{
+       struct net_device **devp, **next;
+       printk(KERN_INFO "rtl8139_detach(%s)\n", node->dev_name);
+       for (devp = &root_rtl8129_dev; *devp; devp = next) {
+               next = &((struct rtl8129_private *)(*devp)->priv)->next_module;
+               if (strcmp((*devp)->name, node->dev_name) == 0) break;
+       }
+       if (*devp) {
+               struct rtl8129_private *np =
+                       (struct rtl8129_private *)(*devp)->priv;
+               unregister_netdev(*devp);
+               release_region((*devp)->base_addr, 
pci_tbl[np->chip_id].io_size);
+#ifndef USE_IO_OPS
+               iounmap((char *)(*devp)->base_addr);
+#endif
+               kfree(*devp);
+               if (np->priv_addr)
+                       kfree(np->priv_addr);
+               *devp = *next;
+               kfree(node);
+               MOD_DEC_USE_COUNT;
+       }
+}
 
-       root_rtl8129_dev = NULL;
-       cards_found = rtl8139_probe(0);
+struct driver_operations realtek_ops = {
+       "realtek_cb",
+       rtl8139_attach, /*rtl8139_suspend*/0, /*rtl8139_resume*/0, 
rtl8139_detach
+};
 
-       return cards_found ? 0 : -ENODEV;
+#endif  /* Cardbus support */
+
+#ifdef MODULE
+int init_module(void)
+{
+       if (debug >= NETIF_MSG_DRV)     /* Emit version even if no cards 
detected. */
+               printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+#ifdef CARDBUS
+       register_driver(&realtek_ops);
+       return 0;
+#else
+       return pci_drv_register(&rtl8139_drv_id, NULL);
+#endif
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
-       struct device *next_dev;
+       struct net_device *next_dev;
+
+#ifdef CARDBUS
+       unregister_driver(&realtek_ops);
+#else
+       pci_drv_unregister(&rtl8139_drv_id);
+#endif
 
-       /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
        while (root_rtl8129_dev) {
-               next_dev = ((struct rtl8129_private 
*)root_rtl8129_dev->priv)->next_module;
+               struct rtl8129_private *np = (void *)(root_rtl8129_dev->priv);
                unregister_netdev(root_rtl8129_dev);
-               release_region(root_rtl8129_dev->base_addr, RTL8129_TOTAL_SIZE);
+               release_region(root_rtl8129_dev->base_addr,
+                                          pci_tbl[np->chip_id].io_size);
+#ifndef USE_IO_OPS
+               iounmap((char *)(root_rtl8129_dev->base_addr));
+#endif
+               next_dev = np->next_module;
+               if (np->priv_addr)
+                       kfree(np->priv_addr);
                kfree(root_rtl8129_dev);
                root_rtl8129_dev = next_dev;
        }
@@ -1292,8 +1736,9 @@
 
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 
-c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall 
-Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] 
&& echo -DMODVERSIONS`"
+ *  compile-command: "make KERNVER=`uname -r` rtl8139.o"
+ *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c rtl8139.c"
+ *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -Wall -Wstrict-prototypes 
-O6 -c rtl8139.c -o realtek_cb.o -I/usr/src/pcmcia/include/"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]