patch-2.3.50 linux/drivers/net/cs89x0.c

Next file: linux/drivers/net/cs89x0.h
Previous file: linux/drivers/net/cops_ltdrv.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.49/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
@@ -1,4 +1,7 @@
-/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
+/* cs89x0.c: A Crystal Semiconductor (Now Cirrus Logic) CS89[02]0
+ *  driver for linux.
+ */
+
 /*
 	Written 1996 by Russell Nelson, with reference to skeleton.c
 	written 1993-1994 by Donald Becker.
@@ -6,8 +9,8 @@
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
 
-	The author may be reached at nelson@crynwr.com, Crynwr
-	Software, 11 Grant St., Potsdam, NY 13676
+        The author may be reached at nelson@crynwr.com, Crynwr
+        Software, 521 Pleasant Valley Rd., Potsdam, NY 13676
 
   Changelog:
 
@@ -25,19 +28,30 @@
                     : as an example. Disabled autoprobing in init_module(),
                     : not a good thing to do to other devices while Linux
                     : is running from all accounts.
-                    
+
+  Russ Nelson       : Jul 13 1998.  Added RxOnly DMA support.
+
+  Melody Lee        : Aug 10 1999.  Changes for Linux 2.2.5 compatibility. 
+                    : email: ethernet@crystal.cirrus.com
+
   Alan Cox          : Removed 1.2 support, added 2.1 extra counters.
+
+  Andrew Morton     : andrewm@uow.edu.au
+                    : Kernel 2.3.48
+                    : Handle kmalloc() failures
+                    : Other resource allocation fixes
+                    : Add SMP locks
+                    : Integrate Russ Nelson's ALLOW_DMA functionality back in.
+                    : If ALLOW_DMA is true, make DMA runtime selectable
+                    : Folded in changes from Cirrus (Melody Lee
+                    : <klee@crystal.cirrus.com>)
+                    : Don't call netif_wake_queue() in net_send_packet()
+                    : Fixed an out-of-mem bug in dma_rx()
+                    : Updated Documentation/cs89x0.txt
 */
 
 static char *version =
-"cs89x0.c:v1.03 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
-
-/* ======================= configure the driver here ======================= */
-
-/* use 0 for production, 1 for verification, >2 for debug */
-#ifndef NET_DEBUG
-#define NET_DEBUG 2
-#endif
+"cs89x0.c: (kernel 2.3.48) Russell Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>\n";
 
 /* ======================= end of configuration ======================= */
 
@@ -52,7 +66,19 @@
 #define MOD_DEC_USE_COUNT
 #endif
 
-#define PRINTK(x) printk x
+/*
+ * Set this to zero to disable DMA code
+ *
+ * Note that even if DMA is turned off we still support the 'dma' and  'use_dma'
+ * module options so we don't break any startup scripts.
+ */
+#define ALLOW_DMA	1
+
+/*
+ * Set this to zero to remove all the debug statements via
+ * dead code elimination
+ */
+#define DEBUGGING	0
 
 /*
   Sources:
@@ -76,39 +102,66 @@
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
+#if ALLOW_DMA
+#include <asm/dma.h>
+#endif
 #include <linux/errno.h>
 #include <linux/init.h>
+#include <linux/spinlock.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+
 #include "cs89x0.h"
 
 /* First, a few definitions that the brave might change. */
 /* A zero-terminated list of I/O addresses to be probed. */
 static unsigned int netcard_portlist[] __initdata =
-   { 0x300, 0x320, 0x340, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
+   { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
 
-static unsigned int net_debug = NET_DEBUG;
+#if DEBUGGING
+static unsigned int net_debug = 5;
+#else
+#define net_debug 0	/* gcc will remove all the debug code for us */
+#endif
 
 /* The number of low I/O ports used by the ethercard. */
 #define NETCARD_IO_EXTENT	16
 
+/* we allow the user to override various values normally set in the EEPROM */
+#define FORCE_RJ45	0x0001    /* pick one of these three */
+#define FORCE_AUI	0x0002
+#define FORCE_BNC	0x0004
+
+#define FORCE_AUTO	0x0010    /* pick one of these three */
+#define FORCE_HALF	0x0020
+#define FORCE_FULL	0x0030
+
 /* Information that need to be kept for each board. */
 struct net_local {
 	struct net_device_stats stats;
 	int chip_type;		/* one of: CS8900, CS8920, CS8920M */
 	char chip_revision;	/* revision letter of the chip ('A'...) */
-	int send_cmd;		/* the propercommand used to send a packet. */
-	int auto_neg_cnf;
-	int adapter_cnf;
-	int isa_config;
-	int irq_map;
-	int rx_mode;
-	int curr_rx_cfg;
-        int linectl;
-        int send_underrun;      /* keep track of how many underruns in a row we get */
-	struct sk_buff *skb;
+	int send_cmd;		/* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
+	int auto_neg_cnf;	/* auto-negotiation word from EEPROM */
+	int adapter_cnf;	/* adapter configuration from EEPROM */
+	int isa_config;		/* ISA configuration from EEPROM */
+	int irq_map;		/* IRQ map from EEPROM */
+	int rx_mode;		/* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
+	int curr_rx_cfg;	/* a copy of PP_RxCFG */
+	int linectl;		/* either 0 or LOW_RX_SQUELCH, depending on configuration. */
+	int send_underrun;	/* keep track of how many underruns in a row we get */
+	int force;		/* force various values; see FORCE* above. */
+	spinlock_t lock;
+#if ALLOW_DMA
+	int use_dma;		/* Flag: we're using dma */
+	int dma;		/* DMA channel */
+	int dmasize;		/* 16 or 64 */
+	unsigned char *dma_buff;	/* points to the beginning of the buffer */
+	unsigned char *end_dma_buff;	/* points to the end of the buffer */
+	unsigned char *rx_dma_ptr;	/* points to the next packet  */
+#endif
 };
 
 /* Index to functions, as function prototypes. */
@@ -117,7 +170,7 @@
 
 static int cs89x0_probe1(struct net_device *dev, int ioaddr);
 static int net_open(struct net_device *dev);
-static int	net_send_packet(struct sk_buff *skb, struct net_device *dev);
+static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
 static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void set_multicast_list(struct net_device *dev);
 static void net_timeout(struct net_device *dev);
@@ -128,7 +181,11 @@
 static int get_eeprom_data(struct net_device *dev, int off, int len, int *buffer);
 static int get_eeprom_cksum(int off, int len, int *buffer);
 static int set_mac_address(struct net_device *dev, void *addr);
-
+static void count_rx_errors(int status, struct net_local *lp);
+#if ALLOW_DMA
+static void get_dma_channel(struct net_device *dev);
+static void release_dma_buff(struct net_local *lp);
+#endif
 
 /* Example routines you must write ;->. */
 #define tx_done(dev) 1
@@ -139,6 +196,7 @@
    If dev->base_addr == 1, always return failure.
    If dev->base_addr == 2, allocate space for the device and return success
    (detachable devices only).
+   Return 0 on success.
    */
 
 int __init cs89x0_probe(struct net_device *dev)
@@ -146,6 +204,9 @@
 	int i;
 	int base_addr = dev ? dev->base_addr : 0;
 
+	if (net_debug)
+		printk("cs89x0:cs89x0_probe()\n");
+
 	if (base_addr > 0x1ff)		/* Check a single specified location. */
 		return cs89x0_probe1(dev, base_addr);
 	else if (base_addr != 0)	/* Don't probe at all. */
@@ -158,34 +219,38 @@
 		if (cs89x0_probe1(dev, ioaddr) == 0)
 			return 0;
 	}
-	printk("cs89x0: no cs8900 or cs8920 detected.  Be sure to disable PnP with SETUP\n");
+	printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected.  Be sure to disable PnP with SETUP\n");
 	return ENODEV;
 }
 
-extern int inline readreg(struct net_device *dev, int portno)
+extern int inline
+readreg(struct net_device *dev, int portno)
 {
 	outw(portno, dev->base_addr + ADD_PORT);
 	return inw(dev->base_addr + DATA_PORT);
 }
 
-extern  void inline writereg(struct net_device *dev, int portno, int value)
+extern void inline
+writereg(struct net_device *dev, int portno, int value)
 {
 	outw(portno, dev->base_addr + ADD_PORT);
 	outw(value, dev->base_addr + DATA_PORT);
 }
 
-
-extern int inline readword(struct net_device *dev, int portno)
+extern int inline
+readword(struct net_device *dev, int portno)
 {
 	return inw(dev->base_addr + portno);
 }
 
-extern void inline writeword(struct net_device *dev, int portno, int value)
+extern void inline
+writeword(struct net_device *dev, int portno, int value)
 {
 	outw(value, dev->base_addr + portno);
 }
 
-static int __init wait_eeprom_ready(struct net_device *dev)
+static int __init
+wait_eeprom_ready(struct net_device *dev)
 {
 	int timeout = jiffies;
 	/* check to see if the EEPROM is ready, a timeout is used -
@@ -197,7 +262,8 @@
 	return 0;
 }
 
-static int __init get_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
+static int __init
+get_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
 {
 	int i;
 
@@ -214,7 +280,8 @@
         return 0;
 }
 
-static int  __init  get_eeprom_cksum(int off, int len, int *buffer)
+static int  __init
+get_eeprom_cksum(int off, int len, int *buffer)
 {
 	int i, cksum;
 
@@ -229,19 +296,28 @@
 
 /* This is the real probe routine.  Linux has a history of friendly device
    probes on the ISA bus.  A good device probes avoids doing writes, and
-   verifies that the correct device exists and functions.  */
+   verifies that the correct device exists and functions.
+   Return 0 on success.
+ */
 
-static int __init cs89x0_probe1(struct net_device *dev, int ioaddr)
+static int __init
+cs89x0_probe1(struct net_device *dev, int ioaddr)
 {
 	struct net_local *lp;
 	static unsigned version_printed = 0;
 	int i;
 	unsigned rev_type = 0;
 	int eeprom_buff[CHKSUM_LEN];
+	int retval;
 
 	/* Initialize the device structure. */
 	if (dev->priv == NULL) {
 		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+		if (dev->priv == 0)
+		{
+			retval = ENOMEM;
+			goto out;
+		}
                 memset(dev->priv, 0, sizeof(struct net_local));
         }
 	lp = (struct net_local *)dev->priv;
@@ -257,7 +333,10 @@
 	}
 
 	if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
-		return ENODEV;
+	{
+		retval = ENODEV;
+		goto out1;
+	}
 
 	/* Fill in the 'dev' fields. */
 	dev->base_addr = ioaddr;
@@ -278,7 +357,7 @@
 	if (net_debug  &&  version_printed++ == 0)
 		printk(version);
 
-	printk("%s: cs89%c0%s rev %c found at %#3lx",
+	printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#3lx ",
 	       dev->name,
 	       lp->chip_type==CS8900?'0':'2',
 	       lp->chip_type==CS8920M?"M":"",
@@ -289,11 +368,11 @@
 
 	/* First check to see if an EEPROM is attached*/
 	if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
-		printk("\ncs89x0: No EEPROM, relying on command line....\n");
+		printk(KERN_WARNING "\ncs89x0: No EEPROM, relying on command line....\n");
 	else if (get_eeprom_data(dev, START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
-		printk("\ncs89x0: EEPROM read failed, relying on command line.\n");
+		printk(KERN_WARNING "\ncs89x0: EEPROM read failed, relying on command line.\n");
         } else if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
-                printk("\ncs89x0: EEPROM checksum bad, relying on command line\n");
+                printk(KERN_WARNING "\ncs89x0: EEPROM checksum bad, relying on command line\n");
         } else {
                 /* get transmission control word  but keep the autonegotiation bits */
                 if (!lp->auto_neg_cnf) lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
@@ -301,16 +380,36 @@
                 if (!lp->adapter_cnf) lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
                 /* Store ISA configuration */
                 lp->isa_config = eeprom_buff[ISA_CNF_OFFSET/2];
-                /* store the initial memory base address */
                 dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
+
+                /* eeprom_buff has 32-bit ints, so we can't just memcpy it */
+                /* store the initial memory base address */
                 for (i = 0; i < ETH_ALEN/2; i++) {
                         dev->dev_addr[i*2] = eeprom_buff[i];
                         dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8;
                 }
         }
 
+        /* allow them to force multiple transceivers.  If they force multiple, autosense */
+        {
+		int count = 0;
+		if (lp->force & FORCE_RJ45)	{lp->adapter_cnf |= A_CNF_10B_T; count++; }
+		if (lp->force & FORCE_AUI) 	{lp->adapter_cnf |= A_CNF_AUI; count++; }
+		if (lp->force & FORCE_BNC)	{lp->adapter_cnf |= A_CNF_10B_2; count++; }
+		if (count > 1)			{lp->adapter_cnf |= A_CNF_MEDIA_AUTO; }
+		else if (lp->force & FORCE_RJ45){lp->adapter_cnf |= A_CNF_MEDIA_10B_T; }
+		else if (lp->force & FORCE_AUI)	{lp->adapter_cnf |= A_CNF_MEDIA_AUI; }
+		else if (lp->force & FORCE_BNC)	{lp->adapter_cnf |= A_CNF_MEDIA_10B_2; }
+        }
+
+        /* FIXME: We don't let you set dc-dc polarity or low RX squelch from the command line: add it here */
+
+        /* FIXME: We don't let you set the IMM bit from the command line: add it to lp->auto_neg_cnf here */
 
-	printk(" media %s%s%s",
+        /* FIXME: we don't set the Ethernet address on the command line.  Use
+           ifconfig IFACE hw ether AABBCCDDEEFF */
+
+	printk(KERN_INFO "cs89x0 media %s%s%s",
 	       (lp->adapter_cnf & A_CNF_10B_T)?"RJ-45,":"",
 	       (lp->adapter_cnf & A_CNF_AUI)?"AUI,":"",
 	       (lp->adapter_cnf & A_CNF_10B_2)?"BNC,":"");
@@ -353,14 +452,33 @@
 			dev->irq = i;
 	}
 
-	printk(" IRQ %d", dev->irq);
+	printk(", IRQ %d", dev->irq);
 
+#if ALLOW_DMA
+	if (lp->use_dma)
+	{
+		get_dma_channel(dev);
+		printk(", DMA %d", dev->dma);
+	}
+	else
+#endif
+	{
+		printk(", programmed I/O");
+	}
 
 	/* print the ethernet address. */
+	printk(", MAC ");
 	for (i = 0; i < ETH_ALEN; i++)
-		printk(" %2.2x", dev->dev_addr[i]);
+	{
+		printk("%s%02x", i ? ":" : "", dev->dev_addr[i]);
+	}
 
 	/* Grab the region so we can find another board if autoIRQ fails. */
+
+	/*
+	 * FIXME: we should check this, but really the isapnp stuff should have given
+	 * us a free region.  Sort this out when the isapnp is sorted out
+	 */
 	request_region(ioaddr, NETCARD_IO_EXTENT,"cs89x0");
 
 	dev->open		= net_open;
@@ -376,11 +494,188 @@
 	ether_setup(dev);
 
 	printk("\n");
+	if (net_debug)
+		printk("cs89x0_probe1() successful\n");
 	return 0;
+out1:
+	kfree(dev->priv);
+	dev->priv = 0;
+out:
+	return retval;
+}
+
+
+/*********************************
+ * This page contains DMA routines
+**********************************/
+
+#if ALLOW_DMA
+
+#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+
+static void
+get_dma_channel(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if (lp->dma) {
+		dev->dma = lp->dma;
+		lp->isa_config |= ISA_RxDMA;
+	} else {
+		if ((lp->isa_config & ANY_ISA_DMA) == 0)
+			return;
+		dev->dma = lp->isa_config & DMA_NO_MASK;
+		if (lp->chip_type == CS8900)
+			dev->dma += 5;
+		if (dev->dma < 5 || dev->dma > 7) {
+			lp->isa_config &= ~ANY_ISA_DMA;
+			return;
+		}
+	}
+	return;
+}
+
+static void
+write_dma(struct net_device *dev, int chip_type, int dma)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	if ((lp->isa_config & ANY_ISA_DMA) == 0)
+		return;
+	if (chip_type == CS8900) {
+		writereg(dev, PP_CS8900_ISADMA, dma-5);
+	} else {
+		writereg(dev, PP_CS8920_ISADMA, dma);
+	}
+}
+
+static void
+set_dma_cfg(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if (lp->use_dma)
+	{
+		if ((lp->isa_config & ANY_ISA_DMA) == 0)
+		{
+			if (net_debug > 3)
+				printk("set_dma_cfg(): no DMA\n");
+			return;
+		}
+		if (lp->isa_config & ISA_RxDMA)
+		{
+			lp->curr_rx_cfg |= RX_DMA_ONLY;
+			if (net_debug > 3)
+				printk("set_dma_cfg(): RX_DMA_ONLY\n");
+		}
+		else
+		{
+			lp->curr_rx_cfg |= AUTO_RX_DMA;	/* not that we support it... */
+			if (net_debug > 3)
+				printk("set_dma_cfg(): AUTO_RX_DMA\n");
+		}
+	}
+}
+
+static int
+dma_bufcfg(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	if (lp->use_dma)
+		return (lp->isa_config & ANY_ISA_DMA)? RX_DMA_ENBL : 0;
+	else
+		return 0;
+}
+
+static int
+dma_busctl(struct net_device *dev)
+{
+	int retval = 0;
+	struct net_local *lp = (struct net_local *)dev->priv;
+	if (lp->use_dma)
+	{
+		if (lp->isa_config & ANY_ISA_DMA)
+			retval |= RESET_RX_DMA; /* Reset the DMA pointer */
+		if (lp->isa_config & DMA_BURST)
+			retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */
+		if (lp->dmasize == 64)
+			retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */
+		retval |= MEMORY_ON;	/* we need memory enabled to use DMA. */
+	}
+	return retval;
+}
+
+static void
+dma_rx(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	struct sk_buff *skb;
+	int status, length;
+	unsigned char *bp = lp->rx_dma_ptr;
+
+	{
+		int i;
+		for (i = 0; i < 1000; i++)
+			;
+	}
+
+	status = bp[0] + (bp[1]<<8);
+	length = bp[2] + (bp[3]<<8);
+	bp += 4;
+	if (net_debug > 5)
+	{
+		printk(	"%s: receiving DMA packet at %lx, status %x, length %x\n",
+			dev->name, (unsigned long)bp, status, length);
+	}
+	if ((status & RX_OK) == 0) {
+		count_rx_errors(status, lp);
+		goto skip_this_frame;
+	}
+
+	/* Malloc up new buffer. */
+	skb = alloc_skb(length, GFP_ATOMIC);
+	if (skb == NULL) {
+		if (net_debug)	/* I don't think we want to do this to a stressed system */
+			printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+		lp->stats.rx_dropped++;
+
+		/* AKPM: advance bp to the next frame */
+skip_this_frame:
+		bp += (length + 3) & ~3;
+		if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024;
+		lp->rx_dma_ptr = bp;
+		return;
+	}
+
+	skb->len = length;
+	skb->dev = dev;
+
+	if (bp + length > lp->end_dma_buff) {
+		int semi_cnt = lp->end_dma_buff - bp;
+		memcpy(skb_put(skb,semi_cnt), bp, semi_cnt);
+		memcpy(skb_put(skb,length - semi_cnt), lp->dma_buff,
+		       length - semi_cnt);
+	} else {
+		memcpy(skb_put(skb,length), bp, length);
+	}
+	bp += (length + 3) & ~3;
+	if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024;
+	lp->rx_dma_ptr = bp;
+
+	if (net_debug > 3)
+	{
+		printk(	"%s: received %d byte DMA packet of type %x\n",
+			dev->name, length,
+			(skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+	}
+        skb->protocol=eth_type_trans(skb,dev);
+	netif_rx(skb);
+	lp->stats.rx_packets++;
+	return;
 }
 
-void  __init 
-reset_chip(struct net_device *dev)
+#endif	/* ALLOW_DMA */
+
+void  __init reset_chip(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
@@ -399,8 +694,8 @@
 		outb(0,      ioaddr + DATA_PORT + 1);
 
 		outw(PP_CS8920_ISAMemB, ioaddr + ADD_PORT);
-		outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT);
-		outb((dev->mem_start >> 24) & 0xff,   ioaddr + DATA_PORT + 1);
+		outb((dev->mem_start >> 16) & 0xff, ioaddr + DATA_PORT);
+		outb((dev->mem_start >> 8) & 0xff,   ioaddr + DATA_PORT + 1);
 	}
 	/* Wait until the chip is reset */
 	reset_start_time = jiffies;
@@ -430,11 +725,18 @@
 
 }
 
+#define DETECTED_NONE  0
+#define DETECTED_RJ45H 1
+#define DETECTED_RJ45F 2
+#define DETECTED_AUI   3
+#define DETECTED_BNC   4
+
 static int
 detect_tp(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int timenow = jiffies;
+	int fdx;
 
 	if (net_debug > 1) printk("%s: Attempting TP\n", dev->name);
 
@@ -450,35 +752,63 @@
 	for (timenow = jiffies; jiffies - timenow < 15; )
                 ;
 	if ((readreg(dev, PP_LineST) & LINK_OK) == 0)
-		return 0;
+		return DETECTED_NONE;
 
-	if (lp->chip_type != CS8900) {
+	if (lp->chip_type == CS8900) {
+                switch (lp->force & 0xf0) {
+#if 0
+                case FORCE_AUTO:
+			printk("%s: cs8900 doesn't autonegotiate\n",dev->name);
+                        return DETECTED_NONE;
+#endif
+		/* CS8900 doesn't support AUTO, change to HALF*/
+                case FORCE_AUTO:
+			lp->force &= ~FORCE_AUTO;
+                        lp->force |= FORCE_HALF;
+			break;
+		case FORCE_HALF:
+			break;
+                case FORCE_FULL:
+			writereg(dev, PP_TestCTL, readreg(dev, PP_TestCTL) | FDX_8900);
+			break;
+                }
+		fdx = readreg(dev, PP_TestCTL) & FDX_8900;
+	} else {
+		switch (lp->force & 0xf0) {
+		case FORCE_AUTO:
+			lp->auto_neg_cnf = AUTO_NEG_ENABLE;
+			break;
+		case FORCE_HALF:
+			lp->auto_neg_cnf = 0;
+			break;
+		case FORCE_FULL:
+			lp->auto_neg_cnf = RE_NEG_NOW | ALLOW_FDX;
+			break;
+                }
 
 		writereg(dev, PP_AutoNegCTL, lp->auto_neg_cnf & AUTO_NEG_MASK);
 
 		if ((lp->auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
-			printk("%s: negotiating duplex...\n",dev->name);
+			printk(KERN_INFO "%s: negotiating duplex...\n",dev->name);
 			while (readreg(dev, PP_AutoNegST) & AUTO_NEG_BUSY) {
 				if (jiffies - timenow > 4000) {
-					printk("**** Full / half duplex auto-negotiation timed out ****\n");
+					printk(KERN_ERR "**** Full / half duplex auto-negotiation timed out ****\n");
 					break;
 				}
 			}
 		}
-		if (readreg(dev, PP_AutoNegST) & FDX_ACTIVE)
-			printk("%s: using full duplex\n", dev->name);
-		else
-			printk("%s: using half duplex\n", dev->name);
+		fdx = readreg(dev, PP_AutoNegST) & FDX_ACTIVE;
 	}
-
-	return A_CNF_MEDIA_10B_T;
+	if (fdx)
+		return DETECTED_RJ45F;
+	else
+		return DETECTED_RJ45H;
 }
 
 /* send a test packet - return true if carrier bits are ok */
 static int
 send_test_pkt(struct net_device *dev)
 {
-	int ioaddr = dev->base_addr;
 	char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
 				 0, 46, /* A 46 in network order */
 				 0, 0, /* DSAP=0 & SSAP=0 fields */
@@ -490,8 +820,8 @@
 	memcpy(test_packet,          dev->dev_addr, ETH_ALEN);
 	memcpy(test_packet+ETH_ALEN, dev->dev_addr, ETH_ALEN);
 
-        outw(TX_AFTER_ALL, ioaddr + TX_CMD_PORT);
-        outw(ETH_ZLEN, ioaddr + TX_LEN_PORT);
+        writeword(dev, TX_CMD_PORT, TX_AFTER_ALL);
+        writeword(dev, TX_LEN_PORT, ETH_ZLEN);
 
 	/* Test to see if the chip has allocated memory for the packet */
 	while (jiffies - timenow < 5)
@@ -501,11 +831,7 @@
 		return 0;	/* this shouldn't happen */
 
 	/* Write the contents of the packet */
-	if (dev->mem_start) {
-		memcpy((void *)dev->mem_start + PP_TxFrame, test_packet, ETH_ZLEN);
-	} else {
-		outsw(ioaddr + TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1);
-	}
+	outsw(dev->base_addr + TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1);
 
 	if (net_debug > 1) printk("Sending test packet ");
 	/* wait a couple of jiffies for packet to be received */
@@ -531,9 +857,9 @@
 	writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY);
 
 	if (send_test_pkt(dev))
-		return A_CNF_MEDIA_AUI;
+		return DETECTED_AUI;
 	else
-		return 0;
+		return DETECTED_NONE;
 }
 
 static int
@@ -547,9 +873,9 @@
 	writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY);
 
 	if (send_test_pkt(dev))
-		return A_CNF_MEDIA_10B_2;
+		return DETECTED_BNC;
 	else
-		return 0;
+		return DETECTED_NONE;
 }
 
 
@@ -579,6 +905,9 @@
    registers that "should" only need to be set once at boot, so that
    there is non-reboot way to recover if something goes wrong.
    */
+
+/* AKPM: do we need to do any locking here? */
+
 static int
 net_open(struct net_device *dev)
 {
@@ -588,9 +917,15 @@
 
 	if (dev->irq < 2) {
 		/* Allow interrupts to be generated by the chip */
+/* Cirrus' release had this: */
+#if 0
+		writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );
+#endif
+/* And 2.3.47 had this: */
 		writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
+
 		for (i = 2; i < CS8920_NO_INTS; i++) if ((1 << dev->irq) & lp->irq_map) {
-			if (request_irq (i, NULL, 0, "cs8920", dev) != -EBUSY) {
+			if (request_irq (i, NULL, 0, "cs89x0", dev) != -EBUSY) {
 				write_irq(dev, lp->chip_type, i);
 				writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT);
 				if (request_irq (dev->irq = i, &net_interrupt, 0, "cs89x0", dev) == 0)
@@ -601,21 +936,74 @@
 
 		if (i >= CS8920_NO_INTS) {
 			writereg(dev, PP_BusCTL, 0);	/* disable interrupts. */
+			if (net_debug)
+				printk("cs89x0: can't get an interrupt\n");
 			return -EAGAIN;
 		}
 	} else {
 		if (((1 << dev->irq) & lp->irq_map) == 0) {
-			printk("%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
+			printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
                                dev->name, dev->irq, lp->irq_map);
 			return -EAGAIN;
 		}
+/* FIXME: Cirrus' release had this: */
+		writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );
+/* And 2.3.47 had this: */
+#if 0
 		writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
+#endif
 		write_irq(dev, lp->chip_type, dev->irq);
 		if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) {
+			if (net_debug)
+				printk("cs89x0: request_irq(%d) failed\n", dev->irq);
 			return -EAGAIN;
 		}
 	}
 
+#if ALLOW_DMA
+	if (lp->use_dma)
+	{
+		if (lp->isa_config & ANY_ISA_DMA) {
+			unsigned long flags;
+			lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL,
+							(lp->dmasize * 1024) / PAGE_SIZE);
+
+			if (!lp->dma_buff) {
+				printk(KERN_ERR "%s: cannot get %dK memory for DMA\n", dev->name, lp->dmasize);
+				goto release_irq;
+			}
+			if (net_debug > 1)
+			{
+				printk(	"%s: dma %lx %lx\n",
+					dev->name,
+					(unsigned long)lp->dma_buff,
+					(unsigned long)virt_to_bus(lp->dma_buff));
+			}
+			if ((unsigned long)virt_to_bus(lp->dma_buff) >= MAX_DMA_ADDRESS ||
+			    !dma_page_eq(lp->dma_buff, lp->dma_buff+lp->dmasize*1024-1)) {
+				printk(KERN_ERR "%s: not usable as DMA buffer\n", dev->name);
+				goto release_irq;
+			}
+			memset(lp->dma_buff, 0, lp->dmasize * 1024);	/* Why? */
+			if (request_dma(dev->dma, "cs89x0")) {
+				printk(KERN_ERR "%s: cannot get dma channel %d\n", dev->name, dev->dma);
+				goto release_irq;
+			}
+			write_dma(dev, lp->chip_type, dev->dma);
+			lp->rx_dma_ptr = lp->dma_buff;
+			lp->end_dma_buff = lp->dma_buff + lp->dmasize*1024;
+			spin_lock_irqsave(&lp->lock, flags);
+			disable_dma(dev->dma);
+			clear_dma_ff(dev->dma);
+			set_dma_mode(dev->dma, 0x14); /* auto_init as well */
+			set_dma_addr(dev->dma, virt_to_bus(lp->dma_buff));
+			set_dma_count(dev->dma, lp->dmasize*1024);
+			enable_dma(dev->dma);
+			spin_unlock_irqrestore(&lp->lock, flags);
+		}
+	}
+#endif	/* ALLOW_DMA */
+
 	/* set the Ethernet address */
 	for (i=0; i < ETH_ALEN/2; i++)
 		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
@@ -637,8 +1025,11 @@
         default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2);
         }
         if (!result) {
-                printk("%s: EEPROM is configured for unavailable media\n", dev->name);
+                printk(KERN_ERR "%s: EEPROM is configured for unavailable media\n", dev->name);
         release_irq:
+#if ALLOW_DMA
+		release_dma_buff(lp);
+#endif
                 writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
                 free_irq(dev->irq, dev);
 		return -EAGAIN;
@@ -648,43 +1039,58 @@
 	switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
 	case A_CNF_MEDIA_10B_T:
                 result = detect_tp(dev);
-                if (!result) printk("%s: 10Base-T (RJ-45) has no cable\n", dev->name);
-                if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
-                        result = A_CNF_MEDIA_10B_T; /* Yes! I don't care if I see a link pulse */
+                if (result==DETECTED_NONE) {
+                        printk(KERN_WARNING "%s: 10Base-T (RJ-45) has no cable\n", dev->name);
+                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+                                result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
+                }
 		break;
 	case A_CNF_MEDIA_AUI:
                 result = detect_aui(dev);
-                if (!result) printk("%s: 10Base-5 (AUI) has no cable\n", dev->name);
-                if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
-                        result = A_CNF_MEDIA_AUI; /* Yes! I don't care if I see a carrrier */
+                if (result==DETECTED_NONE) {
+                        printk(KERN_WARNING "%s: 10Base-5 (AUI) has no cable\n", dev->name);
+                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+                                result = DETECTED_AUI; /* Yes! I don't care if I see a carrrier */
+                }
 		break;
 	case A_CNF_MEDIA_10B_2:
                 result = detect_bnc(dev);
-                if (!result) printk("%s: 10Base-2 (BNC) has no cable\n", dev->name);
-                if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
-                        result = A_CNF_MEDIA_10B_2; /* Yes! I don't care if I can xmit a packet */
+                if (result==DETECTED_NONE) {
+                        printk(KERN_WARNING "%s: 10Base-2 (BNC) has no cable\n", dev->name);
+                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+                                result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
+                }
 		break;
 	case A_CNF_MEDIA_AUTO:
 		writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
 		if (lp->adapter_cnf & A_CNF_10B_T)
-			if ((result = detect_tp(dev)) != 0)
+			if ((result = detect_tp(dev)) != DETECTED_NONE)
 				break;
 		if (lp->adapter_cnf & A_CNF_AUI)
-			if ((result = detect_aui(dev)) != 0)
+			if ((result = detect_aui(dev)) != DETECTED_NONE)
 				break;
 		if (lp->adapter_cnf & A_CNF_10B_2)
-			if ((result = detect_bnc(dev)) != 0)
+			if ((result = detect_bnc(dev)) != DETECTED_NONE)
 				break;
-		printk("%s: no media detected\n", dev->name);
+		printk(KERN_ERR "%s: no media detected\n", dev->name);
                 goto release_irq;
 	}
 	switch(result) {
-	case 0: printk("%s: no network cable attached to configured media\n", dev->name);
+	case DETECTED_NONE:
+		printk(KERN_ERR "%s: no network cable attached to configured media\n", dev->name);
                 goto release_irq;
-	case A_CNF_MEDIA_10B_T: printk("%s: using 10Base-T (RJ-45)\n", dev->name);break;
-	case A_CNF_MEDIA_AUI:   printk("%s: using 10Base-5 (AUI)\n", dev->name);break;
-	case A_CNF_MEDIA_10B_2: printk("%s: using 10Base-2 (BNC)\n", dev->name);break;
-	default: printk("%s: unexpected result was %x\n", dev->name, result); goto release_irq;
+	case DETECTED_RJ45H:
+		printk(KERN_INFO "%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);
+		break;
+	case DETECTED_RJ45F:
+		printk(KERN_INFO "%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);
+		break;
+	case DETECTED_AUI:
+		printk(KERN_INFO "%s: using 10Base-5 (AUI)\n", dev->name);
+		break;
+	case DETECTED_BNC:
+		printk(KERN_INFO "%s: using 10Base-2 (BNC)\n", dev->name);
+		break;
 	}
 
 	/* Turn on both receive and transmit operations */
@@ -695,22 +1101,34 @@
 	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
 
 	lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
+
 	if (lp->isa_config & STREAM_TRANSFER)
 		lp->curr_rx_cfg |= RX_STREAM_ENBL;
-
+#if ALLOW_DMA
+	set_dma_cfg(dev);
+#endif
 	writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
 
 	writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
-	       TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
+		TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
 
 	writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
-		 TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
+#if ALLOW_DMA
+		dma_bufcfg(dev) |
+#endif
+		TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
 
 	/* now that we've got our act together, enable everything */
 	writereg(dev, PP_BusCTL, ENABLE_IRQ
+		 | (dev->mem_start?MEMORY_ON : 0) /* turn memory on */
+#if ALLOW_DMA
+		 | dma_busctl(dev)
+#endif
                  );
         MOD_INC_USE_COUNT;
         netif_start_queue(dev);
+	if (net_debug)
+		printk("cs89x0: net_open() succeeded\n");
 	return 0;
 }
 
@@ -727,39 +1145,54 @@
 static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
-	short ioaddr = dev->base_addr;
-	unsigned long flags;
 
 	if (net_debug > 3)
-		printk("%s: sent %d byte packet of type %x\n", dev->name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+	{
+		printk("%s: sent %d byte packet of type %x\n",
+			dev->name, skb->len,
+			(skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+	}
 
 	/* keep the upload from being interrupted, since we
                   ask the chip to start transmitting before the
                   whole packet has been completely uploaded. */
 
-	save_flags(flags);
-	cli();
+	spin_lock_irq(&lp->lock);
 	netif_stop_queue(dev);
-	
+
 	/* initiate a transmit sequence */
-	outw(lp->send_cmd, ioaddr + TX_CMD_PORT);
-	outw(skb->len, ioaddr + TX_LEN_PORT);
+	writeword(dev, TX_CMD_PORT, lp->send_cmd);
+	writeword(dev, TX_LEN_PORT, skb->len);
 
 	/* Test to see if the chip has allocated memory for the packet */
-	if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
-		/* Gasp!  It hasn't.  But that shouldn't happen since
-		   we're waiting for TxOk, so return 1 and requeue this packet. */
-		restore_flags(flags);
+	if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0)
+	{
+		/*
+		 * Gasp!  It hasn't.  But that shouldn't happen since
+		 * we're waiting for TxOk, so return 1 and requeue this packet.
+		 */
+		
+		spin_unlock_irq(&lp->lock);
+		if (net_debug) printk("cs89x0: Tx buffer not free!\n");
 		return 1;
 	}
 	/* Write the contents of the packet */
-        outsw(ioaddr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
-
-	restore_flags(flags);
+	outsw(dev->base_addr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
+	spin_unlock_irq(&lp->lock);
 	dev->trans_start = jiffies;
 	dev_kfree_skb (skb);
-	netif_wake_queue(dev);
-	
+
+	/*
+	 * We DO NOT call netif_wake_queue() here.
+	 * We also DO NOT call netif_start_queue().
+	 *
+	 * Either of these would cause another bottom half run through
+	 * net_send_packet() before this packet has fully gone out.  That causes
+	 * us to hit the "Gasp!" above and the send is rescheduled.  it runs like
+	 * a dog.  We just return and wait for the Tx completion interrupt handler
+	 * to restart the netdevice layer
+	 */
+
 	return 0;
 }
 
@@ -771,7 +1204,7 @@
 	struct net_device *dev = dev_id;
 	struct net_local *lp;
 	int ioaddr, status;
-
+ 
 	ioaddr = dev->base_addr;
 	lp = (struct net_local *)dev->priv;
 
@@ -792,11 +1225,18 @@
 		case ISQ_TRANSMITTER_EVENT:
 			lp->stats.tx_packets++;
 			netif_wake_queue(dev);	/* Inform upper layers. */
-			if ((status & TX_OK) == 0) lp->stats.tx_errors++;
-			if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
-			if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
-			if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
-			if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
+			if ((status & (	TX_OK |
+					TX_LOST_CRS |
+					TX_SQE_ERROR |
+					TX_LATE_COL |
+					TX_16_COL)) != TX_OK)
+			{
+				if ((status & TX_OK) == 0) lp->stats.tx_errors++;
+				if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
+				if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
+				if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
+				if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
+			}
 			break;
 		case ISQ_BUFFER_EVENT:
 			if (status & READY_FOR_TX) {
@@ -819,6 +1259,22 @@
 				   event of a tx underrun */
 				netif_wake_queue(dev);	/* Inform upper layers. */
                         }
+#if ALLOW_DMA
+			if (lp->use_dma && (status & RX_DMA)) {
+				int count = readreg(dev, PP_DmaFrameCnt);
+				while(count) {
+					if (net_debug > 5)
+						printk("%s: receiving %d DMA frames\n", dev->name, count);
+					if (net_debug > 2 && count >1)
+						printk("%s: receiving %d DMA frames\n", dev->name, count);
+					dma_rx(dev);
+					if (--count == 0)
+						count = readreg(dev, PP_DmaFrameCnt);
+					if (net_debug > 2 && count > 0)
+						printk("%s: continuing with %d DMA frames\n", dev->name, count);
+				}
+			}
+#endif
 			break;
 		case ISQ_RX_MISS_EVENT:
 			lp->stats.rx_missed_errors += (status >>6);
@@ -830,45 +1286,58 @@
 	}
 }
 
+static void
+count_rx_errors(int status, struct net_local *lp)
+{
+	lp->stats.rx_errors++;
+	if (status & RX_RUNT) lp->stats.rx_length_errors++;
+	if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
+	if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
+		/* per str 172 */
+		lp->stats.rx_crc_errors++;
+	if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
+	return;
+}
+
 /* We have a good packet(s), get it/them out of the buffers. */
 static void
 net_rx(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
-	int ioaddr = dev->base_addr;
 	struct sk_buff *skb;
 	int status, length;
 
+	int ioaddr = dev->base_addr;
 	status = inw(ioaddr + RX_FRAME_PORT);
 	length = inw(ioaddr + RX_FRAME_PORT);
+
 	if ((status & RX_OK) == 0) {
-		lp->stats.rx_errors++;
-		if (status & RX_RUNT) lp->stats.rx_length_errors++;
-		if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
-		if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
-			/* per str 172 */
-			lp->stats.rx_crc_errors++;
-		if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
+		count_rx_errors(status, lp);
 		return;
 	}
 
 	/* Malloc up new buffer. */
 	skb = alloc_skb(length, GFP_ATOMIC);
 	if (skb == NULL) {
-		printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+#if 0		/* Again, this seems a cruel thing to do */
+		printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+#endif
 		lp->stats.rx_dropped++;
 		return;
 	}
 	skb->len = length;
 	skb->dev = dev;
 
-        insw(ioaddr + RX_FRAME_PORT, skb->data, length >> 1);
+	insw(ioaddr + RX_FRAME_PORT, skb->data, length >> 1);
 	if (length & 1)
 		skb->data[length-1] = inw(ioaddr + RX_FRAME_PORT);
 
-	if (net_debug > 3)printk("%s: received %d byte packet of type %x\n",
-                                 dev->name, length,
-                                 (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+	if (net_debug > 3)
+	{
+		printk(	"%s: received %d byte packet of type %x\n",
+			dev->name, length,
+			(skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+	}
 
         skb->protocol=eth_type_trans(skb,dev);
 	netif_rx(skb);
@@ -877,10 +1346,23 @@
 	return;
 }
 
+#if ALLOW_DMA
+static void release_dma_buff(struct net_local *lp)
+{
+	if (lp->dma_buff)
+	{
+		free_pages((unsigned long)(lp->dma_buff), (lp->dmasize * 1024) / PAGE_SIZE);
+		lp->dma_buff = 0;
+	}
+}
+#endif
+
 /* The inverse routine to net_open(). */
 static int
 net_close(struct net_device *dev)
 {
+	struct net_local *lp = (struct net_local *)dev->priv;
+
 	netif_stop_queue(dev);
 	
 	writereg(dev, PP_RxCFG, 0);
@@ -890,11 +1372,16 @@
 
 	free_irq(dev->irq, dev);
 
-	/* Update the statistics here. */
+#if ALLOW_DMA
+	if (lp->use_dma && lp->dma) {
+		free_dma(dev->dma);
+		release_dma_buff(lp);
+	}
+#endif
 
+	/* Update the statistics here. */
         MOD_DEC_USE_COUNT;
 	return 0;
-
 }
 
 /* Get the current statistics.	This may be called with the card open or
@@ -903,12 +1390,13 @@
 net_get_stats(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
+	unsigned long flags;
 
-	cli();
+	spin_lock_irqsave(&lp->lock, flags);
 	/* Update the statistics from the device registers. */
 	lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
 	lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
-	sti();
+	spin_unlock_irqrestore(&lp->lock, flags);
 
 	return &lp->stats;
 }
@@ -916,7 +1404,9 @@
 static void set_multicast_list(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
+	unsigned long flags;
 
+	spin_lock_irqsave(&lp->lock, flags);
 	if(dev->flags&IFF_PROMISC)
 	{
 		lp->rx_mode = RX_ALL_ACCEPT;
@@ -935,18 +1425,23 @@
 	/* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
 	writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
 	     (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
+	spin_unlock_irqrestore(&lp->lock, flags);
 }
 
 
 static int set_mac_address(struct net_device *dev, void *addr)
 {
 	int i;
+
 	if (netif_running(dev))
 		return -EBUSY;
-	printk("%s: Setting MAC address to ", dev->name);
-	for (i = 0; i < 6; i++)
-		printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
-	printk(".\n");
+	if (net_debug)
+	{
+		printk("%s: Setting MAC address to ", dev->name);
+		for (i = 0; i < 6; i++)
+			printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
+		printk(".\n");
+	}
 	/* set the Ethernet address */
 	for (i=0; i < ETH_ALEN/2; i++)
 		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
@@ -963,17 +1458,31 @@
         0, 0,
         0, 0, 0, NULL, NULL };
 
+/*
+ * Support the 'debug' module parm even if we're compiled for non-debug to 
+ * avoid breaking someone's startup scripts 
+ */
+
 static int io=0;
 static int irq=0;
 static int debug=0;
 static char media[8];
 static int duplex=-1;
 
+static int use_dma = 0;			/* These generate unused var warnings if ALLOW_DMA = 0 */
+static int dma=0;
+static int dmasize=16;			/* or 64 */
+
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
 MODULE_PARM(debug, "i");
 MODULE_PARM(media, "s");
 MODULE_PARM(duplex, "i");
+MODULE_PARM(dma , "i");
+MODULE_PARM(dmasize , "i");
+MODULE_PARM(use_dma , "i");
+
+MODULE_AUTHOR("Mike Cruse, Russwll Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>");
 
 EXPORT_NO_SYMBOLS;
 
@@ -1008,15 +1517,34 @@
 {
 	struct net_local *lp;
 
+#if DEBUGGING
 	net_debug = debug;
+#endif
         dev_cs89x0.name = namespace;
 	dev_cs89x0.irq = irq;
 	dev_cs89x0.base_addr = io;
+
         dev_cs89x0.init = cs89x0_probe;
         dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	if (dev_cs89x0.priv == 0)
+	{
+		printk(KERN_ERR "cs89x0.c: Out of memory.\n");
+		return -ENOMEM;
+	}
 	memset(dev_cs89x0.priv, 0, sizeof(struct net_local));
 	lp = (struct net_local *)dev_cs89x0.priv;
 
+#if ALLOW_DMA
+	if (use_dma)
+	{
+		lp->use_dma = use_dma;
+		lp->dma = dma;
+		lp->dmasize = dmasize;
+	}
+#endif
+
+	spin_lock_init(&lp->lock);
+
         /* boy, they'd better get these right */
         if (!strcmp(media, "rj45"))
 		lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
@@ -1031,12 +1559,20 @@
 		lp->auto_neg_cnf = AUTO_NEG_ENABLE;
 
         if (io == 0)  {
-                printk(KERN_NOTICE "cs89x0.c: Module autoprobing not allowed.\n");
-                printk(KERN_NOTICE "cs89x0.c: Append io=0xNNN\n");
+                printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n");
+                printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n");
                 return -EPERM;
         }
+
+#if ALLOW_DMA
+	if (use_dma && dmasize != 16 && dmasize != 64) {
+		printk(KERN_ERR "cs89x0.c: dma size must be either 16K or 64K, not %dK\n", dmasize);
+		return -EPERM;
+	}
+#endif
+
         if (register_netdev(&dev_cs89x0) != 0) {
-                printk(KERN_WARNING "cs89x0.c: No card found at 0x%x\n", io);
+                printk(KERN_ERR "cs89x0.c: No card found at 0x%x\n", io);
                 return -ENXIO;
         }
 	return 0;
@@ -1045,13 +1581,7 @@
 void
 cleanup_module(void)
 {
-
-#endif
-#ifdef MODULE
-	outw(0, dev_cs89x0.base_addr + ADD_PORT);
-#endif
-#ifdef MODULE
-
+	outw(PP_ChipID, dev_cs89x0.base_addr + ADD_PORT);
         if (dev_cs89x0.priv != NULL) {
                 /* Free up the private structure, or leak memory :-)  */
                 unregister_netdev(&dev_cs89x0);

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