patch-2.4.0-test9 linux/drivers/net/8139too.c

Next file: linux/drivers/net/8390.h
Previous file: linux/drivers/net/3c59x.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test8/linux/drivers/net/8139too.c linux/drivers/net/8139too.c
@@ -2,8 +2,37 @@
 
 	8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
 
-	Copyright 2000 Jeff Garzik <jgarzik@mandrakesoft.com>
-	Originally: Written 1997-1999 by Donald Becker.
+	Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
+
+	Much code comes from Donald Becker's rtl8139.c driver,
+	versions 1.11 and older.  This driver was originally based
+	on rtl8139.c version 1.07.  Header of rtl8139.c version 1.11:
+
+	-----<snip>-----
+
+        	Written 1997-2000 by Donald Becker.
+		This software may be used and distributed according to the
+		terms of the GNU General Public License (GPL), incorporated
+		herein by reference.  Drivers based on or derived from this
+		code fall under the GPL and must retain the authorship,
+		copyright and license notice.  This file is not a complete
+		program and may only be used when the entire operating
+		system is licensed under the GPL.
+
+		This driver is for boards based on the RTL8129 and RTL8139
+		PCI ethernet chips.
+
+		The author may be reached as becker@scyld.com, or C/O Scyld
+		Computing Corporation 410 Severn Ave., Suite 210 Annapolis
+		MD 21403
+
+		Support and updates available at
+		http://www.scyld.com/network/rtl8139.html
+
+		Twister-tuning table provided by Kinston
+		<shangh@realtek.com.tw>.
+
+	-----<snip>-----
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
@@ -16,7 +45,7 @@
 		Tigran Aivazian - bug fixes, skbuff free cleanup
 
 		Martin Mares - suggestions for PCI cleanup
-		
+
 		David S. Miller - PCI DMA and softnet updates
 
 		Ernst Gill - fixes ported from BSD driver
@@ -24,7 +53,22 @@
 		Daniel Kobras - identified specific locations of
 			posted MMIO write bugginess
 
-		Gerard Sharp - bug fix
+		Gerard Sharp - bug fix, testing and feedback
+
+		David Ford - Rx ring wrap fix
+
+		Dan DeMaggio - swapped RTL8139 cards with me, and allowed me
+		to find and fix a crucial bug on older chipsets.
+
+		Donald Becker/Chris Butterworth/Marcus Westergren -
+		Noticed various Rx packet size-related buglets.
+
+		Santiago Garcia Mantinan - testing and feedback
+
+		Jens David - 2.2.x kernel backports
+
+		Martin Dennett - incredibly helpful insight on undocumented
+		features of the 8139 chips
 
 	Submitting bug reports:
 
@@ -73,7 +117,7 @@
 IVb. References
 
 http://www.realtek.com.tw/cn/cn.html
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.scyld.com/expert/NWay.html
 
 IVc. Errata
 
@@ -97,25 +141,33 @@
 #include <asm/io.h>
 
 
-#define RTL8139_VERSION "0.9.8"
+#define RTL8139_VERSION "0.9.10"
 #define RTL8139_MODULE_NAME "8139too"
 #define RTL8139_DRIVER_NAME   RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION
 #define PFX RTL8139_MODULE_NAME ": "
 
-#undef RTL8139_DEBUG /* define to 1 to enable copious debugging info */
+
+/* define to 1 to enable PIO instead of MMIO */
+#undef USE_IO_OPS
+
+/* define to 1 to enable copious debugging info */
+#undef RTL8139_DEBUG
+
+/* define to 1 to disable lightweight runtime debugging checks */
+#undef RTL8139_NDEBUG
+
 
 #ifdef RTL8139_DEBUG
 /* note: prints function name for you */
-#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
 #else
-#define DPRINTK(fmt, args...)
+#  define DPRINTK(fmt, args...)
 #endif
 
-#undef RTL8139_NDEBUG	/* define to 1 to disable lightweight runtime checks */
 #ifdef RTL8139_NDEBUG
-#define assert(expr)
+#  define assert(expr) do {} while (0)
 #else
-#define assert(expr) \
+#  define assert(expr) \
         if(!(expr)) {					\
         printk( "Assertion failed! %s,%s,%s,line=%d\n",	\
         #expr,__FILE__,__FUNCTION__,__LINE__);		\
@@ -125,12 +177,6 @@
 #define arraysize(x)            (sizeof(x)/sizeof(*(x)))
 
 
-#ifndef PCI_GET_DRIVER_DATA
-  #define PCI_GET_DRIVER_DATA(pdev)		((pdev)->driver_data)
-  #define PCI_SET_DRIVER_DATA(pdev,data)	(((pdev)->driver_data) = (data))
-#endif /* PCI_GET_DRIVER_DATA */
-
-
 /* A few user-configurable values. */
 /* media options */
 static int media[] = {-1, -1, -1, -1, -1, -1, -1, -1};
@@ -146,7 +192,8 @@
 #define RX_BUF_LEN_IDX	2	/* 0==8K, 1==16K, 2==32K, 3==64K */
 #define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
 #define RX_BUF_PAD 16
-#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD)
+#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
+#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
 
 /* Number of Tx descriptor registers. */
 #define NUM_TX_DESC	4
@@ -160,9 +207,9 @@
 #define TX_FIFO_THRESH 256	/* In bytes, rounded down to 32 byte units. */
 
 /* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024, 7==end of packet. */
-#define RX_FIFO_THRESH	4	/* Rx buffer level before first PCI xfer.  */
-#define RX_DMA_BURST	4	/* Maximum PCI burst, '7' is unlimited */
-#define TX_DMA_BURST	4	/* Maximum PCI burst, '4' is 256 */
+#define RX_FIFO_THRESH	6	/* Rx buffer level before first PCI xfer.  */
+#define RX_DMA_BURST	6	/* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST	6	/* Maximum PCI burst, '6' is 1024 */
 
 
 /* Operational parameters that usually are not changed. */
@@ -321,6 +368,7 @@
 	TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
 	TxCRC = (1 << 16),	/* DISABLE appending CRC to end of Tx packets */
 	TxClearAbt = (1 << 0),	/* Clear abort (WO) */
+	TxDMAShift = 8,		/* DMA burst value (0-7) is shift this many bits */
 
 	TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
 };
@@ -341,7 +389,7 @@
 	/* Early Rx threshold, none or X/16 */
 	RxCfgEarlyRxNone = 0,
 	RxCfgEarlyRxShift = 24,
-	
+
 	/* rx fifo threshold */
 	RxCfgFIFOShift = 13,
 	RxCfgFIFONone = (7 << RxCfgFIFOShift),
@@ -355,6 +403,9 @@
 	RxCfgRcv16K = (1 << 11),
 	RxCfgRcv32K = (1 << 12),
 	RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+	/* Disable packet wrap at end of Rx buffer */
+	RxNoWrap = (1 << 7),
 };
 
 
@@ -411,17 +462,17 @@
 	  0x40,
 	  0xf0fe0040, /* XXX copied from RTL8139A, verify */
 	},
-	
+
 	{ "RTL-8139 rev K",
 	  0x60,
-	  0xf0fe0040, /* XXX copied from RTL8139A, verify */
+	  0xf0fe0040,
 	},
-	
+
 	{ "RTL-8139A",
 	  0x70,
 	  0xf0fe0040,
 	},
-	
+
 	{ "RTL-8139B",
 	  0x78,
 	  0xf0fc0040
@@ -497,6 +548,32 @@
 static void rtl8139_hw_start (struct net_device *dev);
 
 
+#ifdef USE_IO_OPS
+
+#define RTL_R8(reg)		inb (((unsigned long)ioaddr) + (reg))
+#define RTL_R16(reg)		inw (((unsigned long)ioaddr) + (reg))
+#define RTL_R32(reg)		inl (((unsigned long)ioaddr) + (reg))
+#define RTL_W8(reg, val8)	outb ((val8), ((unsigned long)ioaddr) + (reg))
+#define RTL_W16(reg, val16)	outw ((val16), ((unsigned long)ioaddr) + (reg))
+#define RTL_W32(reg, val32)	outl ((val32), ((unsigned long)ioaddr) + (reg))
+#define RTL_W8_F		RTL_W8
+#define RTL_W16_F		RTL_W16
+#define RTL_W32_F		RTL_W32
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb(addr) inb((unsigned long)(addr))
+#define readw(addr) inw((unsigned long)(addr))
+#define readl(addr) inl((unsigned long)(addr))
+#define writeb(val,addr) outb((val),(unsigned long)(addr))
+#define writew(val,addr) outw((val),(unsigned long)(addr))
+#define writel(val,addr) outl((val),(unsigned long)(addr))
+
+#else
+
 /* write MMIO register, with flush */
 /* Flush avoids rtl8139 bug w/ posted MMIO writes */
 #define RTL_W8_F(reg, val8)	do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0)
@@ -525,13 +602,17 @@
 #define RTL_R16(reg)		readw (ioaddr + (reg))
 #define RTL_R32(reg)		readl (ioaddr + (reg))
 
+#endif /* USE_IO_OPS */
 
-static const u16 rtl8139_intr_mask = 
+
+static const u16 rtl8139_intr_mask =
 	PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
 	TxErr | TxOK | RxErr | RxOK;
 
 static const unsigned int rtl8139_rx_config =
-	  RxCfgEarlyRxNone | RxCfgFIFONone | RxCfgRcv32K | RxCfgDMAUnlimited;
+	  RxCfgEarlyRxNone | RxCfgRcv32K | RxNoWrap |
+	  (RX_FIFO_THRESH << RxCfgFIFOShift) |
+	  (RX_DMA_BURST << RxCfgDMAShift);
 
 
 static int __devinit rtl8139_init_board (struct pci_dev *pdev,
@@ -577,7 +658,7 @@
 	/* set this immediately, we need to know before
 	 * we talk to the chip directly */
 	DPRINTK("PIO region size == 0x%02X\n", pio_len);
-	DPRINTK("MMIO region size == 0x%02X\n", mmio_len);
+	DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
 	if (pio_len == RTL8139B_IO_SIZE)
 		tp->chipset = CH_8139B;
 
@@ -587,14 +668,14 @@
 		rc = -ENODEV;
 		goto err_out;
 	}
-	
+
 	/* make sure PCI base addr 1 is MMIO */
 	if (!(mmio_flags & IORESOURCE_MEM)) {
 		printk (KERN_ERR PFX "region #1 not an MMIO resource, aborting\n");
 		rc = -ENODEV;
 		goto err_out;
 	}
-	
+
 	/* check for weird/broken PCI region reporting */
 	if ((pio_len != mmio_len) ||
 	    (pio_len < RTL_MIN_IO_SIZE) ||
@@ -610,14 +691,14 @@
 		rc = -EBUSY;
 		goto err_out;
 	}
-	
+
 	/* make sure our MMIO region in PCI space is available */
 	if (!request_mem_region (mmio_start, mmio_len, dev->name)) {
 		printk (KERN_ERR PFX "no mem resource available, aborting\n");
 		rc = -EBUSY;
 		goto err_out_free_pio;
 	}
-	
+
 	/* enable device (incl. PCI PM wakeup), and bus-mastering */
 	rc = pci_enable_device (pdev);
 	if (rc)
@@ -625,6 +706,9 @@
 
 	pci_set_master (pdev);
 
+#ifdef USE_IO_OPS
+	ioaddr = (void *) pio_start;
+#else
 	/* ioremap MMIO region */
 	ioaddr = ioremap (mmio_start, mmio_len);
 	if (ioaddr == NULL) {
@@ -632,6 +716,7 @@
 		rc = -EIO;
 		goto err_out_free_mmio;
 	}
+#endif /* USE_IO_OPS */
 
 	/* Soft reset the chip. */
 	RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdReset);
@@ -653,12 +738,14 @@
 		RTL_W8 (Config1, 0);
 	}
 
+#ifndef USE_IO_OPS
 	/* sanity checks -- ensure PIO and MMIO registers agree */
 	assert (inb (pio_start+Config0) == readb (ioaddr+Config0));
 	assert (inb (pio_start+Config1) == readb (ioaddr+Config1));
 	assert (inb (pio_start+TxConfig) == readb (ioaddr+TxConfig));
 	assert (inb (pio_start+RxConfig) == readb (ioaddr+RxConfig));
-	
+#endif /* !USE_IO_OPS */
+
 	/* make sure chip thinks PIO and MMIO are enabled */
 	tmp8 = RTL_R8 (Config1);
 	if ((tmp8 & Cfg1_PIO) == 0) {
@@ -671,7 +758,7 @@
 		rc = -EIO;
 		goto err_out_iounmap;
 	}
-	
+
 	/* identify chip attached to board */
 	tmp = RTL_R8 (ChipVersion);
 	for (i = arraysize (rtl_chip_info) - 1; i >= 0; i--)
@@ -691,15 +778,17 @@
 		tmp,
 		tp->chipset,
 		rtl_chip_info[tp->chipset].name);
-	
+
 	DPRINTK ("EXIT, returning 0\n");
 	*ioaddr_out = ioaddr;
 	*dev_out = dev;
-	return 0;	
+	return 0;
 
 err_out_iounmap:
 	assert (ioaddr > 0);
+#ifndef USE_IO_OPS
 	iounmap (ioaddr);
+#endif /* !USE_IO_OPS */
 err_out_free_mmio:
 	release_mem_region (mmio_start, mmio_len);
 err_out_free_pio:
@@ -720,14 +809,11 @@
 	int i, addr_len, option;
 	void *ioaddr = NULL;
 	static int board_idx = -1;
-	u8 tmp;
-
-#ifndef RTL8139_NDEBUG
 	static int printed_version = 0;
-#endif /* RTL8139_NDEBUG */
+	u8 tmp;
 
 	DPRINTK ("ENTER\n");
-	
+
 	assert (pdev != NULL);
 	assert (ent != NULL);
 
@@ -743,9 +829,9 @@
 		DPRINTK ("EXIT, returning %d\n", i);
 		return i;
 	}
-	
+
 	tp = dev->priv;
-	
+
 	assert (ioaddr != NULL);
 	assert (dev != NULL);
 	assert (tp != NULL);
@@ -766,7 +852,7 @@
 	dev->watchdog_timeo = TX_TIMEOUT;
 
 	dev->irq = pdev->irq;
-	dev->base_addr = pci_resource_start (pdev, 1);
+	dev->base_addr = (unsigned long) ioaddr;
 
 	/* dev->priv/tp zeroed and aligned in init_etherdev */
 	tp = dev->priv;
@@ -779,24 +865,23 @@
 	tp->mmio_addr = ioaddr;
 	tp->lock = SPIN_LOCK_UNLOCKED;
 
-	PCI_SET_DRIVER_DATA (pdev, dev);
+	pdev->driver_data = dev;
 
 	tp->phys[0] = 32;
 
-	printk (KERN_INFO "%s: %s board found at 0x%lx, IRQ %d\n",
-		dev->name, board_info[ent->driver_data].name,
-		dev->base_addr, dev->irq);
-
-	printk (KERN_INFO "%s:   Chip is '%s'\n",
-		dev->name,
-		rtl_chip_info[tp->chipset].name);
-
-	printk (KERN_INFO "%s:   MAC address "
-		"%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n",
+	printk (KERN_INFO "%s: %s at 0x%lx, "
+		"%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+		"IRQ %d\n",
 		dev->name,
+		board_info[ent->driver_data].name,
+		dev->base_addr,
 		dev->dev_addr[0], dev->dev_addr[1],
 		dev->dev_addr[2], dev->dev_addr[3],
-		dev->dev_addr[4], dev->dev_addr[5]);
+		dev->dev_addr[4], dev->dev_addr[5],
+		dev->irq);
+
+	printk (KERN_DEBUG "%s:  Identified 8139 chip type '%s'\n",
+		dev->name, rtl_chip_info[tp->chipset].name);
 
 	/* Put the chip into low-power mode. */
 	RTL_W8_F (Cfg9346, Cfg9346_Unlock);
@@ -831,7 +916,7 @@
 
 static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
 {
-	struct net_device *dev = PCI_GET_DRIVER_DATA (pdev);
+	struct net_device *dev = pdev->driver_data;
 	struct rtl8139_private *np;
 
 	DPRINTK ("ENTER\n");
@@ -843,7 +928,10 @@
 
 	unregister_netdev (dev);
 
+#ifndef USE_IO_OPS
 	iounmap (np->mmio_addr);
+#endif /* !USE_IO_OPS */
+
 	release_region (pci_resource_start (pdev, 0),
 			pci_resource_len (pdev, 0));
 	release_mem_region (pci_resource_start (pdev, 1),
@@ -857,7 +945,9 @@
 #endif /* RTL8139_NDEBUG */
 
 	kfree (dev);
-	
+
+	pdev->driver_data = NULL;
+
 	DPRINTK ("EXIT\n");
 }
 
@@ -1094,9 +1184,9 @@
 		DPRINTK ("EXIT, returning -ENOMEM\n");
 		MOD_DEC_USE_COUNT;
 		return -ENOMEM;
-		
+
 	}
-	
+
 	tp->full_duplex = tp->duplex_lock;
 	tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
 
@@ -1131,7 +1221,7 @@
 	u8 tmp;
 
 	DPRINTK ("ENTER\n");
-	
+
 	/* Soft reset the chip. */
 	RTL_W8 (ChipCmd, (RTL_R8 (ChipCmd) & ChipCmdClear) | CmdReset);
 	udelay (100);
@@ -1154,7 +1244,7 @@
 	RTL_W32_F (RxConfig, i);
 
 	/* Check this value: the documentation for IFG contradicts ifself. */
-	RTL_W32 (TxConfig, (TX_DMA_BURST << 8));
+	RTL_W32 (TxConfig, (TX_DMA_BURST << TxDMAShift));
 
 	/* unlock Config[01234] and BMCR register writes */
 	RTL_W8_F (Cfg9346, Cfg9346_Unlock);
@@ -1175,9 +1265,9 @@
 	if (tp->chipset >= CH_8139B) {
 		tmp = RTL_R8 (Config4) & ~(1<<2);
 		/* chip will clear Rx FIFO overflow automatically */
-		tmp |= (1<<7);  
+		tmp |= (1<<7);
 		RTL_W8 (Config4, tmp);
-	
+
 		/* disable magic packet scanning, which is enabled
 		 * when PM is enabled above (Config1) */
 		RTL_W8 (Config3, RTL_R8 (Config3) & ~(1<<5));
@@ -1334,15 +1424,13 @@
 {
 	struct net_device *dev = (struct net_device *) data;
 	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
+	void *ioaddr = tp->mmio_addr;
 	int next_tick = 60 * HZ;
 	int mii_reg5;
 
-	spin_lock_irq (&tp->lock);
-
 	mii_reg5 = mdio_read (dev, tp->phys[0], 5);
 
 	if (!tp->duplex_lock && mii_reg5 != 0xffff) {
-		void *ioaddr = tp->mmio_addr;
 		int duplex = (mii_reg5 & 0x0100)
 		    || (mii_reg5 & 0x01C0) == 0x0040;
 		if (tp->full_duplex != duplex) {
@@ -1371,8 +1459,6 @@
 		 dev->name, RTL_R8 (Config0),
 		 RTL_R8 (Config1));
 
-	spin_unlock_irq (&tp->lock);
-
 	tp->timer.expires = jiffies + next_tick;
 	add_timer (&tp->timer);
 }
@@ -1383,6 +1469,7 @@
 	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
 	void *ioaddr = tp->mmio_addr;
 	int i;
+	unsigned long flags;
 
 	DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x "
 		 "media %2.2x.\n", dev->name,
@@ -1390,25 +1477,20 @@
 		 RTL_R16 (IntrStatus),
 		 RTL_R8 (MediaStatus));
 
-	spin_lock_irq (&tp->lock);
-
 	/* Disable interrupts by clearing the interrupt mask. */
 	RTL_W16 (IntrMask, 0x0000);
 
-	spin_unlock_irq (&tp->lock);
-	
 	/* Emit info to figure out what went wrong. */
-	printk (KERN_DEBUG
-		"%s: Tx queue start entry %d  dirty entry %d.\n",
+	printk (KERN_DEBUG "%s: Tx queue start entry %d  dirty entry %d.\n",
 		dev->name, atomic_read (&tp->cur_tx),
 		atomic_read (&tp->dirty_tx));
 	for (i = 0; i < NUM_TX_DESC; i++)
 		printk (KERN_DEBUG "%s:  Tx descriptor %d is %8.8x.%s\n",
 			dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
-			i ==
-		      atomic_read (&tp->dirty_tx) % NUM_TX_DESC ? " (queue head)" : "");
+			i == atomic_read (&tp->dirty_tx) % NUM_TX_DESC ?
+				" (queue head)" : "");
 
-	spin_lock_irq (&tp->lock);
+	spin_lock_irqsave (&tp->lock, flags);
 
 	/* Stop a shared interrupt from scavenging while we are. */
 	atomic_set (&tp->cur_tx, 0);
@@ -1418,7 +1500,8 @@
 	for (i = 0; i < NUM_TX_DESC; i++) {
 		struct ring_info *rp = &tp->tx_info[i];
 		if (rp->mapping != 0) {
-			pci_unmap_single (tp->pci_dev, rp->mapping, rp->skb->len, PCI_DMA_TODEVICE);
+			pci_unmap_single (tp->pci_dev, rp->mapping,
+					  rp->skb->len, PCI_DMA_TODEVICE);
 			rp->mapping = 0;
 		}
 		if (rp->skb) {
@@ -1427,9 +1510,10 @@
 			tp->stats.tx_dropped++;
 		}
 	}
-	
-	spin_unlock_irq (&tp->lock);
 
+	spin_unlock_irqrestore (&tp->lock, flags);
+
+	/* ...and finally, reset everything */
 	rtl8139_hw_start (dev);
 }
 
@@ -1444,18 +1528,17 @@
 	/* Calculate the next Tx descriptor entry. */
 	entry = atomic_read (&tp->cur_tx) % NUM_TX_DESC;
 
+	assert (tp->tx_info[entry].skb == NULL);
+	assert (tp->tx_info[entry].mapping == 0);
+
 	tp->tx_info[entry].skb = skb;
-	tp->tx_info[entry].mapping = 0;
+	/* tp->tx_info[entry].mapping = 0; */
 	memcpy (tp->tx_buf[entry], skb->data, skb->len);
 
-	spin_lock_irq (&tp->lock);
-
 	/* Note: the chip doesn't have auto-pad! */
 	RTL_W32 (TxStatus0 + (entry * sizeof(u32)),
 		 tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
 
-	spin_unlock_irq (&tp->lock);
-
 	dev->trans_start = jiffies;
 	atomic_inc (&tp->cur_tx);
 	if ((atomic_read (&tp->cur_tx) - atomic_read (&tp->dirty_tx)) >= NUM_TX_DESC)
@@ -1477,20 +1560,15 @@
 	assert (dev != NULL);
 	assert (tp != NULL);
 	assert (ioaddr != NULL);
-	
-	/* drop lock held in rtl8139_interrupt */
-	spin_unlock (&tp->lock);
-	
+
 	dirty_tx = atomic_read (&tp->dirty_tx);
 
 	while ((atomic_read (&tp->cur_tx) - dirty_tx) > 0) {
 		int entry = dirty_tx % NUM_TX_DESC;
 		int txstatus;
 
-		spin_lock (&tp->lock);
-		txstatus = RTL_R32 (TxStatus0 + (entry * 4));
-		spin_unlock (&tp->lock);
-		
+		txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));
+
 		if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
 			break;	/* It still hasn't been Txed */
 
@@ -1502,9 +1580,7 @@
 			tp->stats.tx_errors++;
 			if (txstatus & TxAborted) {
 				tp->stats.tx_aborted_errors++;
-				spin_lock (&tp->lock);
-				RTL_W32 (TxConfig, (TX_DMA_BURST << 8));
-				spin_unlock (&tp->lock);
+				RTL_W32 (TxConfig, TxClearAbt | (TX_DMA_BURST << TxDMAShift));
 			}
 			if (txstatus & TxCarrierLost)
 				tp->stats.tx_carrier_errors++;
@@ -1551,17 +1627,63 @@
 #endif /* RTL8139_NDEBUG */
 
 	atomic_set (&tp->dirty_tx, dirty_tx);
-	
-	/* obtain lock need for rtl8139_interrupt */
-	spin_lock (&tp->lock);
+}
+
+
+/* TODO: clean this up!  Rx reset need not be this intensive */
+static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,
+			    struct rtl8139_private *tp, void *ioaddr)
+{
+	u8 tmp8;
+	int tmp_work = 1000;
+
+	DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n",
+	         dev->name, rx_status);
+	if (rx_status & RxTooLong) {
+		DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
+			 dev->name, rx_status);
+		/* A.C.: The chip hangs here. */
+	}
+	tp->stats.rx_errors++;
+	if (rx_status & (RxBadSymbol | RxBadAlign))
+		tp->stats.rx_frame_errors++;
+	if (rx_status & (RxRunt | RxTooLong))
+		tp->stats.rx_length_errors++;
+	if (rx_status & RxCRCErr)
+		tp->stats.rx_crc_errors++;
+	/* Reset the receiver, based on RealTek recommendation. (Bug?) */
+	tp->cur_rx = 0;
+
+	/* disable receive */
+	tmp8 = RTL_R8 (ChipCmd) & ChipCmdClear;
+	RTL_W8_F (ChipCmd, tmp8 | CmdTxEnb);
+
+	/* A.C.: Reset the multicast list. */
+	rtl8139_set_rx_mode (dev);
+
+	/* XXX potentially temporary hack to
+	 * restart hung receiver */
+	while (--tmp_work > 0) {
+		tmp8 = RTL_R8 (ChipCmd);
+		if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
+			break;
+		RTL_W8_F (ChipCmd,
+			  (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb);
+	}
+
+	/* G.S.: Re-enable receiver */
+	/* XXX temporary hack to work around receiver hang */
+	rtl8139_set_rx_mode (dev);
+
+	if (tmp_work <= 0)
+		printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
 }
 
 
 /* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
    field alignments and semantics. */
 static void rtl8139_rx_interrupt (struct net_device *dev,
-				  struct rtl8139_private *tp,
-				  void *ioaddr)
+				  struct rtl8139_private *tp, void *ioaddr)
 {
 	unsigned char *rx_ring;
 	u16 cur_rx;
@@ -1569,31 +1691,33 @@
 	assert (dev != NULL);
 	assert (tp != NULL);
 	assert (ioaddr != NULL);
-	
+
 	rx_ring = tp->rx_ring;
 	cur_rx = tp->cur_rx;
 
 	DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
-			" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
-			RTL_R16 (RxBufAddr),
-			RTL_R16 (RxBufPtr),
-			RTL_R8 (ChipCmd));
+		 " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
+		 RTL_R16 (RxBufAddr),
+		 RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
 
 	while ((RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
 		int ring_offset = cur_rx % RX_BUF_LEN;
 		u32 rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
 		int rx_size = rx_status >> 16;
+		struct sk_buff *skb;
+		int pkt_size = rx_size - 4;
 
 		DPRINTK ("%s:  rtl8139_rx() status %4.4x, size %4.4x,"
-			" cur %4.4x.\n", dev->name, rx_status,
-			rx_size, cur_rx);
+			 " cur %4.4x.\n", dev->name, rx_status,
+			 rx_size, cur_rx);
 #if RTL8139_DEBUG > 2
 		{
-		int i;
-		DPRINTK ("%s: Frame contents ", dev->name);
-		for (i = 0; i < 70; i++)
-			printk (" %2.2x", rx_ring[ring_offset + i]);
-		printk (".\n");
+			int i;
+			DPRINTK ("%s: Frame contents ", dev->name);
+			for (i = 0; i < 70; i++)
+				printk (" %2.2x",
+					rx_ring[ring_offset + i]);
+			printk (".\n");
 		}
 #endif
 
@@ -1609,128 +1733,68 @@
 		if (rx_size == 0xfff0)
 			break;
 
+		/* if Rx err received, Rx process gets reset, so
+		 * we abort any further Rx processing
+		 */
 		if (rx_status &
-		    (RxBadSymbol | RxRunt | RxTooLong | RxCRCErr |
-		     RxBadAlign)) {
-			u8 tmp8;
-			int tmp_work = 1000;
-
-			DPRINTK ("%s: Ethernet frame had errors,"
-					" status %8.8x.\n", dev->name,
-					rx_status);
-			if (rx_status & RxTooLong) {
-				DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
-						dev->name, rx_status);
-				/* A.C.: The chip hangs here. */
-			}
-			tp->stats.rx_errors++;
-			if (rx_status & (RxBadSymbol | RxBadAlign))
-				tp->stats.rx_frame_errors++;
-			if (rx_status & (RxRunt | RxTooLong))
-				tp->stats.rx_length_errors++;
-			if (rx_status & RxCRCErr)
-				tp->stats.rx_crc_errors++;
-			/* Reset the receiver, based on RealTek recommendation. (Bug?) */
-			tp->cur_rx = 0;
-
-			/* disable receive */
-			tmp8 = RTL_R8 (ChipCmd) & ChipCmdClear;
-			RTL_W8_F (ChipCmd, tmp8 | CmdTxEnb);
-
-			/* A.C.: Reset the multicast list. */
-			rtl8139_set_rx_mode (dev);
-
-			/* XXX potentially temporary hack to
-			 * restart hung receiver */
-			while (--tmp_work > 0) {
-				tmp8 = RTL_R8 (ChipCmd);
-				if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
-					break;
-				RTL_W8_F (ChipCmd, (tmp8 & ChipCmdClear) | CmdRxEnb | CmdTxEnb);
-			}
+		    (RxBadSymbol | RxRunt | RxTooLong | RxCRCErr | RxBadAlign)) {
+			rtl8139_rx_err (rx_status, dev, tp, ioaddr);
+			return;
+		}
 
-			/* G.S.: Re-enable receiver */
-			/* XXX temporary hack to work around receiver hang */
-			rtl8139_set_rx_mode (dev);
+		/* Malloc up new buffer, compatible with net-2e. */
+		/* Omit the four octet CRC from the length. */
 
-			if (tmp_work <= 0)			
-				printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
-		} else {
-			/* Malloc up new buffer, compatible with net-2e. */
-			/* Omit the four octet CRC from the length. */
-			struct sk_buff *skb;
-			int pkt_size = rx_size - 4;
-
-			skb = dev_alloc_skb (pkt_size + 2);
-			if (skb == NULL) {
-				printk (KERN_WARNING
-					"%s: Memory squeeze, dropping packet.\n",
-					dev->name);
-				/* We should check that some rx space is free.
-				   If not, free one and mark stats->rx_dropped++. */
-				tp->stats.rx_dropped++;
-				break;
-			}
-			skb->dev = dev;
-			skb_reserve (skb, 2);	/* 16 byte align the IP fields. */
+		/* TODO: consider allocating skb's outside of
+		 * interrupt context, both to speed interrupt processing,
+		 * and also to reduce the chances of having to
+		 * drop packets here under memory pressure.
+		 */
 
-			if (ring_offset + rx_size + 4 > RX_BUF_LEN) {
-				int semi_count =
-				    RX_BUF_LEN - ring_offset - 4;
-				/* This could presumably use two calls to copy_and_sum()? */
-				memcpy (skb_put (skb, semi_count),
-					&rx_ring[ring_offset + 4],
-					semi_count);
-				memcpy (skb_put (skb, pkt_size - semi_count),
-					rx_ring, pkt_size - semi_count);
-#ifdef RTL8139_DEBUG
-				{
-				int i;
-				printk (KERN_DEBUG "%s:  Frame wrap @%d",
-					dev->name, semi_count);
-				for (i = 0; i < 16; i++)
-					printk (" %2.2x", rx_ring[i]);
-				printk ("\n");
-				memset (rx_ring, 0xcc, 16);
-				}
-#endif /* RTL8139_DEBUG */
-
-			} else {
-				eth_copy_and_sum (skb,
-						  &rx_ring[ring_offset + 4],
-						  pkt_size, 0);
-				skb_put (skb, pkt_size);
-			}
-			skb->protocol = eth_type_trans (skb, dev);
-			netif_rx (skb);
-			tp->stats.rx_bytes += pkt_size;
-			tp->stats.rx_packets++;
+		skb = dev_alloc_skb (pkt_size + 2);
+		if (skb == NULL) {
+			printk (KERN_WARNING
+				"%s: Memory squeeze, dropping packet.\n",
+				dev->name);
+			tp->stats.rx_dropped++;
+			break;
 		}
+		skb->dev = dev;
+		skb_reserve (skb, 2);	/* 16 byte align the IP fields. */
+
+		eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
+		skb_put (skb, pkt_size);
+
+		skb->protocol = eth_type_trans (skb, dev);
+		netif_rx (skb);
+		tp->stats.rx_bytes += pkt_size;
+		tp->stats.rx_packets++;
 
 		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
 		RTL_W16_F (RxBufPtr, cur_rx - 16);
 	}
+
 	DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
-		" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
-		RTL_R16 (RxBufAddr),
-		RTL_R16 (RxBufPtr),
-		RTL_R8 (ChipCmd));
+		 " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
+		 RTL_R16 (RxBufAddr),
+		 RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+
 	tp->cur_rx = cur_rx;
 }
 
 
-static int rtl8139_weird_interrupt (struct net_device *dev,
-				    struct rtl8139_private *tp,
-				    void *ioaddr,
-				    int status, int link_changed)
-{
-	DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
-		 dev->name, status);
-		 
+static void rtl8139_weird_interrupt (struct net_device *dev,
+				     struct rtl8139_private *tp,
+				     void *ioaddr,
+				     int status, int link_changed)
+{
+	printk (KERN_DEBUG "%s: Abnormal interrupt, status %8.8x.\n",
+		dev->name, status);
+
 	assert (dev != NULL);
 	assert (tp != NULL);
 	assert (ioaddr != NULL);
-	
+
 	/* Update the error count. */
 	tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
 	RTL_W32 (RxMissed, 0);
@@ -1768,8 +1832,6 @@
 		printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
 			dev->name, pci_cmd_status);
 	}
-	
-	return 0;
 }
 
 
@@ -1785,14 +1847,14 @@
 	int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */
 
 	spin_lock (&tp->lock);
-	
+
 	do {
 		status = RTL_R16 (IntrStatus);
 
 		/* h/w no longer present (hotplug?) or major error, bail */
 		if (status == 0xFFFF)
 			break;
-		
+
 		/* Acknowledge all of the current interrupt sources ASAP, but
 		   an first get an additional status bit from CSCR. */
 		if (status & RxUnderrun)
@@ -1854,7 +1916,7 @@
 	}
 
 	spin_unlock (&tp->lock);
-	
+
 	DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
 		 dev->name, RTL_R16 (IntrStatus));
 }
@@ -1875,7 +1937,7 @@
 			dev->name, RTL_R16 (IntrStatus));
 
 	del_timer_sync (&tp->timer);
-	
+
 	spin_lock_irqsave (&tp->lock, flags);
 
 	/* Disable interrupts by clearing the interrupt mask. */
@@ -1889,7 +1951,7 @@
 	RTL_W32 (RxMissed, 0);
 
 	spin_unlock_irqrestore (&tp->lock, flags);
-	
+
 	/* snooze for a small bit */
 	if (current->need_resched)
 		schedule ();
@@ -2053,7 +2115,7 @@
 			set_bit (ether_crc (ETH_ALEN, mclist->dmi_addr) >> 26,
 				 mc_filter);
 	}
-	
+
 	/* if called from irq handler, lock already acquired */
 	if (!in_irq ())
 		spin_lock_irq (&tp->lock);
@@ -2074,13 +2136,13 @@
 
 static void rtl8139_suspend (struct pci_dev *pdev)
 {
-	struct net_device *dev = PCI_GET_DRIVER_DATA (pdev);
+	struct net_device *dev = pdev->driver_data;
 	struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv;
 	void *ioaddr = tp->mmio_addr;
 	unsigned long flags;
 
 	netif_device_detach (dev);
-	
+
 	spin_lock_irqsave (&tp->lock, flags);
 
 	/* Disable interrupts, stop Tx and Rx. */
@@ -2097,7 +2159,7 @@
 
 static void rtl8139_resume (struct pci_dev *pdev)
 {
-	struct net_device *dev = PCI_GET_DRIVER_DATA (pdev);
+	struct net_device *dev = pdev->driver_data;
 
 	netif_device_attach (dev);
 	rtl8139_hw_start (dev);

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