patch-2.4.11-dontuse linux/drivers/net/pcmcia/xircom_tulip_cb.c

Next file: linux/drivers/net/pcnet32.c
Previous file: linux/drivers/net/pcmcia/xircom_cb.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.10/linux/drivers/net/pcmcia/xircom_tulip_cb.c linux/drivers/net/pcmcia/xircom_tulip_cb.c
@@ -1,45 +1,33 @@
-/* tulip.c: A DEC 21040-family ethernet driver for Linux. */
+/* xircom_tulip_cb.c: A Xircom CBE-100 ethernet driver for Linux. */
 /*
 	Written/copyright 1994-1999 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU General Public License, incorporated herein by reference.
 
-	This driver is for the Digital "Tulip" Ethernet adapter interface.
-	It should work with most DEC 21*4*-based chips/ethercards, as well as
-	with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX.
-
 	The author may be reached as becker@scyld.com, or C/O
 	Scyld Computing Corporation
 	410 Severn Ave., Suite 210
 	Annapolis MD 21403
-
-	Support and updates available at
-	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
 */
 
+#define DRV_NAME	"xircom_tulip_cb"
+#define DRV_VERSION	"0.91+LK"
+#define DRV_RELDATE	"July 19, 2001"
+
 #define CARDBUS 1
-static const char version[] = "xircom_tulip_cb.c:v0.91 4/14/99 becker@scyld.com (modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford)\n";
 
 /* A few user-configurable values. */
 
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 25;
 
-#define MAX_UNITS 8
+#define MAX_UNITS 4
 /* Used to pass the full-duplex flag, etc. */
 static int full_duplex[MAX_UNITS];
 static int options[MAX_UNITS];
 static int mtu[MAX_UNITS];			/* Jumbo MTU for interfaces. */
 
-/*  The possible media types that can be set in options[] are: */
-static const char * const medianame[] = {
-	"10baseT", "10base2", "AUI", "100baseTx",
-	"10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
-	"100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
-	"10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
-};
-
 /* Keep the ring sizes a power of two for efficiency.
    Making the Tx ring too large decreases the effectiveness of channel
    bonding and packet priority.
@@ -82,16 +70,14 @@
 
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  (4*HZ)
+#define TX_TIMEOUT		(4 * HZ)
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+#define PKT_SETUP_SZ		192			/* Size of the setup frame */
 
-#if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
-#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
+/* PCI registers */
+#define PCI_POWERMGMT 	0x40
 
-#include <linux/version.h>
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
@@ -99,13 +85,22 @@
 #include <linux/etherdevice.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
 #include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/uaccess.h>
+
 
-/* Kernel compatibility defines, some common to David Hinds' PCMCIA package.
-   This is only in the support-all-kernels source code. */
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO DRV_NAME ".c derived from tulip.c:v0.91 4/14/99 becker@scyld.com\n"
+KERN_INFO " modified by danilo@cs.uni-magdeburg.de for XIRCOM CBE, fixed by Doug Ledford\n"
+KERN_INFO " unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "\n";
 
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver");
+MODULE_DESCRIPTION("Xircom CBE-100 ethernet driver");
+MODULE_LICENSE("GPL v2");
+
 MODULE_PARM(debug, "i");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(rx_copybreak, "i");
@@ -115,11 +110,11 @@
 
 #define RUN_AT(x) (jiffies + (x))
 
-#define tulip_debug debug
-#ifdef TULIP_DEBUG
-static int tulip_debug = TULIP_DEBUG;
+#define xircom_debug debug
+#ifdef XIRCOM_DEBUG
+static int xircom_debug = XIRCOM_DEBUG;
 #else
-static int tulip_debug = 1;
+static int xircom_debug = 1;
 #endif
 
 /*
@@ -127,17 +122,9 @@
 
 I. Board Compatibility
 
-This device driver is designed for the DECchip "Tulip", Digital's
-single-chip ethernet controllers for PCI.  Supported members of the family
-are the 21040, 21041, 21140, 21140A, 21142, and 21143.  Similar work-alike
-chips from Lite-On, Macronics, ASIX, Compex and other listed below are also
-supported. 
-
-These chips are used on at least 140 unique PCI board designs.  The great
-number of chips and board designs supported is the reason for the
-driver size and complexity.  Almost of the increasing complexity is in the
-board configuration and media selection code.  There is very little
-increasing in the operational critical path length.
+This device driver was forked from the driver for the DECchip "Tulip",
+Digital's single-chip ethernet controllers for PCI.  It supports Xircom's
+almost-Tulip-compatible CBE-100 CardBus adapters.
 
 II. Board-specific settings
 
@@ -149,11 +136,11 @@
 
 IIIa. Ring buffers
 
-The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
+The Xircom can use either ring buffers or lists of Tx and Rx descriptors.
 This driver uses statically allocated rings of Rx and Tx descriptors, set at
 compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs
 for the Rx ring buffers at open() time and passes the skb->data field to the
-Tulip as receive data buffers.  When an incoming frame is less than
+Xircom as receive data buffers.  When an incoming frame is less than
 RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
 copied to the new skbuff.  When the incoming frame is larger, the skbuff is
 passed directly up the protocol stack and replaced by a newly allocated
@@ -165,7 +152,7 @@
 that we are pre-loading the cache with immediately useful header
 information).  For large frames the copying cost is non-trivial, and the
 larger copy might flush the cache of useful data.  A subtle aspect of this
-choice is that the Tulip only receives into longword aligned buffers, thus
+choice is that the Xircom only receives into longword aligned buffers, thus
 the IP header at offset 14 isn't longword aligned for further processing.
 Copied frames are put into the new skbuff at an offset of "+2", thus copying
 has the beneficial effect of aligning the IP header and preloading the
@@ -199,32 +186,8 @@
 
 IVc. Errata
 
-We cannot use MII interrupts because there is no defined GPIO pin to attach
-them.  The MII transceiver status is polled using an kernel timer.
-
 */
 
-static void tulip_timer(unsigned long data);
-
-enum tbl_flag {
-	HAS_MII=1, HAS_ACPI=2,
-};
-static struct tulip_chip_table {
-	char *chip_name;
-	int io_size;
-	int valid_intrs;			/* CSR7 interrupt enable settings */
-	int flags;
-	void (*media_timer)(unsigned long data);
-} tulip_tbl[] = {
-  { "Xircom Cardbus Adapter (DEC 21143 compatible mode)", 128, 0x0801fbff,
-       HAS_MII | HAS_ACPI, tulip_timer }, 
-  {0},
-};
-/* This matches the table above. */
-enum chips {
-	X3201_3,
-};
-
 /* A full-duplex map for media types. */
 enum MediaIs {
 	MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
@@ -234,75 +197,113 @@
 
 /* Offsets to the Command and Status Registers, "CSRs".  All accesses
    must be longword instructions and quadword aligned. */
-enum tulip_offsets {
+enum xircom_offsets {
 	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
 	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
-	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
+	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x04, };
 
 /* The bits in the CSR5 status registers, mostly interrupt sources. */
 enum status_bits {
-	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
-	NormalIntr=0x10000, AbnormalIntr=0x8000,
-	RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
-	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
+	LinkChange=0x08000000,
+	NormalIntr=0x10000, NormalIntrMask=0x00014045,
+	AbnormalIntr=0x8000, AbnormalIntrMask=0x0a00a5a2,
+	ReservedIntrMask=0xe0001a18,
+	EarlyRxIntr=0x4000, BusErrorIntr=0x2000,
+	EarlyTxIntr=0x400, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
+	TxFIFOUnderflow=0x20, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
+};
+
+enum csr0_control_bits {
+	EnableMWI=0x01000000, EnableMRL=0x00800000,
+	EnableMRM=0x00200000, EqualBusPrio=0x02,
+	SoftwareReset=0x01,
+};
+
+enum csr6_control_bits {
+	ReceiveAllBit=0x40000000, AllMultiBit=0x80, PromiscBit=0x40,
+	HashFilterBit=0x01, FullDuplexBit=0x0200,
+	TxThresh10=0x400000, TxStoreForw=0x200000,
+	TxThreshMask=0xc000, TxThreshShift=14,
+	EnableTx=0x2000, EnableRx=0x02,
+	ReservedZeroMask=0x8d930134, ReservedOneMask=0x320c0000,
+	EnableTxRx=(EnableTx | EnableRx),
+};
+
+
+enum tbl_flag {
+	HAS_MII=1, HAS_ACPI=2,
+};
+static struct xircom_chip_table {
+	char *chip_name;
+	int valid_intrs;			/* CSR7 interrupt enable settings */
+	int flags;
+} xircom_tbl[] = {
+  { "Xircom Cardbus Adapter (DEC 21143 compatible mode)",
+	LinkChange | NormalIntr | AbnormalIntr | BusErrorIntr |
+	RxDied | RxNoBuf | RxIntr | TxFIFOUnderflow | TxNoBuf | TxDied | TxIntr,
+	HAS_MII | HAS_ACPI, },
+  { NULL, },
+};
+/* This matches the table above. */
+enum chips {
+	X3201_3,
 };
 
-/* The Tulip Rx and Tx buffer descriptors. */
-struct tulip_rx_desc {
+
+/* The Xircom Rx and Tx buffer descriptors. */
+struct xircom_rx_desc {
 	s32 status;
 	s32 length;
 	u32 buffer1, buffer2;
 };
 
-struct tulip_tx_desc {
+struct xircom_tx_desc {
 	s32 status;
 	s32 length;
 	u32 buffer1, buffer2;				/* We use only buffer 1.  */
 };
 
-enum desc_status_bits {
-	DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
+enum tx_desc0_status_bits {
+	Tx0DescOwned=0x80000000, Tx0DescError=0x8000, Tx0NoCarrier=0x0800,
+	Tx0LateColl=0x0200, Tx0ManyColl=0x0100, Tx0Underflow=0x02,
+};
+enum tx_desc1_status_bits {
+	Tx1ComplIntr=0x80000000, Tx1LastSeg=0x40000000, Tx1FirstSeg=0x20000000,
+	Tx1SetupPkt=0x08000000, Tx1DisableCRC=0x04000000, Tx1RingWrap=0x02000000,
+	Tx1ChainDesc=0x01000000, Tx1NoPad=0x800000, Tx1HashSetup=0x400000,
+	Tx1WholePkt=(Tx1FirstSeg | Tx1LastSeg),
+};
+enum rx_desc0_status_bits {
+	Rx0DescOwned=0x80000000, Rx0DescError=0x8000, Rx0NoSpace=0x4000,
+	Rx0Runt=0x0800, Rx0McastPkt=0x0400, Rx0FirstSeg=0x0200, Rx0LastSeg=0x0100,
+	Rx0HugeFrame=0x80, Rx0CRCError=0x02,
+	Rx0WholePkt=(Rx0FirstSeg | Rx0LastSeg),
+};
+enum rx_desc1_status_bits {
+	Rx1RingWrap=0x02000000, Rx1ChainDesc=0x01000000,
 };
 
-/* Ring-wrap flag in length field, use for last ring entry.
-	0x01000000 means chain on buffer2 address,
-	0x02000000 means use the ring start address in CSR2/3.
-   Note: Some work-alike chips do not function correctly in chained mode.
-   The ASIX chip works only in chained mode.
-   Thus we indicates ring mode, but always write the 'next' field for
-   chained mode as well.
-*/
-#define DESC_RING_WRAP 0x02000000
-
-struct tulip_private {
-	char devname[8];			/* Used only for kernel debugging. */
-	const char *product_name;
-	struct tulip_rx_desc rx_ring[RX_RING_SIZE];
-	struct tulip_tx_desc tx_ring[TX_RING_SIZE];
+struct xircom_private {
+	struct xircom_rx_desc rx_ring[RX_RING_SIZE];
+	struct xircom_tx_desc tx_ring[TX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
 #ifdef CARDBUS
-	/* The X3201-3 requires double word aligned tx bufs */
+	/* The X3201-3 requires 4-byte aligned tx bufs */
 	struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];
 #endif
 	/* The addresses of receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
-	char *rx_buffs;				/* Address of temporary Rx buffers. */
-	u8 setup_buf[96*sizeof(u16) + 7];
-	u16 *setup_frame;		/* Pseudo-Tx frame to init address table. */
+	u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)];	/* Pseudo-Tx frame to init address table. */
 	int chip_id;
-	int revision;
 	struct net_device_stats stats;
-	struct timer_list timer;	/* Media selection timer. */
-	int interrupt;				/* In-interrupt flag. */
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
+	unsigned int speed100:1;
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int full_duplex_lock:1;
+	unsigned int autoneg:1;
 	unsigned int default_port:4;		/* Last dev->if_port value. */
-	unsigned int medialock:1;			/* Don't sense media type. */
-	unsigned int mediasense:1;			/* Media sensing in progress. */
 	unsigned int open:1;
 	unsigned int csr0;					/* CSR0 setting. */
 	unsigned int csr6;					/* Current CSR6 control settings. */
@@ -316,44 +317,44 @@
 
 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 value);
-static void tulip_up(struct net_device *dev);
-static void tulip_down(struct net_device *dev);
-static int tulip_open(struct net_device *dev);
-static void tulip_timer(unsigned long data);
-static void tulip_tx_timeout(struct net_device *dev);
-static void tulip_init_ring(struct net_device *dev);
-static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static int tulip_rx(struct net_device *dev);
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int tulip_close(struct net_device *dev);
-static struct net_device_stats *tulip_get_stats(struct net_device *dev);
-#ifdef HAVE_PRIVATE_IOCTL
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-#endif
+static void xircom_up(struct net_device *dev);
+static void xircom_down(struct net_device *dev);
+static int xircom_open(struct net_device *dev);
+static void xircom_tx_timeout(struct net_device *dev);
+static void xircom_init_ring(struct net_device *dev);
+static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static int xircom_rx(struct net_device *dev);
+static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static int xircom_close(struct net_device *dev);
+static struct net_device_stats *xircom_get_stats(struct net_device *dev);
+static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static void set_rx_mode(struct net_device *dev);
+static void check_duplex(struct net_device *dev);
+
 
 /* The Xircom cards are picky about when certain bits in CSR6 can be
    manipulated.  Keith Owens <kaos@ocs.com.au>. */
-
-static void outl_CSR6 (u32 newcsr6, long ioaddr, int chip_idx)
+static void outl_CSR6(u32 newcsr6, long ioaddr)
 {
-	const int strict_bits = 0x0060e202;
+	const int strict_bits =
+		TxThresh10 | TxStoreForw | TxThreshMask | EnableTxRx | FullDuplexBit;
     int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
     long flags;
     save_flags(flags);
     cli();
-    newcsr6 &= 0x726cfecb; /* mask out the reserved CSR6 bits that always */
-			   /* read 0 on the Xircom cards */
-    newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */
+	/* mask out the reserved bits that always read 0 on the Xircom cards */
+	newcsr6 &= ~ReservedZeroMask;
+	/* or in the reserved bits that always read 1 */
+	newcsr6 |= ReservedOneMask;
     currcsr6 = inl(ioaddr + CSR6);
     if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||
-	((currcsr6 & ~0x2002) == 0)) {
+	((currcsr6 & ~EnableTxRx) == 0)) {
 		outl(newcsr6, ioaddr + CSR6);	/* safe */
 		restore_flags(flags);
 		return;
     }
     /* make sure the transmitter and receiver are stopped first */
-    currcsr6 &= ~0x2002;
+    currcsr6 &= ~EnableTxRx;
     while (1) {
 		csr5 = inl(ioaddr + CSR5);
 		if (csr5 == 0xffffffff)
@@ -364,7 +365,7 @@
 			(csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
 			break;  /* both are stopped or suspended */
 		if (!--attempts) {
-			printk(KERN_INFO "tulip.c: outl_CSR6 too many attempts,"
+			printk(KERN_INFO DRV_NAME ": outl_CSR6 too many attempts,"
 				   "csr5=0x%08x\n", csr5);
 			outl(newcsr6, ioaddr + CSR6);  /* unsafe but do it anyway */
 			restore_flags(flags);
@@ -377,102 +378,205 @@
     outl(newcsr6, ioaddr + CSR6);
     restore_flags(flags);
 }
-
-static struct net_device *tulip_probe1(struct pci_dev *pdev,
-				       long ioaddr, int irq,
-				       int chip_idx, int board_idx)
-{
-	static int did_version;			/* Already printed version info. */
-	struct net_device *dev;
-	struct tulip_private *tp;
-	u8 chip_rev;
-	int i;
 
-	if (tulip_debug > 0  &&  did_version++ == 0)
-		printk(KERN_INFO "%s", version);
 
-	dev = alloc_etherdev(0);
-	if (!dev)
-		return NULL;
+static void __devinit read_mac_address(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	int i, j;
+	unsigned char tuple, link, data_id, data_count;
 
-	pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev);
-	/* Bring the 21143 out of sleep mode.
-	   Caution: Snooze mode does not work with some boards! */
-	if (tulip_tbl[chip_idx].flags & HAS_ACPI)
-		pci_write_config_dword(pdev, 0x40, 0x00000000);
+	/* Xircom has its address stored in the CIS;
+	 * we access it through the boot rom interface for now
+	 * this might not work, as the CIS is not parsed but I
+	 * (danilo) use the offset I found on my card's CIS !!!
+	 *
+	 * Doug Ledford: I changed this routine around so that it
+	 * walks the CIS memory space, parsing the config items, and
+	 * finds the proper lan_node_id tuple and uses the data
+	 * stored there.
+	 */
+	outl(1 << 12, ioaddr + CSR9); /* enable boot rom access */
+	for (i = 0x100; i < 0x1f7; i += link+2) {
+		outl(i, ioaddr + CSR10);
+		tuple = inl(ioaddr + CSR9) & 0xff;
+		outl(i + 1, ioaddr + CSR10);
+		link = inl(ioaddr + CSR9) & 0xff;
+		outl(i + 2, ioaddr + CSR10);
+		data_id = inl(ioaddr + CSR9) & 0xff;
+		outl(i + 3, ioaddr + CSR10);
+		data_count = inl(ioaddr + CSR9) & 0xff;
+		if ( (tuple == 0x22) &&
+			 (data_id == 0x04) && (data_count == 0x06) ) {
+			/*
+			 * This is it.  We have the data we want.
+			 */
+			for (j = 0; j < 6; j++) {
+				outl(i + j + 4, ioaddr + CSR10);
+				dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff;
+			}
+			break;
+		} else if (link == 0) {
+			break;
+		}
+	}
+}
 
-	/* Stop the chip's Tx and Rx processes. */
-	outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, chip_idx);
-	/* Clear the missed-packet counter. */
-	(volatile int)inl(ioaddr + CSR8);
 
-	/* The station address ROM is read byte serially.  The register must
-	   be polled, waiting for the value to be read bit serially from the
-	   EEPROM.
-	   */
-	if (chip_idx == X3201_3) {
-		/* Xircom has its address stored in the CIS
-		 * we access it through the boot rom interface for now
-		 * this might not work, as the CIS is not parsed but I
-		 * (danilo) use the offset I found on my card's CIS !!!
-		 * 
-		 * Doug Ledford: I changed this routine around so that it
-		 * walks the CIS memory space, parsing the config items, and
-		 * finds the proper lan_node_id tuple and uses the data
-		 * stored there.
-		 */
-		unsigned char j, tuple, link, data_id, data_count;
-		outl(1<<12, ioaddr + CSR9); /* enable boot rom access */
-		for (i = 0x100; i < 0x1f7; i += link+2) {
-			outl(i, ioaddr + CSR10);
-			tuple = inl(ioaddr + CSR9) & 0xff;
-			outl(i + 1, ioaddr + CSR10);
-			link = inl(ioaddr + CSR9) & 0xff;
-			outl(i + 2, ioaddr + CSR10);
-			data_id = inl(ioaddr + CSR9) & 0xff;
-			outl(i + 3, ioaddr + CSR10);
-			data_count = inl(ioaddr + CSR9) & 0xff;
-			if ( (tuple == 0x22) &&
-				 (data_id == 0x04) && (data_count == 0x06) ) {
-				/*
-				 * This is it.  We have the data we want.
-				 */
-				for (j = 0; j < 6; j++) {
-					outl(i + j + 4, ioaddr + CSR10);
-					dev->dev_addr[j] = inl(ioaddr + CSR9) & 0xff;
-				}
-				break;
-			} else if (link == 0) {
-				break;
+/*
+ * locate the MII interfaces and initialize them.
+ */
+static void find_mii_transceivers(struct net_device *dev)
+{
+	struct xircom_private *tp = dev->priv;
+	int phy, phy_idx;
+
+	/* Find the connected MII xcvrs.
+	   Doing this in open() would allow detecting external xcvrs later,
+	   but takes much time. */
+	for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
+		int mii_status = mdio_read(dev, phy, MII_BMSR);
+		if ((mii_status & (BMSR_100BASE4 | BMSR_100HALF | BMSR_10HALF)) == BMSR_100BASE4 ||
+			((mii_status & BMSR_100BASE4) == 0 &&
+			 (mii_status & (BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | BMSR_10HALF)) != 0)) {
+			int mii_reg0 = mdio_read(dev, phy, MII_BMCR);
+			int mii_advert = mdio_read(dev, phy, MII_ADVERTISE);
+			int reg4 = ((mii_status >> 6) & tp->to_advertise) | ADVERTISE_CSMA;
+			tp->phys[phy_idx] = phy;
+			tp->advertising[phy_idx++] = reg4;
+			printk(KERN_INFO "%s:  MII transceiver #%d "
+				   "config %4.4x status %4.4x advertising %4.4x.\n",
+				   dev->name, phy, mii_reg0, mii_status, mii_advert);
+			/* Fixup for DLink with miswired PHY. */
+			if (mii_advert != reg4) {
+				printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d,"
+					   " previously advertising %4.4x.\n",
+					   dev->name, reg4, phy, mii_advert);
+				mdio_write(dev, phy, MII_ADVERTISE, reg4);
 			}
+			/* Enable autonegotiation: some boards default to off. */
+			mdio_write(dev, phy, MII_BMCR, mii_reg0 | BMCR_ANENABLE |
+					   (tp->full_duplex ? BMCR_FULLDPLX : 0) |
+					   (media_cap[tp->default_port]&MediaIs100 ? BMCR_SPEED100 : 0));
 		}
 	}
+	tp->mii_cnt = phy_idx;
+	if (phy_idx == 0) {
+		printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
+			   dev->name);
+		tp->phys[0] = 0;
+	}
+}
 
-	/* We do a request_region() only to register /proc/ioports info. */
-	request_region(ioaddr, tulip_tbl[chip_idx].io_size, "xircom_tulip_cb");
+
+/*
+ * To quote Arjan van de Ven:
+ *   tranceiver_voodoo() enables the external UTP plug thingy.
+ *   it's called voodoo as I stole this code and cannot cross-reference
+ *   it with the specification.
+ * Actually it seems to go like this:
+ * - GPIO2 enables the MII itself so we can talk to it. The MII gets reset
+ *   so any prior MII settings are lost.
+ * - GPIO0 enables the TP port so the MII can talk to the network.
+ * - a software reset will reset both GPIO pins.
+ * I also moved the software reset here, because doing it in xircom_up()
+ * required enabling the GPIO pins each time, which reset the MII each time.
+ * Thus we couldn't control the MII -- which sucks because we don't know
+ * how to handle full-duplex modes so we *must* disable them.
+ */
+static void transceiver_voodoo(struct net_device *dev)
+{
+	struct xircom_private *tp = dev->priv;
+	long ioaddr = dev->base_addr;
+
+	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+	outl(SoftwareReset, ioaddr + CSR0);
+	udelay(2);
+
+	/* Deassert reset. */
+	outl(tp->csr0, ioaddr + CSR0);
+
+	/* Reset the xcvr interface and turn on heartbeat. */
+	outl(0x0008, ioaddr + CSR15);
+	udelay(5);  /* The delays are Xircom-recommended to give the
+				 * chipset time to reset the actual hardware
+				 * on the PCMCIA card
+				 */
+	outl(0xa8050000, ioaddr + CSR15);
+	udelay(5);
+	outl(0xa00f0000, ioaddr + CSR15);
+	udelay(5);
+
+	outl_CSR6(0, ioaddr);
+	//outl_CSR6(FullDuplexBit, ioaddr);
+}
+
+
+static int __devinit xircom_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct net_device *dev;
+	struct xircom_private *tp;
+	static int board_idx = -1;
+	int chip_idx = id->driver_data;
+	long ioaddr;
+	int i;
+	u8 chip_rev;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+	static int printed_version;
+	if (!printed_version++)
+		printk(version);
+#endif
+
+	printk(KERN_INFO "xircom_init_one(%s)\n", pdev->slot_name);
+
+	board_idx++;
+
+	if (pci_enable_device(pdev))
+		return -ENODEV;
+
+	pci_set_master(pdev);
+
+	ioaddr = pci_resource_start(pdev, 0);
+	dev = alloc_etherdev(sizeof(*tp));
+	if (!dev) {
+		printk (KERN_ERR DRV_NAME "%d: cannot alloc etherdev, aborting\n", board_idx);
+		return -ENOMEM;
+	}
+	SET_MODULE_OWNER(dev);
 
 	dev->base_addr = ioaddr;
-	dev->irq = irq;
+	dev->irq = pdev->irq;
 
-	/* Make certain the data structures are quadword aligned. */
-	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
-	memset(tp, 0, sizeof(*tp));
-	dev->priv = tp;
+	if (pci_request_regions(pdev, dev->name)) {
+		printk (KERN_ERR DRV_NAME " %d: cannot reserve PCI resources, aborting\n", board_idx);
+		goto err_out_free_netdev;
+	}
+
+	/* Bring the chip out of sleep mode.
+	   Caution: Snooze mode does not work with some boards! */
+	if (xircom_tbl[chip_idx].flags & HAS_ACPI)
+		pci_write_config_dword(pdev, PCI_POWERMGMT, 0);
+
+	/* Stop the chip's Tx and Rx processes. */
+	outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr);
+	/* Clear the missed-packet counter. */
+	(volatile int)inl(ioaddr + CSR8);
+
+	tp = dev->priv;
 
 	tp->lock = SPIN_LOCK_UNLOCKED;
 	tp->pdev = pdev;
 	tp->chip_id = chip_idx;
-	tp->revision = chip_rev;
-	tp->csr0 = csr0;
-	tp->setup_frame = (u16 *)(((unsigned long)tp->setup_buf + 7) & ~7);
-
-	/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
-	   And the ASIX must have a burst limit or horrible things happen. */
-	if (chip_idx == X3201_3)
-		tp->csr0 &= ~0x01000000;
+	/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. */
+	/* XXX: is this necessary for Xircom? */
+	tp->csr0 = csr0 & ~EnableMWI;
+
+	pci_set_drvdata(pdev, dev);
 
 	/* The lower four bits are the media type. */
-	if (board_idx >= 0  &&  board_idx < MAX_UNITS) {
+	if (board_idx >= 0 && board_idx < MAX_UNITS) {
 		tp->default_port = options[board_idx] & 15;
 		if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0)
 			tp->full_duplex = 1;
@@ -482,104 +586,58 @@
 	if (dev->mem_start)
 		tp->default_port = dev->mem_start;
 	if (tp->default_port) {
-		tp->medialock = 1;
 		if (media_cap[tp->default_port] & MediaAlwaysFD)
 			tp->full_duplex = 1;
 	}
 	if (tp->full_duplex)
-		tp->full_duplex_lock = 1;
-
-	if (media_cap[tp->default_port] & MediaIsMII) {
-		u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 };
-		tp->to_advertise = media2advert[tp->default_port - 9];
-	} else
-		tp->to_advertise = 0x03e1;
-
-	if (tulip_tbl[chip_idx].flags & HAS_MII) {
-		int phy, phy_idx;
-		/* Find the connected MII xcvrs.
-		   Doing this in open() would allow detecting external xcvrs later,
-		   but takes much time. */
-		for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
-			 phy++) {
-			int mii_status = mdio_read(dev, phy, 1);
-			if ((mii_status & 0x8301) == 0x8001 ||
-				((mii_status & 0x8000) == 0  && (mii_status & 0x7800) != 0)) {
-				int mii_reg0 = mdio_read(dev, phy, 0);
-				int mii_advert = mdio_read(dev, phy, 4);
-				int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
-				tp->phys[phy_idx] = phy;
-				tp->advertising[phy_idx++] = reg4;
-				printk(KERN_INFO "xircom(%s):  MII transceiver #%d "
-					   "config %4.4x status %4.4x advertising %4.4x.\n",
-					   pdev->slot_name, phy, mii_reg0, mii_status, mii_advert);
-				/* Fixup for DLink with miswired PHY. */
-				if (mii_advert != reg4) {
-					printk(KERN_DEBUG "xircom(%s):  Advertising %4.4x on PHY %d,"
-						   " previously advertising %4.4x.\n",
-						   pdev->slot_name, reg4, phy, mii_advert);
-					mdio_write(dev, phy, 4, reg4);
-				}
-				/* Enable autonegotiation: some boards default to off. */
-				mdio_write(dev, phy, 0, mii_reg0 |
-						   (tp->full_duplex ? 0x1100 : 0x1000) |
-						   (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
-			}
-		}
-		tp->mii_cnt = phy_idx;
-		if (phy_idx == 0) {
-			printk(KERN_INFO "xircom(%s): ***WARNING***: No MII transceiver found!\n",
-			       pdev->slot_name);
-			tp->phys[0] = 1;
-		}
-	}
+		tp->autoneg = 0;
+	else
+		tp->autoneg = 1;
+	tp->speed100 = 1;
 
-	/* The Tulip-specific entries in the device structure. */
-	dev->open = &tulip_open;
-	dev->hard_start_xmit = &tulip_start_xmit;
-	dev->stop = &tulip_close;
-	dev->get_stats = &tulip_get_stats;
-#ifdef HAVE_PRIVATE_IOCTL
-	dev->do_ioctl = &private_ioctl;
-#endif
+	/* The Xircom-specific entries in the device structure. */
+	dev->open = &xircom_open;
+	dev->hard_start_xmit = &xircom_start_xmit;
+	dev->stop = &xircom_close;
+	dev->get_stats = &xircom_get_stats;
+	dev->do_ioctl = &xircom_ioctl;
 #ifdef HAVE_MULTICAST
 	dev->set_multicast_list = &set_rx_mode;
 #endif
-	dev->tx_timeout = tulip_tx_timeout;
+	dev->tx_timeout = xircom_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
 
-	/* Reset the xcvr interface and turn on heartbeat. */
-	switch (chip_idx) {
-	case X3201_3:
-		outl(0x0008, ioaddr + CSR15);
-		udelay(5);  /* The delays are Xircom recommended to give the
-					 * chipset time to reset the actual hardware
-					 * on the PCMCIA card
-					 */
-		outl(0xa8050000, ioaddr + CSR15);
-		udelay(5);
-		outl(0xa00f0000, ioaddr + CSR15);
-		udelay(5);
-		outl_CSR6(0x32000200, ioaddr, chip_idx);
-		break;
-	}
-
-	if (register_netdev(dev)) {
-		request_region(ioaddr, tulip_tbl[chip_idx].io_size, "xircom_tulip_cb");
-		kfree(dev->priv);
-		kfree(dev);
-		return NULL;
-	}
+	transceiver_voodoo(dev);
+
+	read_mac_address(dev);
 
+	if (register_netdev(dev))
+		goto err_out_cleardev;
+
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev);
 	printk(KERN_INFO "%s: %s rev %d at %#3lx,",
-	       dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
+	       dev->name, xircom_tbl[chip_idx].chip_name, chip_rev, ioaddr);
 	for (i = 0; i < 6; i++)
 		printk("%c%2.2X", i ? ':' : ' ', dev->dev_addr[i]);
-	printk(", IRQ %d.\n", irq);
+	printk(", IRQ %d.\n", dev->irq);
 
-	return dev;
+	if (xircom_tbl[chip_idx].flags & HAS_MII) {
+		find_mii_transceivers(dev);
+		check_duplex(dev);
+	}
+
+	return 0;
+
+err_out_cleardev:
+	pci_set_drvdata(pdev, NULL);
+	pci_release_regions(pdev);
+err_out_free_netdev:
+	unregister_netdev(dev);
+	kfree(dev);
+	return -ENODEV;
 }
 
+
 /* MII transceiver control section.
    Read and write the MII registers using software-generated serial
    MDIO protocol.  See the MII specifications or DP83840A data sheet
@@ -635,10 +693,11 @@
 	return (retval>>1) & 0xffff;
 }
 
+
 static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
 {
 	int i;
-	int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+	int cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
 	long ioaddr = dev->base_addr;
 	long mdio_addr = ioaddr + CSR9;
 
@@ -667,58 +726,23 @@
 	return;
 }
 
+
 static void
-tulip_up(struct net_device *dev)
+xircom_up(struct net_device *dev)
 {
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 	long ioaddr = dev->base_addr;
 	int i;
 
-	/* On some chip revs we must set the MII/SYM port before the reset!? */
-	if (tp->mii_cnt)
-		outl_CSR6(0x00040000, ioaddr, tp->chip_id);
-
-	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
-	outl(0x00000001, ioaddr + CSR0);
-
-	/* Deassert reset. */
-	outl(tp->csr0, ioaddr + CSR0);
-	udelay(2);
-
-	if (tulip_tbl[tp->chip_id].flags & HAS_ACPI)
-		pci_write_config_dword(tp->pdev, 0x40, 0x00000000);
-
 	/* Clear the tx ring */
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		tp->tx_skbuff[i] = 0;
-		tp->tx_ring[i].status = 0x00000000;
+		tp->tx_ring[i].status = 0;
 	}
 
-	if (tulip_debug > 1)
-		printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+	if (xircom_debug > 1)
+		printk(KERN_DEBUG "%s: xircom_up() irq %d.\n", dev->name, dev->irq);
 
-	{ /* X3201_3 */
-		u16 *eaddrs = (u16 *)dev->dev_addr;
-		u16 *setup_frm = &tp->setup_frame[0*6];
-		
-		/* fill the table with the broadcast address */
-		memset(tp->setup_frame, 0xff, 96*sizeof(u16));
-		/* re-fill the first 14 table entries with our address */
-		for(i=0; i<14; i++) {
-			*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-			*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-			*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-		}
-
-		/* Put the setup frame on the Tx list. */
-		tp->tx_ring[tp->cur_tx].length = 0x68000000 | 192;
-		/* Lie about the address of our setup frame to make the */
-		/* chip happy */
-		tp->tx_ring[tp->cur_tx].buffer1 = virt_to_bus(tp->setup_frame);
-		tp->tx_ring[tp->cur_tx].status = DescOwned;
-
-		tp->cur_tx++;
-	}
 	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
 	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
 
@@ -726,153 +750,65 @@
 	if (dev->if_port == 0)
 		dev->if_port = tp->default_port;
 
-	/* Allow selecting a default media. */
-	tp->csr6 = 0;
-	if (tp->chip_id == X3201_3) {
-		outl(0x0008, ioaddr + CSR15);
-		udelay(5);
-		outl(0xa8050000, ioaddr + CSR15);
-		udelay(5);
-		outl(0xa00f0000, ioaddr + CSR15); 
-		udelay(5);
-		tp->csr6  = 0x32400000;
-	}
+	tp->csr6 = TxThresh10 /*| FullDuplexBit*/;						/* XXX: why 10 and not 100? */
+
+	set_rx_mode(dev);
+
 	/* Start the chip's Tx to process setup frame. */
-	outl_CSR6(tp->csr6, ioaddr, tp->chip_id);
-	outl_CSR6(tp->csr6 | 0x2000, ioaddr, tp->chip_id);
+	outl_CSR6(tp->csr6, ioaddr);
+	outl_CSR6(tp->csr6 | EnableTx, ioaddr);
 
+	/* Acknowledge all outstanding interrupts sources */
+	outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
 	/* Enable interrupts by setting the interrupt mask. */
-	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
-	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
-	outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-	outl(0, ioaddr + CSR2);		/* Rx poll demand */
-	
+	outl(xircom_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
+	/* Enable Rx */
+	outl_CSR6(tp->csr6 | EnableTxRx, ioaddr);
+	/* Rx poll demand */
+	outl(0, ioaddr + CSR2);
+
+	/* Tell the net layer we're ready */
 	netif_start_queue (dev);
 
-	if (tulip_debug > 2) {
-		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
+	if (xircom_debug > 2) {
+		printk(KERN_DEBUG "%s: Done xircom_up(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
 			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
 			   inl(ioaddr + CSR6));
 	}
-	/* 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(5*HZ);
-	tp->timer.data = (unsigned long)dev;
-	tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
-	add_timer(&tp->timer);
 }
 
+
 static int
-tulip_open(struct net_device *dev)
+xircom_open(struct net_device *dev)
 {
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 
-	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
+	if (request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev))
 		return -EAGAIN;
 
-	tulip_init_ring(dev);
+	xircom_init_ring(dev);
 
-	tulip_up(dev);
+	xircom_up(dev);
 	tp->open = 1;
-	MOD_INC_USE_COUNT;
-
-	return 0;
-}
-
-/*
-  Check the MII negotiated duplex, and change the CSR6 setting if
-  required.
-  Return 0 if everything is OK.
-  Return < 0 if the transceiver is missing or has no link beat.
-  */
-#if 0
-static int check_duplex(struct net_device *dev)
-{
-	long ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int mii_reg1, mii_reg5, negotiated, duplex;
 
-	if (tp->full_duplex_lock)
-		return 0;
-	mii_reg1 = mdio_read(dev, tp->phys[0], 1);
-	mii_reg5 = mdio_read(dev, tp->phys[0], 5);
-	if (tulip_debug > 1)
-		printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
-			   "%4.4x.\n", dev->name, mii_reg1, mii_reg5);
-	if (mii_reg1 == 0xffff)
-		return -2;
-	if ((mii_reg1 & 0x0004) == 0) {
-		int new_reg1 = mdio_read(dev, tp->phys[0], 1);
-		if ((new_reg1 & 0x0004) == 0) {
-			if (tulip_debug  > 1)
-				printk(KERN_INFO "%s: No link beat on the MII interface,"
-					   " status %4.4x.\n", dev->name, new_reg1);
-			return -1;
-		}
-	}
-	negotiated = mii_reg5 & tp->advertising[0];
-	duplex = ((negotiated & 0x0300) == 0x0100
-			  || (negotiated & 0x00C0) == 0x0040);
-	/* 100baseTx-FD  or  10T-FD, but not 100-HD */
-	if (tp->full_duplex != duplex) {
-		tp->full_duplex = duplex;
-		if (tp->full_duplex) tp->csr6 |= 0x0200;
-		else				 tp->csr6 &= ~0x0200;
-		outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-		outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-		if (tulip_debug > 0)
-			printk(KERN_INFO "%s: Setting %s-duplex based on MII"
-				   "#%d link partner capability of %4.4x.\n",
-				   dev->name, tp->full_duplex ? "full" : "half",
-				   tp->phys[0], mii_reg5);
-	}
 	return 0;
 }
-#endif
-
-static void tulip_timer(unsigned long data)
-{
-	struct net_device *dev = (struct net_device *)data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	long ioaddr = dev->base_addr;
-	u32 csr12 = inl(ioaddr + CSR12);
-	int next_tick = 2*HZ;
-
-	if (tulip_debug > 2) {
-		printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
-			   "SIA %8.8x %8.8x %8.8x %8.8x.\n",
-			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
-			   csr12, inl(ioaddr + CSR13),
-			   inl(ioaddr + CSR14), inl(ioaddr + CSR15));
-	}
 
-	/* Not much that can be done.
-	   Assume this a generic MII or SYM transceiver. */
-	next_tick = 60*HZ;
-	if (tulip_debug > 2)
-		printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x "
-		       "CSR12 0x%2.2x.\n",
-		       dev->name, inl(ioaddr + CSR6), csr12 & 0xff);
-
-	tp->timer.expires = RUN_AT(next_tick);
-	add_timer(&tp->timer);
-}
 
-static void tulip_tx_timeout(struct net_device *dev)
+static void xircom_tx_timeout(struct net_device *dev)
 {
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 	long ioaddr = dev->base_addr;
 
 	if (media_cap[dev->if_port] & MediaIsMII) {
 		/* Do nothing -- the media monitor should handle this. */
-		if (tulip_debug > 1)
+		if (xircom_debug > 1)
 			printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
 				   dev->name);
 	}
 
 #if defined(way_too_many_messages)
-	if (tulip_debug > 3) {
+	if (xircom_debug > 3) {
 		int i;
 		for (i = 0; i < RX_RING_SIZE; i++) {
 			u8 *buf = (u8 *)(tp->rx_ring[i].buffer1);
@@ -898,9 +834,9 @@
 	}
 #endif
 
-	/* Stop and restart the chip's Tx processes . */
-	outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-	outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+	/* Stop and restart the chip's Tx/Rx processes . */
+	outl_CSR6(tp->csr6 | EnableRx, ioaddr);
+	outl_CSR6(tp->csr6 | EnableTxRx, ioaddr);
 	/* Trigger an immediate transmit demand. */
 	outl(0, ioaddr + CSR1);
 
@@ -909,10 +845,11 @@
 	tp->stats.tx_errors++;
 }
 
+
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void tulip_init_ring(struct net_device *dev)
+static void xircom_init_ring(struct net_device *dev)
 {
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 	int i;
 
 	tp->tx_full = 0;
@@ -920,13 +857,13 @@
 	tp->dirty_rx = tp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = 0x00000000;
+		tp->rx_ring[i].status = 0;
 		tp->rx_ring[i].length = PKT_BUF_SZ;
 		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
 		tp->rx_skbuff[i] = NULL;
 	}
 	/* Mark the last entry as wrapping the ring. */
-	tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP;
+	tp->rx_ring[i-1].length = PKT_BUF_SZ | Rx1RingWrap;
 	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
@@ -938,7 +875,7 @@
 		if (skb == NULL)
 			break;
 		skb->dev = dev;			/* Mark as being used by this device. */
-		tp->rx_ring[i].status = DescOwned;	/* Owned by Tulip chip */
+		tp->rx_ring[i].status = Rx0DescOwned;	/* Owned by Xircom chip */
 		tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
 	}
 	tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
@@ -947,7 +884,7 @@
 	   do need to clear the ownership bit. */
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		tp->tx_skbuff[i] = 0;
-		tp->tx_ring[i].status = 0x00000000;
+		tp->tx_ring[i].status = 0;
 		tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
 #ifdef CARDBUS
 		if (tp->chip_id == X3201_3)
@@ -957,10 +894,11 @@
 	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
 }
 
+
 static int
-tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
+xircom_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 	int entry;
 	u32 flag;
 
@@ -980,21 +918,21 @@
 		tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
 
 	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
-		flag = 0x60000000; /* No interrupt */
+		flag = Tx1WholePkt; /* No interrupt */
 	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
-		flag = 0xe0000000; /* Tx-done intr. */
+		flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */
 	} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
-		flag = 0x60000000; /* No Tx-done intr. */
+		flag = Tx1WholePkt; /* No Tx-done intr. */
 	} else {
 		/* Leave room for set_rx_mode() to fill entries. */
-		flag = 0xe0000000; /* Tx-done intr. */
+		flag = Tx1WholePkt | Tx1ComplIntr; /* Tx-done intr. */
 		tp->tx_full = 1;
 	}
-	if (entry == TX_RING_SIZE-1)
-		flag |= 0xe0000000 | DESC_RING_WRAP;
+	if (entry == TX_RING_SIZE - 1)
+		flag |= Tx1WholePkt | Tx1ComplIntr | Tx1RingWrap;
 
 	tp->tx_ring[entry].length = skb->len | flag;
-	tp->tx_ring[entry].status = DescOwned;	/* Pass ownership to the chip. */
+	tp->tx_ring[entry].status = Tx0DescOwned;	/* Pass ownership to the chip. */
 	tp->cur_tx++;
 	if (tp->tx_full)
 		netif_stop_queue (dev);
@@ -1009,12 +947,102 @@
 	return 0;
 }
 
+
+static void xircom_media_change(struct net_device *dev)
+{
+	struct xircom_private *tp = dev->priv;
+	long ioaddr = dev->base_addr;
+	u16 reg0, reg1, reg4, reg5;
+	u32 csr6 = inl(ioaddr + CSR6), newcsr6;
+
+	/* reset status first */
+	mdio_read(dev, tp->phys[0], MII_BMCR);
+	mdio_read(dev, tp->phys[0], MII_BMSR);
+
+	reg0 = mdio_read(dev, tp->phys[0], MII_BMCR);
+	reg1 = mdio_read(dev, tp->phys[0], MII_BMSR);
+
+	if (reg1 & BMSR_LSTATUS) {
+		/* link is up */
+		if (reg0 & BMCR_ANENABLE) {
+			/* autonegotiation is enabled */
+			reg4 = mdio_read(dev, tp->phys[0], MII_ADVERTISE);
+			reg5 = mdio_read(dev, tp->phys[0], MII_LPA);
+			if (reg4 & ADVERTISE_100FULL && reg5 & LPA_100FULL) {
+				tp->speed100 = 1;
+				tp->full_duplex = 1;
+			} else if (reg4 & ADVERTISE_100HALF && reg5 & LPA_100HALF) {
+				tp->speed100 = 1;
+				tp->full_duplex = 0;
+			} else if (reg4 & ADVERTISE_10FULL && reg5 & LPA_10FULL) {
+				tp->speed100 = 0;
+				tp->full_duplex = 1;
+			} else {
+				tp->speed100 = 0;
+				tp->full_duplex = 0;
+			}
+		} else {
+			/* autonegotiation is disabled */
+			if (reg0 & BMCR_SPEED100)
+				tp->speed100 = 1;
+			else
+				tp->speed100 = 0;
+			if (reg0 & BMCR_FULLDPLX)
+				tp->full_duplex = 1;
+			else
+				tp->full_duplex = 0;
+		}
+		printk(KERN_DEBUG "%s: Link is up, running at %sMbit %s-duplex\n",
+		       dev->name,
+		       tp->speed100 ? "100" : "10",
+		       tp->full_duplex ? "full" : "half");
+		newcsr6 = csr6 & ~FullDuplexBit;
+		if (tp->full_duplex)
+			newcsr6 |= FullDuplexBit;
+		if (newcsr6 != csr6)
+			outl_CSR6(newcsr6, ioaddr + CSR6);
+	} else {
+		printk(KERN_DEBUG "%s: Link is down\n", dev->name);
+	}
+}
+
+
+static void check_duplex(struct net_device *dev)
+{
+	struct xircom_private *tp = dev->priv;
+	u16 reg0;
+
+	mdio_write(dev, tp->phys[0], MII_BMCR, BMCR_RESET);
+	udelay(500);
+	while (mdio_read(dev, tp->phys[0], MII_BMCR) & BMCR_RESET);
+
+	reg0 = mdio_read(dev, tp->phys[0], MII_BMCR);
+	mdio_write(dev, tp->phys[0], MII_ADVERTISE, tp->advertising[0]);
+
+	if (tp->autoneg) {
+		reg0 &= ~(BMCR_SPEED100 | BMCR_FULLDPLX);
+		reg0 |= BMCR_ANENABLE | BMCR_ANRESTART;
+	} else {
+		reg0 &= ~(BMCR_ANENABLE | BMCR_ANRESTART);
+		if (tp->speed100)
+			reg0 |= BMCR_SPEED100;
+		if (tp->full_duplex)
+			reg0 |= BMCR_FULLDPLX;
+		printk(KERN_DEBUG "%s: Link forced to %sMbit %s-duplex\n",
+		       dev->name,
+		       tp->speed100 ? "100" : "10",
+		       tp->full_duplex ? "full" : "half");
+	}
+	mdio_write(dev, tp->phys[0], MII_BMCR, reg0);
+}
+
+
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+static void xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
-	struct net_device *dev = (struct net_device *)dev_instance;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct net_device *dev = dev_instance;
+	struct xircom_private *tp = dev->priv;
 	long ioaddr = dev->base_addr;
 	int csr5, work_budget = max_interrupt_work;
 
@@ -1025,7 +1053,7 @@
 		/* Acknowledge all of the current interrupt sources ASAP. */
 		outl(csr5 & 0x0001ffff, ioaddr + CSR5);
 
-		if (tulip_debug > 4)
+		if (xircom_debug > 4)
 			printk(KERN_DEBUG "%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
 				   dev->name, csr5, inl(dev->base_addr + CSR5));
 
@@ -1036,7 +1064,7 @@
 			break;
 
 		if (csr5 & (RxIntr | RxNoBuf))
-			work_budget -= tulip_rx(dev);
+			work_budget -= xircom_rx(dev);
 
 		if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
 			unsigned int dirty_tx;
@@ -1052,27 +1080,24 @@
 				if (tp->tx_skbuff[entry] == NULL)
 				  continue;
 
-				if (status & 0x8000) {
+				if (status & Tx0DescError) {
 					/* There was an major error, log it. */
 #ifndef final_version
-					if (tulip_debug > 1)
+					if (xircom_debug > 1)
 						printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
 							   dev->name, status);
 #endif
 					tp->stats.tx_errors++;
-					if (status & 0x4104) tp->stats.tx_aborted_errors++;
-					if (status & 0x0C00) tp->stats.tx_carrier_errors++;
-					if (status & 0x0200) tp->stats.tx_window_errors++;
-					if (status & 0x0002) tp->stats.tx_fifo_errors++;
-					if ((status & 0x0080) && tp->full_duplex == 0)
-						tp->stats.tx_heartbeat_errors++;
+					if (status & Tx0ManyColl) {
+						tp->stats.tx_aborted_errors++;
 #ifdef ETHER_STATS
-					if (status & 0x0100) tp->stats.collisions16++;
+						tp->stats.collisions16++;
 #endif
+					}
+					if (status & Tx0NoCarrier) tp->stats.tx_carrier_errors++;
+					if (status & Tx0LateColl) tp->stats.tx_window_errors++;
+					if (status & Tx0Underflow) tp->stats.tx_fifo_errors++;
 				} else {
-#ifdef ETHER_STATS
-					if (status & 0x0001) tp->stats.tx_deferred++;
-#endif
 					tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
 					tp->stats.collisions += (status >> 3) & 15;
 					tp->stats.tx_packets++;
@@ -1091,11 +1116,11 @@
 			}
 #endif
 
-			if (tp->tx_full && 
+			if (tp->tx_full &&
 			    tp->cur_tx - dirty_tx  < TX_RING_SIZE - 2)
 				/* The ring is no longer full */
 				tp->tx_full = 0;
-			
+
 			if (tp->tx_full)
 				netif_stop_queue (dev);
 			else
@@ -1103,104 +1128,92 @@
 
 			tp->dirty_tx = dirty_tx;
 			if (csr5 & TxDied) {
-				if (tulip_debug > 2)
+				if (xircom_debug > 2)
 					printk(KERN_WARNING "%s: The transmitter stopped."
 						   "  CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
 						   dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
-				outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+				outl_CSR6(tp->csr6 | EnableRx, ioaddr);
+				outl_CSR6(tp->csr6 | EnableTxRx, ioaddr);
 			}
 		}
 
 		/* Log errors. */
 		if (csr5 & AbnormalIntr) {	/* Abnormal error summary bit. */
-			if (csr5 == 0xffffffff)
-				break;
-			if (csr5 & TxJabber) tp->stats.tx_errors++;
+			if (csr5 & LinkChange)
+				xircom_media_change(dev);
 			if (csr5 & TxFIFOUnderflow) {
-				if ((tp->csr6 & 0xC000) != 0xC000)
-					tp->csr6 += 0x4000;	/* Bump up the Tx threshold */
+				if ((tp->csr6 & TxThreshMask) != TxThreshMask)
+					tp->csr6 += (1 << TxThreshShift);	/* Bump up the Tx threshold */
 				else
-					tp->csr6 |= 0x00200000;  /* Store-n-forward. */
+					tp->csr6 |= TxStoreForw;  /* Store-n-forward. */
 				/* Restart the transmit process. */
-				outl_CSR6(tp->csr6 | 0x0002, ioaddr, tp->chip_id);
-				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
+				outl_CSR6(tp->csr6 | EnableRx, ioaddr);
+				outl_CSR6(tp->csr6 | EnableTxRx, ioaddr);
 			}
 			if (csr5 & RxDied) {		/* Missed a Rx frame. */
 				tp->stats.rx_errors++;
 				tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
-				outl_CSR6(tp->csr6 | 0x2002, ioaddr, tp->chip_id);
-			}
-			if (csr5 & TimerInt) {
-				if (tulip_debug > 2)
-					printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n",
-						   dev->name, csr5);
-				outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
+				outl_CSR6(tp->csr6 | EnableTxRx, ioaddr);
 			}
 			/* Clear all error sources, included undocumented ones! */
 			outl(0x0800f7ba, ioaddr + CSR5);
 		}
 		if (--work_budget < 0) {
-			if (tulip_debug > 1)
+			if (xircom_debug > 1)
 				printk(KERN_WARNING "%s: Too much work during an interrupt, "
 					   "csr5=0x%8.8x.\n", dev->name, csr5);
 			/* Acknowledge all interrupt sources. */
 			outl(0x8001ffff, ioaddr + CSR5);
-#ifdef notdef
-			/* Clear all but standard interrupt sources. */
-			outl((~csr5) & 0x0001ebef, ioaddr + CSR7);
-#endif
 			break;
 		}
 	} while (1);
 
-	if (tulip_debug > 3)
+	if (xircom_debug > 3)
 		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
 			   dev->name, inl(ioaddr + CSR5));
 
 	spin_unlock (&tp->lock);
 }
 
+
 static int
-tulip_rx(struct net_device *dev)
+xircom_rx(struct net_device *dev)
 {
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 	int entry = tp->cur_rx % RX_RING_SIZE;
 	int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx;
 	int work_done = 0;
 
-	if (tulip_debug > 4)
-		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
+	if (xircom_debug > 4)
+		printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry,
 			   tp->rx_ring[entry].status);
 	/* If we own the next entry, it's a new packet. Send it up. */
 	while (tp->rx_ring[entry].status >= 0) {
 		s32 status = tp->rx_ring[entry].status;
 
-		if (tulip_debug > 5)
-			printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
+		if (xircom_debug > 5)
+			printk(KERN_DEBUG " In xircom_rx(), entry %d %8.8x.\n", entry,
 				   tp->rx_ring[entry].status);
 		if (--rx_work_limit < 0)
 			break;
 		if ((status & 0x38008300) != 0x0300) {
 			if ((status & 0x38000300) != 0x0300) {
-				/* Ingore earlier buffers. */
+				/* Ignore earlier buffers. */
 				if ((status & 0xffff) != 0x7fff) {
-					if (tulip_debug > 1)
+					if (xircom_debug > 1)
 						printk(KERN_WARNING "%s: Oversized Ethernet frame "
 							   "spanned multiple buffers, status %8.8x!\n",
 							   dev->name, status);
 					tp->stats.rx_length_errors++;
 				}
-			} else if (status & RxDescFatalErr) {
+			} else if (status & Rx0DescError) {
 				/* There was a fatal error. */
-				if (tulip_debug > 2)
+				if (xircom_debug > 2)
 					printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
 						   dev->name, status);
 				tp->stats.rx_errors++; /* end of a packet.*/
-				if (status & 0x0890) tp->stats.rx_length_errors++;
-				if (status & 0x0004) tp->stats.rx_frame_errors++;
-				if (status & 0x0002) tp->stats.rx_crc_errors++;
-				if (status & 0x0001) tp->stats.rx_fifo_errors++;
+				if (status & (Rx0Runt | Rx0HugeFrame)) tp->stats.rx_length_errors++;
+				if (status & Rx0CRCError) tp->stats.rx_crc_errors++;
 			}
 		} else {
 			/* Omit the four octet CRC from the length. */
@@ -1231,15 +1244,8 @@
 #endif
 				work_done++;
 			} else { 	/* Pass up the skb already on the Rx ring. */
-				char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
+				skb_put(skb = tp->rx_skbuff[entry], pkt_len);
 				tp->rx_skbuff[entry] = NULL;
-#ifndef final_version
-				if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp)
-					printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
-						   "do not match in tulip_rx: %p vs. %p / %p.\n",
-						   dev->name, bus_to_virt(tp->rx_ring[entry].buffer1),
-						   skb->head, temp);
-#endif
 			}
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
@@ -1262,24 +1268,23 @@
 			tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail);
 			work_done++;
 		}
-		tp->rx_ring[entry].status = DescOwned;
+		tp->rx_ring[entry].status = Rx0DescOwned;
 	}
 
 	return work_done;
 }
 
+
 static void
-tulip_down(struct net_device *dev)
+xircom_down(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-
-	del_timer_sync(&tp->timer);
+	struct xircom_private *tp = dev->priv;
 
 	/* Disable interrupts by clearing the interrupt mask. */
-	outl(0x00000000, ioaddr + CSR7);
+	outl(0, ioaddr + CSR7);
 	/* Stop the chip's Tx and Rx processes. */
-	outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr, tp->chip_id);
+	outl_CSR6(inl(ioaddr + CSR6) & ~EnableTxRx, ioaddr);
 
 	if (inl(ioaddr + CSR6) != 0xffffffff)
 		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
@@ -1287,23 +1292,22 @@
 	dev->if_port = tp->saved_if_port;
 }
 
+
 static int
-tulip_close(struct net_device *dev)
+xircom_close(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 	int i;
 
-	if (tulip_debug > 1)
+	if (xircom_debug > 1)
 		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
 			   dev->name, inl(ioaddr + CSR5));
 
-	del_timer_sync(&tp->timer);
-
 	netif_stop_queue(dev);
 
 	if (netif_device_present(dev))
-		tulip_down(dev);
+		xircom_down(dev);
 
 	free_irq(dev->irq, dev);
 
@@ -1311,7 +1315,7 @@
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		struct sk_buff *skb = tp->rx_skbuff[i];
 		tp->rx_skbuff[i] = 0;
-		tp->rx_ring[i].status = 0;		/* Not owned by Tulip chip. */
+		tp->rx_ring[i].status = 0;		/* Not owned by Xircom chip. */
 		tp->rx_ring[i].length = 0;
 		tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
 		if (skb) {
@@ -1324,14 +1328,14 @@
 		tp->tx_skbuff[i] = 0;
 	}
 
-	MOD_DEC_USE_COUNT;
 	tp->open = 0;
 	return 0;
 }
 
-static struct net_device_stats *tulip_get_stats(struct net_device *dev)
+
+static struct net_device_stats *xircom_get_stats(struct net_device *dev)
 {
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 	long ioaddr = dev->base_addr;
 
 	if (netif_device_present(dev))
@@ -1340,44 +1344,158 @@
 	return &tp->stats;
 }
 
-#ifdef HAVE_PRIVATE_IOCTL
+
+static int xircom_ethtool_ioctl(struct net_device *dev, void *useraddr)
+{
+	struct ethtool_cmd ecmd;
+	struct xircom_private *tp = dev->priv;
+
+	if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+		return -EFAULT;
+
+	switch (ecmd.cmd) {
+	case ETHTOOL_GSET:
+		ecmd.supported =
+			SUPPORTED_10baseT_Half |
+			SUPPORTED_10baseT_Full |
+			SUPPORTED_100baseT_Half |
+			SUPPORTED_100baseT_Full |
+			SUPPORTED_Autoneg |
+			SUPPORTED_MII;
+
+		ecmd.advertising = ADVERTISED_MII;
+		if (tp->advertising[0] & ADVERTISE_10HALF)
+			ecmd.advertising |= ADVERTISED_10baseT_Half;
+		if (tp->advertising[0] & ADVERTISE_10FULL)
+			ecmd.advertising |= ADVERTISED_10baseT_Full;
+		if (tp->advertising[0] & ADVERTISE_100HALF)
+			ecmd.advertising |= ADVERTISED_100baseT_Half;
+		if (tp->advertising[0] & ADVERTISE_100FULL)
+			ecmd.advertising |= ADVERTISED_100baseT_Full;
+		if (tp->autoneg) {
+			ecmd.advertising |= ADVERTISED_Autoneg;
+			ecmd.autoneg = AUTONEG_ENABLE;
+		} else
+			ecmd.autoneg = AUTONEG_DISABLE;
+
+		ecmd.port = PORT_MII;
+		ecmd.transceiver = XCVR_INTERNAL;
+		ecmd.phy_address = tp->phys[0];
+		ecmd.speed = tp->speed100 ? SPEED_100 : SPEED_10;
+		ecmd.duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+		ecmd.maxtxpkt = TX_RING_SIZE / 2;
+		ecmd.maxrxpkt = 0;
+
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+
+	case ETHTOOL_SSET: {
+		u16 autoneg, speed100, full_duplex;
+
+		autoneg = (ecmd.autoneg == AUTONEG_ENABLE);
+		speed100 = (ecmd.speed == SPEED_100);
+		full_duplex = (ecmd.duplex == DUPLEX_FULL);
+
+		tp->autoneg = autoneg;
+		if (speed100 != tp->speed100 ||
+		    full_duplex != tp->full_duplex) {
+			tp->speed100 = speed100;
+			tp->full_duplex = full_duplex;
+			/* change advertising bits */
+			tp->advertising[0] &= ~(ADVERTISE_10HALF |
+					     ADVERTISE_10FULL |
+					     ADVERTISE_100HALF |
+					     ADVERTISE_100FULL |
+					     ADVERTISE_100BASE4);
+			if (speed100) {
+				if (full_duplex)
+					tp->advertising[0] |= ADVERTISE_100FULL;
+				else
+					tp->advertising[0] |= ADVERTISE_100HALF;
+			} else {
+				if (full_duplex)
+					tp->advertising[0] |= ADVERTISE_10FULL;
+				else
+					tp->advertising[0] |= ADVERTISE_10HALF;
+			}
+		}
+		check_duplex(dev);
+		return 0;
+	}
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info;
+		memset(&info, 0, sizeof(info));
+		info.cmd = ecmd.cmd;
+		strcpy(info.driver, DRV_NAME);
+		strcpy(info.version, DRV_VERSION);
+		*info.fw_version = 0;
+		strcpy(info.bus_info, tp->pdev->slot_name);
+		if (copy_to_user(useraddr, &info, sizeof(info)))
+		       return -EFAULT;
+		return 0;
+	}
+
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+
 /* Provide ioctl() calls to examine the MII xcvr state. */
-static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct xircom_private *tp = dev->priv;
 	u16 *data = (u16 *)&rq->ifr_data;
 	int phy = tp->phys[0] & 0x1f;
 	long flags;
 
 	switch(cmd) {
-	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
+	case SIOCETHTOOL:
+		return xircom_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	/* Legacy mii-diag interface */
+	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
+	case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
 		if (tp->mii_cnt)
 			data[0] = phy;
 		else
 			return -ENODEV;
 		return 0;
-	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
-		{
-			save_flags(flags);
-			cli();
-			data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
-			restore_flags(flags);
-		}
+	case SIOCGMIIREG:		/* Read MII PHY register. */
+	case SIOCDEVPRIVATE+1:		/* for binary compat, remove in 2.5 */
+		save_flags(flags);
+		cli();
+		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+		restore_flags(flags);
 		return 0;
-	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
-#if defined(CAP_NET_ADMIN)
+	case SIOCSMIIREG:		/* Write MII PHY register. */
+	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
-#else
-		if (!suser())
-			return -EPERM;
-#endif
-		{
-			save_flags(flags);
-			cli();
-			mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
-			restore_flags(flags);
+		save_flags(flags);
+		cli();
+		if (data[0] == tp->phys[0]) {
+			u16 value = data[2];
+			switch (data[1]) {
+			case 0:
+				if (value & (BMCR_RESET | BMCR_ANENABLE))
+					/* Autonegotiation. */
+					tp->autoneg = 1;
+				else {
+					tp->full_duplex = (value & BMCR_FULLDPLX) ? 1 : 0;
+					tp->autoneg = 0;
+				}
+				break;
+			case 4:
+				tp->advertising[0] = value;
+				break;
+			}
+			check_duplex(dev);
 		}
+		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+		restore_flags(flags);
 		return 0;
 	default:
 		return -EOPNOTSUPP;
@@ -1385,12 +1503,7 @@
 
 	return -EOPNOTSUPP;
 }
-#endif  /* HAVE_PRIVATE_IOCTL */
 
-/* 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 AUTODIN32 ethernet CRC calculation.
    N.B. Do not use for bulk data, use a table-based routine instead.
@@ -1427,191 +1540,229 @@
     return crc;
 }
 
+
+/* 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. */
 static void set_rx_mode(struct net_device *dev)
 {
+	struct xircom_private *tp = dev->priv;
+	struct dev_mc_list *mclist;
 	long ioaddr = dev->base_addr;
-	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int csr6 = inl(ioaddr + CSR6);
+	u16 *eaddrs, *setup_frm;
+	u32 tx_flags;
+	int i;
 
-	tp->csr6 &= ~0x00D5;
+	tp->csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit);
+	csr6 &= ~(AllMultiBit | PromiscBit | HashFilterBit);
 	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
-		tp->csr6 |= 0x00C0;
-		csr6 |= 0x00C0;
-		/* Unconditionally log net taps. */
-		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
-	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
+		tp->csr6 |= PromiscBit;
+		csr6 |= PromiscBit;
+		goto out;
+	}
+
+	if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) {
 		/* Too many to filter well -- accept all multicasts. */
-		tp->csr6 |= 0x0080;
-		csr6 |= 0x0080;
-	} else {
-		u16 *eaddrs, *setup_frm = tp->setup_frame;
-		struct dev_mc_list *mclist;
-		u32 tx_flags = 0x68000000 | 192;
-		int i;
+		tp->csr6 |= AllMultiBit;
+		csr6 |= AllMultiBit;
+		goto out;
+	}
+
+	tx_flags = Tx1WholePkt | Tx1SetupPkt | PKT_SETUP_SZ;
+
+	/* Note that only the low-address shortword of setup_frame is valid! */
+	setup_frm = tp->setup_frame;
+	mclist = dev->mc_list;
+
+	/* Fill the first entry with our physical address. */
+	eaddrs = (u16 *)dev->dev_addr;
+	*setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2;
+	*setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2;
+	*setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2;
+
+	if (dev->mc_count > 14) { /* Must use a multicast hash table. */
+		u32 *hash_table = (u32 *)(tp->setup_frame + 4 * 12);
+		u32 hash, hash2;
+
+		tx_flags |= Tx1HashSetup;
+		tp->csr6 |= HashFilterBit;
+		csr6 |= HashFilterBit;
+
+		/* Fill the unused 3 entries with the broadcast address.
+		   At least one entry *must* contain the broadcast address!!!*/
+		for (i = 0; i < 3; i++) {
+			*setup_frm = 0xffff; setup_frm += 2;
+			*setup_frm = 0xffff; setup_frm += 2;
+			*setup_frm = 0xffff; setup_frm += 2;
+		}
 
-		/* Note that only the low-address shortword of setup_frame is valid!
-		   The values are doubled for big-endian architectures. */
-		if (dev->mc_count > 14) { /* Must use a multicast hash table. */
-			u16 hash_table[32];
-			tx_flags = 0x68400000 | 192;		/* Use hash filter. */
-			memset(hash_table, 0, sizeof(hash_table));
-			set_bit(255, hash_table); 			/* Broadcast entry */
-			/* This should work on big-endian machines as well. */
-			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) & 0x1ff,
-						hash_table);
-			for (i = 0; i < 32; i++) {
-				*setup_frm++ = hash_table[i];
-				*setup_frm++ = hash_table[i];
-			}
-			setup_frm = &tp->setup_frame[13*6];
-		} else {
-			/* We have <= 14 addresses so we can use the wonderful
-			   16 address perfect filtering of the Tulip. */
-			for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
-				 i++, mclist = mclist->next) {
-				eaddrs = (u16 *)mclist->dmi_addr;
-				*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-				*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-				*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+		/* Truly brain-damaged hash filter layout */
+		/* XXX: not sure if I should take the last or the first 9 bits */
+		for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) {
+			u32 *hptr;
+			hash = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x1ff;
+			if (hash < 384) {
+				hash2 = hash + ((hash >> 4) << 4) +
+					((hash >> 5) << 5);
+			} else {
+				hash -= 384;
+				hash2 = 64 + hash + (hash >> 4) * 80;
 			}
-			/* Fill the unused entries with the broadcast address. */
-			memset(setup_frm, 0xff, (15-i)*12);
-			setup_frm = &tp->setup_frame[15*6];
+			hptr = &hash_table[hash2 & ~0x1f];
+			*hptr |= cpu_to_le32(1 << (hash2 & 0x1f));
 		}
+	} else {
+		/* We have <= 14 mcast addresses so we can use Xircom's
+		   wonderful 16-address perfect filter. */
+		for (i = 0; i < dev->mc_count; i++, mclist = mclist->next) {
+			eaddrs = (u16 *)mclist->dmi_addr;
+			*setup_frm = cpu_to_le16(eaddrs[0]); setup_frm += 2;
+			*setup_frm = cpu_to_le16(eaddrs[1]); setup_frm += 2;
+			*setup_frm = cpu_to_le16(eaddrs[2]); setup_frm += 2;
+		}
+		/* Fill the unused entries with the broadcast address.
+		   At least one entry *must* contain the broadcast address!!!*/
+		for (; i < 15; i++) {
+			*setup_frm = 0xffff; setup_frm += 2;
+			*setup_frm = 0xffff; setup_frm += 2;
+			*setup_frm = 0xffff; setup_frm += 2;
+		}
+	}
 
-		/* Fill the final entry with our physical address. */
-		eaddrs = (u16 *)dev->dev_addr;
-		*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
-		*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
-		*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
-		/* Now add this frame to the Tx list. */
-		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
-			/* Same setup recently queued, we need not add it. */
-		} else {
-			unsigned long flags;
-			unsigned int entry;
-			int dummy = -1;
-
-			save_flags(flags); cli();
-			entry = tp->cur_tx++ % TX_RING_SIZE;
+	/* Now add this frame to the Tx list. */
+	if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
+		/* Same setup recently queued, we need not add it. */
+		/* XXX: Huh? All it means is that the Tx list is full...*/
+	} else {
+		unsigned long flags;
+		unsigned int entry;
+		int dummy = -1;
 
-			if (entry != 0) {
-				/* Avoid a chip errata by prefixing a dummy entry. */
-				tp->tx_skbuff[entry] = 0;
-				tp->tx_ring[entry].length =
-					(entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0;
-				tp->tx_ring[entry].buffer1 = 0;
-				/* race with chip, set DescOwned later */
-				dummy = entry;
-				entry = tp->cur_tx++ % TX_RING_SIZE;
-			}
+		save_flags(flags); cli();
+		entry = tp->cur_tx++ % TX_RING_SIZE;
 
+		if (entry != 0) {
+			/* Avoid a chip errata by prefixing a dummy entry. */
 			tp->tx_skbuff[entry] = 0;
-			/* Put the setup frame on the Tx list. */
-			if (entry == TX_RING_SIZE-1)
-				tx_flags |= DESC_RING_WRAP;		/* Wrap ring. */
-			tp->tx_ring[entry].length = tx_flags;
-			tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
-			tp->tx_ring[entry].status = DescOwned;
-			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
-				tp->tx_full = 1;
-				netif_stop_queue (dev);
-			}
-			if (dummy >= 0)
-				tp->tx_ring[dummy].status = DescOwned;
-			restore_flags(flags);
-			/* Trigger an immediate transmit demand. */
-			outl(0, ioaddr + CSR1);
+			tp->tx_ring[entry].length =
+				(entry == TX_RING_SIZE - 1) ? Tx1RingWrap : 0;
+			tp->tx_ring[entry].buffer1 = 0;
+			/* race with chip, set Tx0DescOwned later */
+			dummy = entry;
+			entry = tp->cur_tx++ % TX_RING_SIZE;
 		}
+
+		tp->tx_skbuff[entry] = 0;
+		/* Put the setup frame on the Tx list. */
+		if (entry == TX_RING_SIZE - 1)
+			tx_flags |= Tx1RingWrap;		/* Wrap ring. */
+		tp->tx_ring[entry].length = tx_flags;
+		tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
+		tp->tx_ring[entry].status = Tx0DescOwned;
+		if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
+			tp->tx_full = 1;
+			netif_stop_queue (dev);
+		}
+		if (dummy >= 0)
+			tp->tx_ring[dummy].status = Tx0DescOwned;
+		restore_flags(flags);
+		/* Trigger an immediate transmit demand. */
+		outl(0, ioaddr + CSR1);
 	}
-	outl_CSR6(csr6 | 0x0000, ioaddr, tp->chip_id);
+
+out:
+	outl_CSR6(csr6, ioaddr);
 }
-
-static  struct pci_device_id tulip_pci_table[] __devinitdata = {
+
+
+static struct pci_device_id xircom_pci_table[] __devinitdata = {
   { 0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 },
   {0},
 };
+MODULE_DEVICE_TABLE(pci, xircom_pci_table);
 
-MODULE_DEVICE_TABLE(pci, tulip_pci_table);
-
-static int __devinit tulip_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
-	struct net_device *dev;
-	static int board_idx;
-
-	printk(KERN_INFO "tulip_attach(%s)\n", pdev->slot_name);
-
-	if (pci_enable_device (pdev))
-		return -ENODEV;
-	pci_set_master (pdev);
-	dev = tulip_probe1(pdev, pci_resource_start (pdev, 0), pdev->irq,
-			   id->driver_data, board_idx++);
-	if (dev) {
-		pdev->driver_data = dev;
-		return 0;
-	}
-	return -ENODEV;
-}
 
-static int tulip_suspend(struct pci_dev *pdev, u32 state)
+#ifdef CONFIG_PM
+static int xircom_suspend(struct pci_dev *pdev, u32 state)
 {
 	struct net_device *dev = pdev->driver_data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	printk(KERN_INFO "tulip_suspend(%s)\n", dev->name);
-	if (tp->open) tulip_down(dev);
+	struct xircom_private *tp = dev->priv;
+	printk(KERN_INFO "xircom_suspend(%s)\n", dev->name);
+	if (tp->open)
+		xircom_down(dev);
 	return 0;
 }
 
-static int tulip_resume(struct pci_dev *pdev)
+
+/* XXX: resume isn't able to power up the MII/PHY! */
+static int xircom_resume(struct pci_dev *pdev)
 {
 	struct net_device *dev = pdev->driver_data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	printk(KERN_INFO "tulip_resume(%s)\n", dev->name);
-	if (tp->open) tulip_up(dev);
+	struct xircom_private *tp = dev->priv;
+	printk(KERN_INFO "xircom_resume(%s)\n", dev->name);
+
+	/* Bring the chip out of sleep mode.
+	   Caution: Snooze mode does not work with some boards! */
+	if (xircom_tbl[tp->chip_id].flags & HAS_ACPI)
+		pci_write_config_dword(tp->pdev, PCI_POWERMGMT, 0);
+
+	transceiver_voodoo(dev);
+	if (xircom_tbl[tp->chip_id].flags & HAS_MII)
+		check_duplex(dev);
+
+	if (tp->open)
+		xircom_up(dev);
 	return 0;
 }
+#endif /* CONFIG_PM */
 
-static void __devexit tulip_remove(struct pci_dev *pdev)
+
+static void __devexit xircom_remove_one(struct pci_dev *pdev)
 {
 	struct net_device *dev = pdev->driver_data;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-	printk(KERN_INFO "tulip_detach(%s)\n", dev->name);
+	printk(KERN_INFO "xircom_remove_one(%s)\n", dev->name);
 	unregister_netdev(dev);
+	pci_release_regions(pdev);
 	kfree(dev);
-	kfree(tp);
+	pci_set_drvdata(pdev, NULL);
 }
 
-static struct pci_driver tulip_ops = {
-	name:		"tulip_cb",
-	id_table:	tulip_pci_table,
-	probe:		tulip_pci_probe,
-	remove:		tulip_remove,
-	suspend:	tulip_suspend,
-	resume:		tulip_resume
+
+static struct pci_driver xircom_driver = {
+	name:		DRV_NAME,
+	id_table:	xircom_pci_table,
+	probe:		xircom_init_one,
+	remove:		xircom_remove_one,
+#ifdef CONFIG_PM
+	suspend:	xircom_suspend,
+	resume:		xircom_resume
+#endif /* CONFIG_PM */
 };
 
-static int __init tulip_init(void)
+
+static int __init xircom_init(void)
 {
-	pci_register_driver(&tulip_ops);
-	return 0;
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+	printk(version);
+#endif
+	return pci_module_init(&xircom_driver);
 }
 
-static void __exit tulip_exit(void)
+
+static void __exit xircom_exit(void)
 {
-	pci_unregister_driver(&tulip_ops);
+	pci_unregister_driver(&xircom_driver);
 }
 
-module_init(tulip_init)
-module_exit(tulip_exit)
+module_init(xircom_init)
+module_exit(xircom_exit)
 
-
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)