[parisc-linux-cvs] new pcnet32.c driver

Helge Deller deller@gmx.de
Mon, 27 Aug 2001 16:15:31 +0200


On Monday 27 August 2001 16:11, Helge Deller wrote:
> CVSROOT:	/home/cvs/parisc
> Module name:	linux
> Changes by:	deller	01/08/27 08:11:33
>
> Modified files:
> 	drivers/net    : pcnet32.c
>
> Log message:
> Made the pcnet32.c driver functional on HP PARISC,
>
> other changes include:
> new version number v1.27, improved CSR/PROM address detection,
> lots of cleanups, new pcnet32vlb module option, HP-PARISC support,
> added module parameter descriptions, initial ethtool support
>
> based on the 2.4.8-ac10 driver which already had some more
> updates than Linus' 2.4.9 driver

Big patch, here it goes:

Index: pcnet32.c
===================================================================
RCS file: /home/cvs/parisc/linux/drivers/net/pcnet32.c,v
retrieving revision 1.12
diff -u -r1.12 pcnet32.c
--- pcnet32.c	2001/07/05 21:36:21	1.12
+++ pcnet32.c	2001/08/27 14:07:52
@@ -12,17 +12,7 @@
  *
  *	This driver is for PCnet32 and PCnetPCI based ethercards
  */
-/**************************************************************************
- *  23 Oct, 2000.
- *  Fixed a few bugs, related to running the controller in 32bit mode.
- *
- *  Carsten Langgaard, carstenl@mips.com
- *  Copyright (C) 2000 MIPS Technologies, Inc.  All rights reserved.
- *
- *************************************************************************/
 
-static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken.de\n";
-
 #include <linux/module.h>
 
 #include <linux/kernel.h>
@@ -39,31 +29,52 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
+#include <asm/uaccess.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
 
-static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0};
+#define DRV_NAME	"pcnet32"
+#define DRV_VERSION	"v1.27"
+#define DRV_DATE	"2001/08/26"
+#define PFX		DRV_NAME ": "
 
-/*
- * PCI device identifiers for "new style" Linux PCI Device Drivers
+static char version[] __devinitdata =
+  "PCnet32 Fast ethernet driver " DRV_VERSION " " DRV_DATE " <tsbogend@alpha.franken.de>";
+
+/* 
+ * PCI device identifiers for "new style" Linux PCI Device Drivers 
  */
 static struct pci_device_id pcnet32_pci_tbl[] __devinitdata = {
     { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE_HOME, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
     { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-    { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0x1014, 0x2000, 0, 0, 0 },
     { 0, }
 };
 
+MODULE_DEVICE_TABLE (pci, pcnet32_pci_tbl);
+
+int cards_found __initdata;
+
+/* 
+ * VLB I/O addresses 
+ */
+static unsigned int pcnet32_portlist[] __initdata = 
+	{0x300, 0x320, 0x340, 0x360, 0};
+
+
+
 static int pcnet32_debug = 1;
 static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */
+static int pcnet32vlb = 0; /* 1: check for VLB cards */
 
 static struct net_device *pcnet32_dev;
 
-static const int max_interrupt_work = 80;
-static const int rx_copybreak = 200;
+static int max_interrupt_work = 80;
+static int rx_copybreak = 200;
 
 #define PORT_AUI      0x00
 #define PORT_10BT     0x01
@@ -100,7 +111,7 @@
     PORT_ASEL			   /* 15 not supported	  */
 };
 
-#define MAX_UNITS 8
+#define MAX_UNITS 8	/* More are supported, limit only on options */
 static int options[MAX_UNITS];
 static int full_duplex[MAX_UNITS];
 
@@ -179,6 +190,14 @@
  * v1.25kf Added No Interrupt on successful Tx for some Tx's <kaf@fc.hp.com>
  * v1.26   Converted to pci_alloc_consistent, Jamey Hicks / George France
  *                                           <jamey@crl.dec.com>
+ * -	   Fixed a few bugs, related to running the controller in 32bit mode.
+ *	   23 Oct, 2000.  Carsten Langgaard, carstenl@mips.com
+ *	   Copyright (C) 2000 MIPS Technologies, Inc.  All rights reserved.
+ * v1.26p  Fix oops on rmmod+insmod; plug i/o resource leak - Paul Gortmaker
+ * v1.27   improved CSR/PROM address detection, lots of cleanups,
+ * 	   new pcnet32vlb module option, HP-PARISC support,
+ * 	   added module parameter descriptions, 
+ * 	   initial ethtool support - Helge Deller <deller@gmx.de>
  */
 
 
@@ -192,13 +211,13 @@
 #define PCNET32_LOG_RX_BUFFERS 5
 #endif
 
-#define TX_RING_SIZE			(1 << (PCNET32_LOG_TX_BUFFERS))
-#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
-#define TX_RING_LEN_BITS		((PCNET32_LOG_TX_BUFFERS) << 12)
-
-#define RX_RING_SIZE			(1 << (PCNET32_LOG_RX_BUFFERS))
-#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
-#define RX_RING_LEN_BITS		((PCNET32_LOG_RX_BUFFERS) << 4)
+#define TX_RING_SIZE		(1 << (PCNET32_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK	(TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS	((PCNET32_LOG_TX_BUFFERS) << 12)
+
+#define RX_RING_SIZE		(1 << (PCNET32_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK	(RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS	((PCNET32_LOG_RX_BUFFERS) << 4)
 
 #define PKT_BUF_SZ		1544
 
@@ -213,7 +232,7 @@
 #define PCNET32_DWIO_RESET	0x18
 #define PCNET32_DWIO_BDP	0x1C
 
-#define PCNET32_TOTAL_SIZE 0x20
+#define PCNET32_TOTAL_SIZE	0x20
 
 #define CRC_POLYNOMIAL_LE 0xedb88320UL	/* Ethernet CRC, little endian */
 
@@ -263,35 +282,34 @@
  */
 struct pcnet32_private {
     /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
-    struct pcnet32_rx_head   rx_ring[RX_RING_SIZE];
-    struct pcnet32_tx_head   tx_ring[TX_RING_SIZE];
-    struct pcnet32_init_block	init_block;
-    dma_addr_t dma_addr;		/* DMA address of beginning of this object, returned by pci_alloc_consistent */
-    struct pci_dev *pci_dev;		/* Pointer to the associated pci device structure */
-    const char *name;
+    struct pcnet32_rx_head    rx_ring[RX_RING_SIZE];
+    struct pcnet32_tx_head    tx_ring[TX_RING_SIZE];
+    struct pcnet32_init_block init_block;
+    dma_addr_t 		dma_addr;	/* DMA address of beginning of this object, 
+					   returned by pci_alloc_consistent */
+    struct pci_dev	*pci_dev;	/* Pointer to the associated pci device structure */
+    const char		*name;
     /* The saved address of a sent-in-place packet/buffer, for skfree(). */
-    struct sk_buff *tx_skbuff[TX_RING_SIZE];
-    struct sk_buff *rx_skbuff[RX_RING_SIZE];
-    dma_addr_t tx_dma_addr[TX_RING_SIZE];
-    dma_addr_t rx_dma_addr[RX_RING_SIZE];
-    struct pcnet32_access a;
-    spinlock_t lock;					/* Guard lock */
-    unsigned int cur_rx, cur_tx;		/* The next free ring entry */
-    unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
+    struct sk_buff	*tx_skbuff[TX_RING_SIZE];
+    struct sk_buff	*rx_skbuff[RX_RING_SIZE];
+    dma_addr_t		tx_dma_addr[TX_RING_SIZE];
+    dma_addr_t		rx_dma_addr[RX_RING_SIZE];
+    struct pcnet32_access *a;
+    spinlock_t		lock;		/* Guard lock */
+    unsigned int	cur_rx, cur_tx;	/* The next free ring entry */
+    unsigned int	dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
     struct net_device_stats stats;
-    char tx_full;
-    int	 options;
-    int	 shared_irq:1,			/* shared irq possible */
-	ltint:1,
-#ifdef DO_DXSUFLO
-	      dxsuflo:1,						    /* disable transmit stop on uflo */
-#endif
-	full_duplex:1,				/* full duplex possible */
-	mii:1;					/* mii port available */
-    struct net_device *next;
+    char		tx_full;
+    int			options;
+    int	shared_irq:1,			/* shared irq possible */
+	ltint:1,			/* enable TxDone-intr inhibitor */
+	dxsuflo:1,			/* disable transmit stop on uflo */
+	full_duplex:1,			/* full duplex possible */
+	mii:1;				/* mii port available */
+    struct net_device	*next;
 };
 
-static int  pcnet32_probe_vlbus(int cards_found);
+static void pcnet32_probe_vlbus(void);
 static int  pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *);
 static int  pcnet32_probe1(unsigned long, unsigned char, int, int, struct pci_dev *);
 static int  pcnet32_open(struct net_device *);
@@ -303,60 +321,49 @@
 static int  pcnet32_close(struct net_device *);
 static struct net_device_stats *pcnet32_get_stats(struct net_device *);
 static void pcnet32_set_multicast_list(struct net_device *);
-#ifdef HAVE_PRIVATE_IOCTL
-static int  pcnet32_mii_ioctl(struct net_device *, struct ifreq *, int);
-#endif
+static int  pcnet32_ioctl(struct net_device *, struct ifreq *, int);
 
 enum pci_flags_bit {
     PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
     PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
 };
 
-struct pcnet32_pci_id_info {
-    const char *name;
-    u16 vendor_id, device_id, svid, sdid, flags;
-    int io_size;
-    int (*probe1) (unsigned long, unsigned char, int, int, struct pci_dev *);
-};
-
-
-MODULE_DEVICE_TABLE (pci, pcnet32_pci_tbl);
 
-static u16 pcnet32_wio_read_csr (unsigned long addr, int index)
+static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
 {
     outw (index, addr+PCNET32_WIO_RAP);
     return inw (addr+PCNET32_WIO_RDP);
 }
 
-static void pcnet32_wio_write_csr (unsigned long addr, int index, u16 val)
+static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val)
 {
     outw (index, addr+PCNET32_WIO_RAP);
     outw (val, addr+PCNET32_WIO_RDP);
 }
 
-static u16 pcnet32_wio_read_bcr (unsigned long addr, int index)
+static u16 pcnet32_wio_read_bcr(unsigned long addr, int index)
 {
     outw (index, addr+PCNET32_WIO_RAP);
     return inw (addr+PCNET32_WIO_BDP);
 }
 
-static void pcnet32_wio_write_bcr (unsigned long addr, int index, u16 val)
+static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val)
 {
     outw (index, addr+PCNET32_WIO_RAP);
     outw (val, addr+PCNET32_WIO_BDP);
 }
 
-static u16 pcnet32_wio_read_rap (unsigned long addr)
+static u16 pcnet32_wio_read_rap(unsigned long addr)
 {
     return inw (addr+PCNET32_WIO_RAP);
 }
 
-static void pcnet32_wio_write_rap (unsigned long addr, u16 val)
+static void pcnet32_wio_write_rap(unsigned long addr, u16 val)
 {
     outw (val, addr+PCNET32_WIO_RAP);
 }
 
-static void pcnet32_wio_reset (unsigned long addr)
+static void pcnet32_wio_reset(unsigned long addr)
 {
     inw (addr+PCNET32_WIO_RESET);
 }
@@ -368,50 +375,50 @@
 }
 
 static struct pcnet32_access pcnet32_wio = {
-    pcnet32_wio_read_csr,
-    pcnet32_wio_write_csr,
-    pcnet32_wio_read_bcr,
-    pcnet32_wio_write_bcr,
-    pcnet32_wio_read_rap,
-    pcnet32_wio_write_rap,
-    pcnet32_wio_reset
+    read_csr:	pcnet32_wio_read_csr,
+    write_csr:	pcnet32_wio_write_csr,
+    read_bcr:	pcnet32_wio_read_bcr,
+    write_bcr:	pcnet32_wio_write_bcr,
+    read_rap:	pcnet32_wio_read_rap,
+    write_rap:	pcnet32_wio_write_rap,
+    reset:	pcnet32_wio_reset
 };
 
-static u16 pcnet32_dwio_read_csr (unsigned long addr, int index)
+static u16 pcnet32_dwio_read_csr(unsigned long addr, int index)
 {
     outl (index, addr+PCNET32_DWIO_RAP);
     return (inl (addr+PCNET32_DWIO_RDP) & 0xffff);
 }
 
-static void pcnet32_dwio_write_csr (unsigned long addr, int index, u16 val)
+static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val)
 {
     outl (index, addr+PCNET32_DWIO_RAP);
     outl (val, addr+PCNET32_DWIO_RDP);
 }
 
-static u16 pcnet32_dwio_read_bcr (unsigned long addr, int index)
+static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index)
 {
     outl (index, addr+PCNET32_DWIO_RAP);
     return (inl (addr+PCNET32_DWIO_BDP) & 0xffff);
 }
 
-static void pcnet32_dwio_write_bcr (unsigned long addr, int index, u16 val)
+static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val)
 {
     outl (index, addr+PCNET32_DWIO_RAP);
     outl (val, addr+PCNET32_DWIO_BDP);
 }
 
-static u16 pcnet32_dwio_read_rap (unsigned long addr)
+static u16 pcnet32_dwio_read_rap(unsigned long addr)
 {
     return (inl (addr+PCNET32_DWIO_RAP) & 0xffff);
 }
 
-static void pcnet32_dwio_write_rap (unsigned long addr, u16 val)
+static void pcnet32_dwio_write_rap(unsigned long addr, u16 val)
 {
     outl (val, addr+PCNET32_DWIO_RAP);
 }
 
-static void pcnet32_dwio_reset (unsigned long addr)
+static void pcnet32_dwio_reset(unsigned long addr)
 {
     inl (addr+PCNET32_DWIO_RESET);
 }
@@ -423,82 +430,65 @@
 }
 
 static struct pcnet32_access pcnet32_dwio = {
-    pcnet32_dwio_read_csr,
-    pcnet32_dwio_write_csr,
-    pcnet32_dwio_read_bcr,
-    pcnet32_dwio_write_bcr,
-    pcnet32_dwio_read_rap,
-    pcnet32_dwio_write_rap,
-    pcnet32_dwio_reset
-
+    read_csr:	pcnet32_dwio_read_csr,
+    write_csr:	pcnet32_dwio_write_csr,
+    read_bcr:	pcnet32_dwio_read_bcr,
+    write_bcr:	pcnet32_dwio_write_bcr,
+    read_rap:	pcnet32_dwio_read_rap,
+    write_rap:	pcnet32_dwio_write_rap,
+    reset:	pcnet32_dwio_reset
 };
+
 
- 
 
-/* only probes for non-PCI devices, the rest are handled by pci_register_driver via pcnet32_probe_pci*/
-static int __init pcnet32_probe_vlbus(int cards_found)
+/* only probes for non-PCI devices, the rest are handled by 
+ * pci_register_driver via pcnet32_probe_pci */
+
+static void __devinit
+pcnet32_probe_vlbus(void)
 {
-    unsigned long ioaddr = 0; // FIXME dev ? dev->base_addr: 0;
-    unsigned int  irq_line = 0; // FIXME dev ? dev->irq : 0;
-    int *port;
-    
-    printk(KERN_INFO "pcnet32_probe_vlbus: cards_found=%d\n", cards_found);
-#ifndef __powerpc__
-    if (ioaddr > 0x1ff) {
-	if (check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0)
-	    return pcnet32_probe1(ioaddr, irq_line, 0, 0, NULL);
-	else
-	    return -ENODEV;
-    } else
-#endif
-	if (ioaddr != 0)
-	    return -ENXIO;
+    unsigned int *port, ioaddr;
     
-    /* now look for PCnet32 VLB cards */
-    for (port = pcnet32_portlist; *port; port++) {
-	unsigned long ioaddr = *port;
-	
-	if ( check_region(ioaddr, PCNET32_TOTAL_SIZE) == 0) {
+    /* search for PCnet32 VLB cards at known addresses */
+    for (port = pcnet32_portlist; (ioaddr = *port); port++) {
+	if (!check_region(ioaddr, PCNET32_TOTAL_SIZE)) {
 	    /* check if there is really a pcnet chip on that ioaddr */
 	    if ((inb(ioaddr + 14) == 0x57) &&
 		(inb(ioaddr + 15) == 0x57) &&
-		(pcnet32_probe1(ioaddr, 0, 0, 0, NULL) == 0))
+		(pcnet32_probe1(ioaddr, 0, 0, cards_found, NULL) == 0))
 		cards_found++;
 	}
     }
-    return cards_found ? 0: -ENODEV;
 }
 
 
-
-static int __init
+static int __devinit
 pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-    static int card_idx;
-    long ioaddr;
-    int err = 0;
+    unsigned long ioaddr;
+    int err;
 
-    printk(KERN_INFO "pcnet32_probe_pci: found device %#08x.%#08x\n", ent->vendor, ent->device);
+    printk(KERN_INFO PFX "found %s\n", pdev->name);
 
-    if ((err = pci_enable_device(pdev)) < 0) {
-	printk(KERN_ERR "pcnet32.c: failed to enable device -- err=%d\n", err);
+    err = pci_enable_device(pdev);
+    if (err < 0) {
+	printk(KERN_ERR PFX "failed to enable device -- err=%d\n", err);
 	return err;
     }
-    pci_set_master(pdev);
 
+    pci_set_master(pdev);
     ioaddr = pci_resource_start (pdev, 0);
-    printk(KERN_INFO "    ioaddr=%#08lx  resource_flags=%#08lx\n", ioaddr, pci_resource_flags (pdev, 0));
     if (!ioaddr) {
-        printk (KERN_ERR "no PCI IO resources, aborting\n");
+        printk (KERN_ERR PFX "card has no PCI IO resources, aborting\n");
         return -ENODEV;
     }
-	
+    
     if (!pci_dma_supported(pdev, PCNET32_DMA_MASK)) {
-	printk(KERN_ERR "pcnet32.c: architecture does not support 32bit PCI busmaster DMA\n");
+	printk(KERN_ERR PFX "architecture does not support 32bit PCI busmaster DMA\n");
 	return -ENODEV;
     }
 
-    return pcnet32_probe1(ioaddr, pdev->irq, 1, card_idx, pdev);
+    return pcnet32_probe1(ioaddr, pdev->irq, 1, cards_found, pdev);
 }
 
 
@@ -506,41 +496,45 @@
  *  Called from both pcnet32_probe_vlbus and pcnet_probe_pci.  
  *  pdev will be NULL when called from pcnet32_probe_vlbus.
  */
-static int __init
-pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev)
+static int __devinit
+pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared,
+		int card_idx, struct pci_dev *pdev)
 {
     struct pcnet32_private *lp;
+    struct resource *res;
     dma_addr_t lp_dma_addr;
-    int i,media,fdx = 0, mii = 0, fset = 0;
-#ifdef DO_DXSUFLO
-    int dxsuflo = 0;
-#endif
-    int ltint = 0;
+    int i, media;
+    int fdx, mii, fset, dxsuflo, ltint;
     int chip_version;
     char *chipname;
     struct net_device *dev;
     struct pcnet32_access *a = NULL;
+    u8 promaddr[6];
 
     /* reset the chip */
     pcnet32_dwio_reset(ioaddr);
     pcnet32_wio_reset(ioaddr);
 
     /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
-    if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) {
+    if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) {
 	a = &pcnet32_wio;
     } else {
-	if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
+	if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
 	    a = &pcnet32_dwio;
 	} else
 	    return -ENODEV;
     }
 
-    chip_version = a->read_csr (ioaddr, 88) | (a->read_csr (ioaddr,89) << 16);
+    chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr,89) << 16);
     if (pcnet32_debug > 2)
 	printk(KERN_INFO "  PCnet chip version is %#x.\n", chip_version);
     if ((chip_version & 0xfff) != 0x003)
 	return -ENODEV;
+    
+    /* initialize variables */
+    fdx = mii = fset = dxsuflo = ltint = 0;
     chip_version = (chip_version >> 12) & 0xffff;
+
     switch (chip_version) {
     case 0x2420:
 	chipname = "PCnet/PCI 79C970"; /* PCI */
@@ -579,23 +573,24 @@
 	 * mode by which the card should operate
 	 */
 	/* switch to home wiring mode */
-	media = a->read_bcr (ioaddr, 49);
+	media = a->read_bcr(ioaddr, 49);
 #if 0
 	if (pcnet32_debug > 2)
-	    printk(KERN_DEBUG "pcnet32: pcnet32 media value %#x.\n",  media);
+	    printk(KERN_DEBUG PFX "media value %#x.\n",  media);
 	media &= ~3;
 	media |= 1;
 #endif
 	if (pcnet32_debug > 2)
-	    printk(KERN_DEBUG "pcnet32: pcnet32 media reset to %#x.\n",  media);
-	a->write_bcr (ioaddr, 49, media);
+	    printk(KERN_DEBUG PFX "media reset to %#x.\n",  media);
+	a->write_bcr(ioaddr, 49, media);
 	break;
     case 0x2627:
 	chipname = "PCnet/FAST III 79C975"; /* PCI */
 	fdx = 1; mii = 1;
 	break;
     default:
-	printk(KERN_INFO "pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
+	printk(KERN_INFO PFX "PCnet version %#x, no PCnet32 chip.\n",
+			chip_version);
 	return -ENODEV;
     }
 
@@ -610,14 +605,12 @@
     {
 	a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800));
 	a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
-#ifdef DO_DXSUFLO
 	dxsuflo = 1;
-#endif
 	ltint = 1;
     }
     
     dev = init_etherdev(NULL, 0);
-    if(dev==NULL)
+    if(!dev)
 	return -ENOMEM;
 
     printk(KERN_INFO "%s: %s at %#3lx,", dev->name, chipname, ioaddr);
@@ -636,24 +629,31 @@
 	dev->dev_addr[2*i] = val & 0x0ff;
 	dev->dev_addr[2*i+1] = (val >> 8) & 0x0ff;
     }
-    {
-	u8 promaddr[6];
-	for (i = 0; i < 6; i++) {
-	    promaddr[i] = inb(ioaddr + i);
-	}
-	if( memcmp( promaddr, dev->dev_addr, 6) )
-	{
-	    printk(" warning PROM address does not match CSR address");
-#if defined(__i386__)
-	    printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name);
-	    memcpy(dev->dev_addr, promaddr, 6);
+
+    /* read PROM address and compare with CSR address */
+    for (i = 0; i < 6; i++)
+	promaddr[i] = inb(ioaddr + i);
+    
+    if( !is_valid_ether_addr(dev->dev_addr) ) {
+	printk(" warning: CSR address invalid,\n");
+	printk(KERN_INFO "    using instead PROM address of");
+	memcpy(dev->dev_addr, promaddr, 6);
+    }
+
+    /* XXX: is this check still needed with the above check ? */
+    if( memcmp( promaddr, dev->dev_addr, 6) ) {
+	printk(" warning: PROM address does not match CSR address, \n");
+#if defined(__i386__) || defined(__hppa__)
+	printk(KERN_INFO "    using instead PROM address of");
+	memcpy(dev->dev_addr, promaddr, 6);
+#else
+	printk(KERN_INFO "    using CSR address of");
 #endif
-	}	    	    
     }
+				    
     /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */
     if( !is_valid_ether_addr(dev->dev_addr) )
-	for (i = 0; i < 6; i++)
-	    dev->dev_addr[i]=0;
+	memset(dev->dev_addr, 0, sizeof(dev->dev_addr));
 
     for (i = 0; i < 6; i++)
 	printk(" %2.2x", dev->dev_addr[i] );
@@ -682,16 +682,19 @@
     }
 
     dev->base_addr = ioaddr;
-    request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname);
+    res = request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname);
+    if (!res)
+	return -EBUSY;
     
     /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */
-    if ((lp = pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL)
+    if ((lp = pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) {
+	release_resource(res);
 	return -ENOMEM;
+    }
 
     memset(lp, 0, sizeof(*lp));
     lp->dma_addr = lp_dma_addr;
     lp->pci_dev = pdev;
-    printk("\n" KERN_INFO "pcnet32: pcnet32_private lp=%p lp_dma_addr=%#08x", lp, lp_dma_addr);
 
     spin_lock_init(&lp->lock);
     
@@ -699,25 +702,25 @@
     lp->name = chipname;
     lp->shared_irq = shared;
     lp->full_duplex = fdx;
-#ifdef DO_DXSUFLO
     lp->dxsuflo = dxsuflo;
-#endif
     lp->ltint = ltint;
     lp->mii = mii;
-    if (options[card_idx] > sizeof (options_mapping))
+    if ((card_idx >= MAX_UNITS) || (options[card_idx] > sizeof(options_mapping)))
 	lp->options = PORT_ASEL;
     else
 	lp->options = options_mapping[options[card_idx]];
     
-    if (fdx && !(lp->options & PORT_ASEL) && full_duplex[card_idx])
+    if (fdx && !(lp->options & PORT_ASEL) && 
+		((card_idx>=MAX_UNITS) || full_duplex[card_idx]))
 	lp->options |= PORT_FD;
     
-    if (a == NULL) {
-      printk(KERN_ERR "pcnet32: No access methods\n");
+    if (!a) {
+      printk(KERN_ERR PFX "No access methods\n");
       pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr);
+      release_resource(res);
       return -ENODEV;
     }
-    lp->a = *a;
+    lp->a = a;
     
     /* detect special T1/E1 WAN card by checking for MAC address */
     if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75)
@@ -733,10 +736,10 @@
     lp->init_block.tx_ring = (u32)le32_to_cpu(lp->dma_addr + offsetof(struct pcnet32_private, tx_ring));
     
     /* switch pcnet32 to 32bit mode */
-    a->write_bcr (ioaddr, 20, 2);
+    a->write_bcr(ioaddr, 20, 2);
 
-    a->write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) & 0xffff);
-    a->write_csr (ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16);
+    a->write_csr(ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) & 0xffff);
+    a->write_csr(ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16);
     
     if (irq_line) {
 	dev->irq = irq_line;
@@ -753,7 +756,7 @@
 	 * boards will work.
 	 */
 	/* Trigger an initialization just for the interrupt. */
-	a->write_csr (ioaddr, 0, 0x41);
+	a->write_csr(ioaddr, 0, 0x41);
 	mdelay (1);
 	
 	dev->irq = probe_irq_off (irq_mask);
@@ -762,12 +765,11 @@
 	else {
 	    printk(", failed to detect IRQ line.\n");
 	    pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr);
+	    release_resource(res);
 	    return -ENODEV;
 	}
     }
 
-    if (pcnet32_debug > 0)
-	printk(KERN_INFO "%s", version);
     
     /* The PCNET32-specific entries in the device structure. */
     dev->open = &pcnet32_open;
@@ -775,9 +777,7 @@
     dev->stop = &pcnet32_close;
     dev->get_stats = &pcnet32_get_stats;
     dev->set_multicast_list = &pcnet32_set_multicast_list;
-#ifdef HAVE_PRIVATE_IOCTL
-    dev->do_ioctl = &pcnet32_mii_ioctl;
-#endif
+    dev->do_ioctl = &pcnet32_ioctl;
     dev->tx_timeout = pcnet32_tx_timeout;
     dev->watchdog_timeo = (HZ >> 1);
 
@@ -788,8 +788,8 @@
     ether_setup(dev);
     return 0;
 }
+
 
- 
 static int
 pcnet32_open(struct net_device *dev)
 {
@@ -809,10 +809,10 @@
 	return -EINVAL;
 
     /* Reset the PCNET32 */
-    lp->a.reset (ioaddr);
+    lp->a->reset(ioaddr);
 
     /* switch pcnet32 to 32bit mode */
-    lp->a.write_bcr (ioaddr, 20, 2);
+    lp->a->write_bcr(ioaddr, 20, 2);
 
     if (pcnet32_debug > 1)
 	printk(KERN_DEBUG "%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n",
@@ -822,54 +822,55 @@
 	       (u32) (lp->dma_addr + offsetof(struct pcnet32_private, init_block)));
     
     /* set/reset autoselect bit */
-    val = lp->a.read_bcr (ioaddr, 2) & ~2;
+    val = lp->a->read_bcr(ioaddr, 2) & ~2;
     if (lp->options & PORT_ASEL)
 	val |= 2;
-    lp->a.write_bcr (ioaddr, 2, val);
+    lp->a->write_bcr(ioaddr, 2, val);
     
     /* handle full duplex setting */
     if (lp->full_duplex) {
-	val = lp->a.read_bcr (ioaddr, 9) & ~3;
+	val = lp->a->read_bcr(ioaddr, 9) & ~3;
 	if (lp->options & PORT_FD) {
 	    val |= 1;
 	    if (lp->options == (PORT_FD | PORT_AUI))
 		val |= 2;
 	}
-	lp->a.write_bcr (ioaddr, 9, val);
+	lp->a->write_bcr(ioaddr, 9, val);
     }
     
     /* set/reset GPSI bit in test register */
-    val = lp->a.read_csr (ioaddr, 124) & ~0x10;
+    val = lp->a->read_csr(ioaddr, 124) & ~0x10;
     if ((lp->options & PORT_PORTSEL) == PORT_GPSI)
 	val |= 0x10;
-    lp->a.write_csr (ioaddr, 124, val);
+    lp->a->write_csr(ioaddr, 124, val);
     
     if (lp->mii && !(lp->options & PORT_ASEL)) {
-	val = lp->a.read_bcr (ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */
+	val = lp->a->read_bcr(ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */
 	if (lp->options & PORT_FD)
 	    val |= 0x10;
 	if (lp->options & PORT_100)
 	    val |= 0x08;
-	lp->a.write_bcr (ioaddr, 32, val);
+	lp->a->write_bcr(ioaddr, 32, val);
     } else {
 	if (lp->options & PORT_ASEL) {  /* enable auto negotiate, setup, disable fd */
-		val = lp->a.read_bcr(ioaddr, 32) & ~0x98;
+		val = lp->a->read_bcr(ioaddr, 32) & ~0x98;
 		val |= 0x20;
-		lp->a.write_bcr(ioaddr, 32, val);
+		lp->a->write_bcr(ioaddr, 32, val);
 	}
     }
 
 #ifdef DO_DXSUFLO 
     if (lp->dxsuflo) { /* Disable transmit stop on underflow */
-	val = lp->a.read_csr (ioaddr, 3);
+	val = lp->a->read_csr(ioaddr, 3);
 	val |= 0x40;
-	lp->a.write_csr (ioaddr, 3, val);
+	lp->a->write_csr(ioaddr, 3, val);
     }
 #endif
+
     if (lp->ltint) { /* Enable TxDone-intr inhibitor */
-	val = lp->a.read_csr (ioaddr, 5);
+	val = lp->a->read_csr(ioaddr, 5);
 	val |= (1<<14);
-	lp->a.write_csr (ioaddr, 5, val);
+	lp->a->write_csr(ioaddr, 5, val);
     }
    
     lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7);
@@ -879,28 +880,28 @@
 	return -ENOMEM;
     
     /* Re-initialize the PCNET32, and start it when done. */
-    lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff);
-    lp->a.write_csr (ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16);
+    lp->a->write_csr(ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff);
+    lp->a->write_csr(ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16);
 
-    lp->a.write_csr (ioaddr, 4, 0x0915);
-    lp->a.write_csr (ioaddr, 0, 0x0001);
+    lp->a->write_csr(ioaddr, 4, 0x0915);
+    lp->a->write_csr(ioaddr, 0, 0x0001);
 
     netif_start_queue(dev);
 
     i = 0;
     while (i++ < 100)
-	if (lp->a.read_csr (ioaddr, 0) & 0x0100)
+	if (lp->a->read_csr(ioaddr, 0) & 0x0100)
 	    break;
     /* 
      * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
      * reports that doing so triggers a bug in the '974.
      */
-    lp->a.write_csr (ioaddr, 0, 0x0042);
+    lp->a->write_csr(ioaddr, 0, 0x0042);
 
     if (pcnet32_debug > 2)
 	printk(KERN_DEBUG "%s: pcnet32 open after %d ticks, init block %#x csr0 %4.4x.\n",
 	       dev->name, i, (u32) (lp->dma_addr + offsetof(struct pcnet32_private, init_block)),
-	       lp->a.read_csr (ioaddr, 0));
+	       lp->a->read_csr(ioaddr, 0));
 
 
     MOD_INC_USE_COUNT;
@@ -992,13 +993,13 @@
 	return;
     
     /* ReInit Ring */
-    lp->a.write_csr (ioaddr, 0, 1);
+    lp->a->write_csr(ioaddr, 0, 1);
     i = 0;
     while (i++ < 100)
-	if (lp->a.read_csr (ioaddr, 0) & 0x0100)
+	if (lp->a->read_csr(ioaddr, 0) & 0x0100)
 	    break;
 
-    lp->a.write_csr (ioaddr, 0, csr0_bits);
+    lp->a->write_csr(ioaddr, 0, csr0_bits);
 }
 
 
@@ -1010,8 +1011,8 @@
 
     /* Transmitter timeout, serious problems. */
 	printk(KERN_ERR "%s: transmit timed out, status %4.4x, resetting.\n",
-	       dev->name, lp->a.read_csr (ioaddr, 0));
-	lp->a.write_csr (ioaddr, 0, 0x0004);
+	       dev->name, lp->a->read_csr(ioaddr, 0));
+	lp->a->write_csr(ioaddr, 0, 0x0004);
 	lp->stats.tx_errors++;
 	if (pcnet32_debug > 2) {
 	    int i;
@@ -1046,7 +1047,7 @@
 
     if (pcnet32_debug > 3) {
 	printk(KERN_DEBUG "%s: pcnet32_start_xmit() called, csr0 %4.4x.\n",
-	       dev->name, lp->a.read_csr (ioaddr, 0));
+	       dev->name, lp->a->read_csr(ioaddr, 0));
     }
 
     spin_lock_irqsave(&lp->lock, flags);
@@ -1088,7 +1089,7 @@
     lp->stats.tx_bytes += skb->len;
 
     /* Trigger an immediate send poll. */
-    lp->a.write_csr (ioaddr, 0, 0x0048);
+    lp->a->write_csr(ioaddr, 0, 0x0048);
 
     dev->trans_start = jiffies;
 
@@ -1113,8 +1114,9 @@
     int boguscnt =  max_interrupt_work;
     int must_restart;
 
-    if (dev == NULL) {
-	printk (KERN_DEBUG "pcnet32_interrupt(): irq %d for unknown device.\n", irq);
+    if (!dev) {
+	printk (KERN_DEBUG "%s(): irq %d for unknown device\n",
+		__FUNCTION__, irq);
 	return;
     }
 
@@ -1123,16 +1125,16 @@
     
     spin_lock(&lp->lock);
     
-    rap = lp->a.read_rap(ioaddr);
-    while ((csr0 = lp->a.read_csr (ioaddr, 0)) & 0x8600 && --boguscnt >= 0) {
+    rap = lp->a->read_rap(ioaddr);
+    while ((csr0 = lp->a->read_csr(ioaddr, 0)) & 0x8600 && --boguscnt >= 0) {
 	/* Acknowledge all of the current interrupt sources ASAP. */
-	lp->a.write_csr (ioaddr, 0, csr0 & ~0x004f);
+	lp->a->write_csr(ioaddr, 0, csr0 & ~0x004f);
 
 	must_restart = 0;
 
 	if (pcnet32_debug > 5)
 	    printk(KERN_DEBUG "%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",
-		   dev->name, csr0, lp->a.read_csr (ioaddr, 0));
+		   dev->name, csr0, lp->a->read_csr(ioaddr, 0));
 
 	if (csr0 & 0x0400)		/* Rx interrupt */
 	    pcnet32_rx(dev);
@@ -1185,7 +1187,8 @@
 
 		/* We must free the original skb */
 		if (lp->tx_skbuff[entry]) {
-                    pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry], lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
+                    pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry],
+			lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
 		    dev_kfree_skb_irq(lp->tx_skbuff[entry]);
 		    lp->tx_skbuff[entry] = 0;
                     lp->tx_dma_addr[entry] = 0;
@@ -1193,13 +1196,12 @@
 		dirty_tx++;
 	    }
 
-#ifndef final_version
 	    if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
-		printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-		       dirty_tx, lp->cur_tx, lp->tx_full);
+		printk(KERN_ERR "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+			dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
 		dirty_tx += TX_RING_SIZE;
 	    }
-#endif
+
 	    if (lp->tx_full &&
 		netif_queue_stopped(dev) &&
 		dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
@@ -1233,18 +1235,18 @@
 
 	if (must_restart) {
 	    /* stop the chip to clear the error condition, then restart */
-	    lp->a.write_csr (ioaddr, 0, 0x0004);
+	    lp->a->write_csr(ioaddr, 0, 0x0004);
 	    pcnet32_restart(dev, 0x0002);
 	}
     }
 
     /* Clear any other interrupt, and set interrupt enable. */
-    lp->a.write_csr (ioaddr, 0, 0x7940);
-    lp->a.write_rap(ioaddr,rap);
+    lp->a->write_csr(ioaddr, 0, 0x7940);
+    lp->a->write_rap(ioaddr,rap);
     
     if (pcnet32_debug > 4)
 	printk(KERN_DEBUG "%s: exiting interrupt, csr0=%#4.4x.\n",
-	       dev->name, lp->a.read_csr (ioaddr, 0));
+	       dev->name, lp->a->read_csr(ioaddr, 0));
 
     spin_unlock(&lp->lock);
 }
@@ -1293,7 +1295,9 @@
 			skb_put (skb, pkt_len);
 			lp->rx_skbuff[entry] = newskb;
 			newskb->dev = dev;
-                        lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, newskb->len, PCI_DMA_FROMDEVICE);
+                        lp->rx_dma_addr[entry] = 
+				pci_map_single(lp->pci_dev, newskb->tail,
+					newskb->len, PCI_DMA_FROMDEVICE);
 			lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]);
 			rx_in_place = 1;
 		    } else
@@ -1351,21 +1355,28 @@
 
     netif_stop_queue(dev);
 
-    lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112);
+    lp->stats.rx_missed_errors = lp->a->read_csr(ioaddr, 112);
 
     if (pcnet32_debug > 1)
 	printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
-	       dev->name, lp->a.read_csr (ioaddr, 0));
+	       dev->name, lp->a->read_csr(ioaddr, 0));
 
     /* We stop the PCNET32 here -- it occasionally polls memory if we don't. */
-    lp->a.write_csr (ioaddr, 0, 0x0004);
+    lp->a->write_csr(ioaddr, 0, 0x0004);
 
     /*
      * Switch back to 16bit mode to avoid problems with dumb 
      * DOS packet driver after a warm reboot
+     */
+    lp->a->write_bcr(ioaddr, 20, 4);
+
+    /*
+     *	FIXME: What happens if the bcr write is posted, the buffers are
+     *	freed and there is still incoming DMA traffic
      */
-    lp->a.write_bcr (ioaddr, 20, 4);
 
+#warning "PCI posting bug"
+
     free_irq(dev->irq, dev);
     
     /* free all allocated skbuffs */
@@ -1402,9 +1413,9 @@
     unsigned long flags;
 
     spin_lock_irqsave(&lp->lock, flags);
-    saved_addr = lp->a.read_rap(ioaddr);
-    lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112);
-    lp->a.write_rap(ioaddr, saved_addr);
+    saved_addr = lp->a->read_rap(ioaddr);
+    lp->stats.rx_missed_errors = lp->a->read_csr(ioaddr, 112);
+    lp->a->write_rap(ioaddr, saved_addr);
     spin_unlock_irqrestore(&lp->lock, flags);
 
     return &lp->stats;
@@ -1423,13 +1434,13 @@
 	
     /* set all multicast bits */
     if (dev->flags & IFF_ALLMULTI){ 
-	ib->filter [0] = 0xffffffff;
-	ib->filter [1] = 0xffffffff;
+	ib->filter[0] = 0xffffffff;
+	ib->filter[1] = 0xffffffff;
 	return;
     }
     /* clear the multicast filter */
-    ib->filter [0] = 0;
-    ib->filter [1] = 0;
+    ib->filter[0] = 0;
+    ib->filter[1] = 0;
 
     /* Add addresses */
     for (i = 0; i < dev->mc_count; i++){
@@ -1477,96 +1488,137 @@
 	pcnet32_load_multicast (dev);
     }
     
-    lp->a.write_csr (ioaddr, 0, 0x0004); /* Temporarily stop the lance. */
+    lp->a->write_csr(ioaddr, 0, 0x0004); /* Temporarily stop the lance. */
 
     pcnet32_restart(dev, 0x0042); /*  Resume normal operation */
 }
+
 
-#ifdef HAVE_PRIVATE_IOCTL
-static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+/*
+ * Provide ethtool interface
+ */
+static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
 {
+    struct pcnet32_private *lp = dev->priv;	 
+    struct ethtool_drvinfo info;
+    u32 ethcmd;
+		
+    if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
+	return -EFAULT;
+
+    switch (ethcmd) {
+	case ETHTOOL_GDRVINFO:
+		memset(&info, 0, sizeof(info));
+		info.cmd = ETHTOOL_GDRVINFO;
+		strcpy(info.driver, DRV_NAME);
+		strcpy(info.version, DRV_VERSION);
+		strcpy(info.fw_version, lp->name);
+		if (lp->pci_dev)
+			strcpy(info.bus_info, lp->pci_dev->slot_name);
+		else
+			strcpy(info.bus_info, "VLB");
+		if (copy_to_user(useraddr, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+    } /* switch (ethcmd) */
+	
+    return -EOPNOTSUPP;
+}
+
+
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
     unsigned long ioaddr = dev->base_addr;
     struct pcnet32_private *lp = dev->priv;	 
-    u16 *data = (u16 *)&rq->ifr_data;
-    int phyaddr = lp->a.read_bcr (ioaddr, 33);
+    struct mii_ioctl_data *data = (struct mii_ioctl_data *) &rq->ifr_data;
+    int phyaddr = lp->a->read_bcr(ioaddr, 33);
 
-    if (lp->mii) {
-	switch(cmd) {
-	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
-	    data[0] = (phyaddr >> 5) & 0x1f;
-	    /* Fall Through */
-	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
-	    lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
-	    data[3] = lp->a.read_bcr (ioaddr, 34);
-	    lp->a.write_bcr (ioaddr, 33, phyaddr);
-	    return 0;
-	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-	    if (!capable(CAP_NET_ADMIN))
+    if (!lp || !lp->mii)
+	    return -ENODEV;
+    
+    switch (cmd) {
+    case SIOCETHTOOL:
+	return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+	
+    case SIOCGMIIPHY:			/* Get address of MII PHY in use. */
+    case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
+	data->phy_id = (phyaddr >> 5) & 0x1f;
+	/* Fall Through */
+	
+    case SIOCGMIIREG:			/* Read MII PHY register. */
+    case SIOCDEVPRIVATE+1:		/* for binary compat, remove in 2.5 */
+	lp->a->write_bcr(ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
+	data->val_out = lp->a->read_bcr(ioaddr, 34);
+	lp->a->write_bcr(ioaddr, 33, phyaddr);
+	return 0;
+	
+    case SIOCSMIIREG:			/* Write MII PHY register. */
+    case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
+	if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
-	    lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
-	    lp->a.write_bcr (ioaddr, 34, data[2]);
-	    lp->a.write_bcr (ioaddr, 33, phyaddr);
-	    return 0;
-	default:
-	    return -EOPNOTSUPP;
-	}
-    }
+	lp->a->write_bcr(ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
+	lp->a->write_bcr(ioaddr, 34, data->val_in);
+	lp->a->write_bcr(ioaddr, 33, phyaddr);
+	return 0;
+	
+    default:
+	return -EOPNOTSUPP;
+    } /* switch (cmd) */
+
     return -EOPNOTSUPP;
 }
-#endif	/* HAVE_PRIVATE_IOCTL */
-					    
+
+
 static struct pci_driver pcnet32_driver = {
-    name:  "pcnet32",
-    probe: pcnet32_probe_pci,
-    remove: NULL,
-    id_table: pcnet32_pci_tbl,
+    name:	DRV_NAME,
+    probe:	pcnet32_probe_pci,
+    id_table:	pcnet32_pci_tbl,
 };
 
 MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, DRV_NAME " debug level (0-6)");
 MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt");  
 MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM_DESC(rx_copybreak, DRV_NAME " copy breakpoint for copy-only-tiny-frames"); 
 MODULE_PARM(tx_start_pt, "i");
+MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)"); 
+MODULE_PARM(pcnet32vlb, "i");
+MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)"); 
 MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)"); 
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM_DESC(full_duplex, DRV_NAME " full duplex setting(s) (1)");
+
 MODULE_AUTHOR("Thomas Bogendoerfer");
 MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards");
 
 /* An additional parameter that may be passed in... */
-static int debug = -1;
-static int tx_start_pt = -1;
+static int debug __initdata = -1;
+static int tx_start_pt __initdata = -1;
 
 static int __init pcnet32_init_module(void)
 {
-    int cards_found = 0;
-    int err;
+    printk(KERN_INFO "%s\n", version);
 
     if (debug > 0)
 	pcnet32_debug = debug;
+
     if ((tx_start_pt >= 0) && (tx_start_pt <= 3))
 	tx_start = tx_start_pt;
-    
-    pcnet32_dev = NULL;
+
     /* find the PCI devices */
-#define USE_PCI_REGISTER_DRIVER
-#ifdef USE_PCI_REGISTER_DRIVER
-    if ((err = pci_module_init(&pcnet32_driver)) < 0 )
-       return err;
-#else
-    {
-        struct pci_device_id *devid = pcnet32_pci_tbl;
-        for (devid = pcnet32_pci_tbl; devid != NULL && devid->vendor != 0; devid++) {
-            struct pci_dev *pdev = pci_find_subsys(devid->vendor, devid->device, devid->subvendor, devid->subdevice, NULL);
-            if (pdev != NULL) {
-                if (pcnet32_probe_pci(pdev, devid) >= 0) {
-                    cards_found++;
-                }
-            }
-        }
-    }
-#endif
-    return 0;
-    /* find any remaining VLbus devices */
-    return pcnet32_probe_vlbus(cards_found);
+    pci_module_init(&pcnet32_driver);
+
+    /* should we find any remaining VLbus devices ? */
+    if (pcnet32vlb)
+	pcnet32_probe_vlbus();
+
+    if (cards_found)
+	printk(KERN_INFO PFX "%d cards_found.\n", cards_found);
+    
+    return cards_found ? 0 : -ENODEV;
 }
 
 static void __exit pcnet32_cleanup_module(void)
@@ -1575,11 +1627,13 @@
 
     /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
     while (pcnet32_dev) {
-        struct pcnet32_private *lp = pcnet32_dev->priv;
+	struct pcnet32_private *lp = pcnet32_dev->priv;
 	next_dev = lp->next;
 	unregister_netdev(pcnet32_dev);
 	release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);
-        pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr);
+	if (lp->pci_dev)
+	    pci_unregister_driver(&pcnet32_driver);
+	pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr);
 	kfree(pcnet32_dev);
 	pcnet32_dev = next_dev;
     }