patch-2.4.21 linux-2.4.21/drivers/net/r8169.c

Next file: linux-2.4.21/drivers/net/rrunner.c
Previous file: linux-2.4.21/drivers/net/ppp_generic.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/drivers/net/r8169.c linux-2.4.21/drivers/net/r8169.c
@@ -0,0 +1,1131 @@
+/*
+=========================================================================
+ r8169.c: A RealTek RTL-8169 Gigabit Ethernet driver for Linux kernel 2.4.x.
+ --------------------------------------------------------------------
+
+ History:
+ Feb  4 2002	- created initially by ShuChen <shuchen@realtek.com.tw>.
+ May 20 2002	- Add link status force-mode and TBI mode support.
+=========================================================================
+  1. The media can be forced in 5 modes.
+	 Command: 'insmod r8169 media = SET_MEDIA'
+	 Ex:	  'insmod r8169 media = 0x04' will force PHY to operate in 100Mpbs Half-duplex.
+	
+	 SET_MEDIA can be:
+ 		_10_Half	= 0x01
+ 		_10_Full	= 0x02
+ 		_100_Half	= 0x04
+ 		_100_Full	= 0x08
+ 		_1000_Full	= 0x10
+  
+  2. Support TBI mode.
+=========================================================================
+VERSION 1.1	<2002/10/4>
+
+	The bit4:0 of MII register 4 is called "selector field", and have to be
+	00001b to indicate support of IEEE std 802.3 during NWay process of
+	exchanging Link Code Word (FLP). 
+
+VERSION 1.2	<2002/11/30>
+
+	- Large style cleanup
+	- Use ether_crc in stock kernel (linux/crc32.h)
+	- Copy mc_filter setup code from 8139cp
+	  (includes an optimization, and avoids set_bit use)
+
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+#define RTL8169_VERSION "1.2"
+#define MODULENAME "r8169"
+#define RTL8169_DRIVER_NAME   MODULENAME " Gigabit Ethernet driver " RTL8169_VERSION
+#define PFX MODULENAME ": "
+
+#ifdef RTL8169_DEBUG
+#define assert(expr) \
+        if(!(expr)) {					\
+	        printk( "Assertion failed! %s,%s,%s,line=%d\n",	\
+        	#expr,__FILE__,__FUNCTION__,__LINE__);		\
+        }
+#else
+#define assert(expr) do {} while (0)
+#endif
+
+/* media options */
+#define MAX_UNITS 8
+static int media[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+   The RTL chips use a 64 element hash table based on the Ethernet CRC.  */
+static int multicast_filter_limit = 32;
+
+/* MAC address length*/
+#define MAC_ADDR_LEN	6
+
+/* max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4).*/
+#define MAX_ETH_FRAME_SIZE	1536
+
+#define TX_FIFO_THRESH 256	/* In bytes */
+
+#define RX_FIFO_THRESH	7	/* 7 means NO threshold, 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 */
+#define EarlyTxThld 	0x3F	/* 0x3F means NO early transmit */
+#define RxPacketMaxSize	0x0800	/* Maximum size supported is 16K-1 */
+#define InterFrameGap	0x03	/* 3 means InterFrameGap = the shortest one */
+
+#define NUM_TX_DESC	64	/* Number of Tx descriptor registers */
+#define NUM_RX_DESC	64	/* Number of Rx descriptor registers */
+#define RX_BUF_SIZE	1536	/* Rx Buffer size */
+
+#define RTL_MIN_IO_SIZE 0x80
+#define TX_TIMEOUT  (6*HZ)
+
+/* write/read MMIO register */
+#define RTL_W8(reg, val8)	writeb ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16)	writew ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32)	writel ((val32), ioaddr + (reg))
+#define RTL_R8(reg)		readb (ioaddr + (reg))
+#define RTL_R16(reg)		readw (ioaddr + (reg))
+#define RTL_R32(reg)		((unsigned long) readl (ioaddr + (reg)))
+
+static struct {
+	const char *name;
+} board_info[] __devinitdata = {
+	{
+"RealTek RTL8169 Gigabit Ethernet"},};
+
+static struct pci_device_id rtl8169_pci_tbl[] __devinitdata = {
+	{0x10ec, 0x8169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,},
+};
+
+MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);
+
+enum RTL8169_registers {
+	MAC0 = 0,		/* Ethernet hardware address. */
+	MAR0 = 8,		/* Multicast filter. */
+	TxDescStartAddr = 0x20,
+	TxHDescStartAddr = 0x28,
+	FLASH = 0x30,
+	ERSR = 0x36,
+	ChipCmd = 0x37,
+	TxPoll = 0x38,
+	IntrMask = 0x3C,
+	IntrStatus = 0x3E,
+	TxConfig = 0x40,
+	RxConfig = 0x44,
+	RxMissed = 0x4C,
+	Cfg9346 = 0x50,
+	Config0 = 0x51,
+	Config1 = 0x52,
+	Config2 = 0x53,
+	Config3 = 0x54,
+	Config4 = 0x55,
+	Config5 = 0x56,
+	MultiIntr = 0x5C,
+	PHYAR = 0x60,
+	TBICSR = 0x64,
+	TBI_ANAR = 0x68,
+	TBI_LPAR = 0x6A,
+	PHYstatus = 0x6C,
+	RxMaxSize = 0xDA,
+	CPlusCmd = 0xE0,
+	RxDescStartAddr = 0xE4,
+	EarlyTxThres = 0xEC,
+	FuncEvent = 0xF0,
+	FuncEventMask = 0xF4,
+	FuncPresetState = 0xF8,
+	FuncForceEvent = 0xFC,
+};
+
+enum RTL8169_register_content {
+	/*InterruptStatusBits */
+	SYSErr = 0x8000,
+	PCSTimeout = 0x4000,
+	SWInt = 0x0100,
+	TxDescUnavail = 0x80,
+	RxFIFOOver = 0x40,
+	RxUnderrun = 0x20,
+	RxOverflow = 0x10,
+	TxErr = 0x08,
+	TxOK = 0x04,
+	RxErr = 0x02,
+	RxOK = 0x01,
+
+	/*RxStatusDesc */
+	RxRES = 0x00200000,
+	RxCRC = 0x00080000,
+	RxRUNT = 0x00100000,
+	RxRWT = 0x00400000,
+
+	/*ChipCmdBits */
+	CmdReset = 0x10,
+	CmdRxEnb = 0x08,
+	CmdTxEnb = 0x04,
+	RxBufEmpty = 0x01,
+
+	/*Cfg9346Bits */
+	Cfg9346_Lock = 0x00,
+	Cfg9346_Unlock = 0xC0,
+
+	/*rx_mode_bits */
+	AcceptErr = 0x20,
+	AcceptRunt = 0x10,
+	AcceptBroadcast = 0x08,
+	AcceptMulticast = 0x04,
+	AcceptMyPhys = 0x02,
+	AcceptAllPhys = 0x01,
+
+	/*RxConfigBits */
+	RxCfgFIFOShift = 13,
+	RxCfgDMAShift = 8,
+
+	/*TxConfigBits */
+	TxInterFrameGapShift = 24,
+	TxDMAShift = 8,		/* DMA burst value (0-7) is shift this many bits */
+
+	/*rtl8169_PHYstatus */
+	TBI_Enable = 0x80,
+	TxFlowCtrl = 0x40,
+	RxFlowCtrl = 0x20,
+	_1000bpsF = 0x10,
+	_100bps = 0x08,
+	_10bps = 0x04,
+	LinkStatus = 0x02,
+	FullDup = 0x01,
+
+	/*GIGABIT_PHY_registers */
+	PHY_CTRL_REG = 0,
+	PHY_STAT_REG = 1,
+	PHY_AUTO_NEGO_REG = 4,
+	PHY_1000_CTRL_REG = 9,
+
+	/*GIGABIT_PHY_REG_BIT */
+	PHY_Restart_Auto_Nego = 0x0200,
+	PHY_Enable_Auto_Nego = 0x1000,
+
+	//PHY_STAT_REG = 1;
+	PHY_Auto_Neco_Comp = 0x0020,
+
+	//PHY_AUTO_NEGO_REG = 4;
+	PHY_Cap_10_Half = 0x0020,
+	PHY_Cap_10_Full = 0x0040,
+	PHY_Cap_100_Half = 0x0080,
+	PHY_Cap_100_Full = 0x0100,
+
+	//PHY_1000_CTRL_REG = 9;
+	PHY_Cap_1000_Full = 0x0200,
+
+	PHY_Cap_Null = 0x0,
+
+	/*_MediaType*/
+	_10_Half = 0x01,
+	_10_Full = 0x02,
+	_100_Half = 0x04,
+	_100_Full = 0x08,
+	_1000_Full = 0x10,
+
+	/*_TBICSRBit*/
+	TBILinkOK = 0x02000000,
+};
+
+const static struct {
+	const char *name;
+	u8 version;		/* depend on RTL8169 docs */
+	u32 RxConfigMask;	/* should clear the bits supported by this chip */
+} rtl_chip_info[] = {
+	{
+"RTL-8169", 0x00, 0xff7e1880,},};
+
+enum _DescStatusBit {
+	OWNbit = 0x80000000,
+	EORbit = 0x40000000,
+	FSbit = 0x20000000,
+	LSbit = 0x10000000,
+};
+
+struct TxDesc {
+	u32 status;
+	u32 vlan_tag;
+	u32 buf_addr;
+	u32 buf_Haddr;
+};
+
+struct RxDesc {
+	u32 status;
+	u32 vlan_tag;
+	u32 buf_addr;
+	u32 buf_Haddr;
+};
+
+struct rtl8169_private {
+	void *mmio_addr;	/* memory map physical address */
+	struct pci_dev *pci_dev;	/* Index of PCI device  */
+	struct net_device_stats stats;	/* statistics of net device */
+	spinlock_t lock;	/* spin lock flag */
+	int chipset;
+	unsigned long cur_rx;	/* Index into the Rx descriptor buffer of next Rx pkt. */
+	unsigned long cur_tx;	/* Index into the Tx descriptor buffer of next Rx pkt. */
+	unsigned long dirty_tx;
+	unsigned char *TxDescArrays;	/* Index of Tx Descriptor buffer */
+	unsigned char *RxDescArrays;	/* Index of Rx Descriptor buffer */
+	struct TxDesc *TxDescArray;	/* Index of 256-alignment Tx Descriptor buffer */
+	struct RxDesc *RxDescArray;	/* Index of 256-alignment Rx Descriptor buffer */
+	unsigned char *RxBufferRings;	/* Index of Rx Buffer  */
+	unsigned char *RxBufferRing[NUM_RX_DESC];	/* Index of Rx Buffer array */
+	struct sk_buff *Tx_skbuff[NUM_TX_DESC];	/* Index of Transmit data buffer */
+};
+
+MODULE_AUTHOR("Realtek");
+MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver");
+MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+static int rtl8169_open(struct net_device *dev);
+static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void rtl8169_interrupt(int irq, void *dev_instance,
+			      struct pt_regs *regs);
+static void rtl8169_init_ring(struct net_device *dev);
+static void rtl8169_hw_start(struct net_device *dev);
+static int rtl8169_close(struct net_device *dev);
+static void rtl8169_set_rx_mode(struct net_device *dev);
+static void rtl8169_tx_timeout(struct net_device *dev);
+static struct net_device_stats *rtl8169_get_stats(struct net_device *netdev);
+
+static const u16 rtl8169_intr_mask =
+    SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK |
+    RxErr | RxOK;
+static const unsigned int rtl8169_rx_config =
+    (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
+
+void
+mdio_write(void *ioaddr, int RegAddr, int value)
+{
+	int i;
+
+	RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value);
+	udelay(1000);
+
+	for (i = 2000; i > 0; i--) {
+		// Check if the RTL8169 has completed writing to the specified MII register
+		if (!(RTL_R32(PHYAR) & 0x80000000)) {
+			break;
+		} else {
+			udelay(100);
+		}
+	}
+}
+
+int
+mdio_read(void *ioaddr, int RegAddr)
+{
+	int i, value = -1;
+
+	RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16);
+	udelay(1000);
+
+	for (i = 2000; i > 0; i--) {
+		// Check if the RTL8169 has completed retrieving data from the specified MII register
+		if (RTL_R32(PHYAR) & 0x80000000) {
+			value = (int) (RTL_R32(PHYAR) & 0xFFFF);
+			break;
+		} else {
+			udelay(100);
+		}
+	}
+	return value;
+}
+
+static int __devinit
+rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out,
+		   void **ioaddr_out)
+{
+	void *ioaddr = NULL;
+	struct net_device *dev;
+	struct rtl8169_private *tp;
+	int rc, i;
+	unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
+	u32 tmp;
+
+	assert(pdev != NULL);
+	assert(ioaddr_out != NULL);
+
+	*ioaddr_out = NULL;
+	*dev_out = NULL;
+
+	// dev zeroed in init_etherdev 
+	dev = init_etherdev(NULL, sizeof (*tp));
+	if (dev == NULL) {
+		printk(KERN_ERR PFX "unable to alloc new ethernet\n");
+		return -ENOMEM;
+	}
+
+	SET_MODULE_OWNER(dev);
+	tp = dev->priv;
+
+	// enable device (incl. PCI PM wakeup and hotplug setup)
+	rc = pci_enable_device(pdev);
+	if (rc)
+		goto err_out;
+
+	mmio_start = pci_resource_start(pdev, 1);
+	mmio_end = pci_resource_end(pdev, 1);
+	mmio_flags = pci_resource_flags(pdev, 1);
+	mmio_len = pci_resource_len(pdev, 1);
+
+	// 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 (mmio_len < RTL_MIN_IO_SIZE) {
+		printk(KERN_ERR PFX "Invalid PCI region size(s), aborting\n");
+		rc = -ENODEV;
+		goto err_out;
+	}
+
+	rc = pci_request_regions(pdev, dev->name);
+	if (rc)
+		goto err_out;
+
+	// enable PCI bus-mastering
+	pci_set_master(pdev);
+
+	// ioremap MMIO region 
+	ioaddr = ioremap(mmio_start, mmio_len);
+	if (ioaddr == NULL) {
+		printk(KERN_ERR PFX "cannot remap MMIO, aborting\n");
+		rc = -EIO;
+		goto err_out_free_res;
+	}
+
+	// Soft reset the chip. 
+	RTL_W8(ChipCmd, CmdReset);
+
+	// Check that the chip has finished the reset.
+	for (i = 1000; i > 0; i--)
+		if ((RTL_R8(ChipCmd) & CmdReset) == 0)
+			break;
+		else
+			udelay(10);
+
+	// identify chip attached to board
+	tmp = RTL_R32(TxConfig);
+	tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24;
+
+	for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--)
+		if (tmp == rtl_chip_info[i].version) {
+			tp->chipset = i;
+			goto match;
+		}
+	//if unknown chip, assume array element #0, original RTL-8169 in this case
+	printk(KERN_DEBUG PFX
+	       "PCI device %s: unknown chip version, assuming RTL-8169\n",
+	       pdev->slot_name);
+	printk(KERN_DEBUG PFX "PCI device %s: TxConfig = 0x%lx\n",
+	       pdev->slot_name, (unsigned long) RTL_R32(TxConfig));
+	tp->chipset = 0;
+
+match:
+	*ioaddr_out = ioaddr;
+	*dev_out = dev;
+	return 0;
+
+err_out_free_res:
+	pci_release_regions(pdev);
+
+err_out:
+	unregister_netdev(dev);
+	kfree(dev);
+	return rc;
+}
+
+static int __devinit
+rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct net_device *dev = NULL;
+	struct rtl8169_private *tp = NULL;
+	void *ioaddr = NULL;
+	static int board_idx = -1;
+	static int printed_version = 0;
+	int i;
+	int option = -1, Cap10_100 = 0, Cap1000 = 0;
+
+	assert(pdev != NULL);
+	assert(ent != NULL);
+
+	board_idx++;
+
+	if (!printed_version) {
+		printk(KERN_INFO RTL8169_DRIVER_NAME " loaded\n");
+		printed_version = 1;
+	}
+
+	i = rtl8169_init_board(pdev, &dev, &ioaddr);
+	if (i < 0) {
+		return i;
+	}
+
+	tp = dev->priv;
+	assert(ioaddr != NULL);
+	assert(dev != NULL);
+	assert(tp != NULL);
+
+	// Get MAC address //
+	for (i = 0; i < MAC_ADDR_LEN; i++) {
+		dev->dev_addr[i] = RTL_R8(MAC0 + i);
+	}
+
+	dev->open = rtl8169_open;
+	dev->hard_start_xmit = rtl8169_start_xmit;
+	dev->get_stats = rtl8169_get_stats;
+	dev->stop = rtl8169_close;
+	dev->tx_timeout = rtl8169_tx_timeout;
+	dev->set_multicast_list = rtl8169_set_rx_mode;
+	dev->watchdog_timeo = TX_TIMEOUT;
+	dev->irq = pdev->irq;
+	dev->base_addr = (unsigned long) ioaddr;
+//      dev->do_ioctl           = mii_ioctl;
+
+	tp = dev->priv;		// private data //
+	tp->pci_dev = pdev;
+	tp->mmio_addr = ioaddr;
+
+	printk(KERN_DEBUG "%s: Identified chip type is '%s'.\n", dev->name,
+	       rtl_chip_info[tp->chipset].name);
+
+	spin_lock_init(&tp->lock);
+
+	pci_set_drvdata(pdev, dev);
+
+	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->irq);
+
+	// if TBI is not endbled
+	if (!(RTL_R8(PHYstatus) & TBI_Enable)) {
+		int val = mdio_read(ioaddr, PHY_AUTO_NEGO_REG);
+
+		option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
+		// Force RTL8169 in 10/100/1000 Full/Half mode.
+		if (option > 0) {
+			printk(KERN_INFO "%s: Force-mode Enabled.\n",
+			       dev->name);
+			Cap10_100 = 0, Cap1000 = 0;
+			switch (option) {
+			case _10_Half:
+				Cap10_100 = PHY_Cap_10_Half;
+				Cap1000 = PHY_Cap_Null;
+				break;
+			case _10_Full:
+				Cap10_100 = PHY_Cap_10_Full;
+				Cap1000 = PHY_Cap_Null;
+				break;
+			case _100_Half:
+				Cap10_100 = PHY_Cap_100_Half;
+				Cap1000 = PHY_Cap_Null;
+				break;
+			case _100_Full:
+				Cap10_100 = PHY_Cap_100_Full;
+				Cap1000 = PHY_Cap_Null;
+				break;
+			case _1000_Full:
+				Cap10_100 = PHY_Cap_Null;
+				Cap1000 = PHY_Cap_1000_Full;
+				break;
+			default:
+				break;
+			}
+			mdio_write(ioaddr, PHY_AUTO_NEGO_REG, Cap10_100 | (val & 0x1F));	//leave PHY_AUTO_NEGO_REG bit4:0 unchanged
+			mdio_write(ioaddr, PHY_1000_CTRL_REG, Cap1000);
+		} else {
+			printk(KERN_INFO "%s: Auto-negotiation Enabled.\n",
+			       dev->name);
+
+			// enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged
+			mdio_write(ioaddr, PHY_AUTO_NEGO_REG,
+				   PHY_Cap_10_Half | PHY_Cap_10_Full |
+				   PHY_Cap_100_Half | PHY_Cap_100_Full | (val &
+									  0x1F));
+
+			// enable 1000 Full Mode
+			mdio_write(ioaddr, PHY_1000_CTRL_REG,
+				   PHY_Cap_1000_Full);
+
+		}
+
+		// Enable auto-negotiation and restart auto-nigotiation
+		mdio_write(ioaddr, PHY_CTRL_REG,
+			   PHY_Enable_Auto_Nego | PHY_Restart_Auto_Nego);
+		udelay(100);
+
+		// wait for auto-negotiation process
+		for (i = 10000; i > 0; i--) {
+			//check if auto-negotiation complete
+			if (mdio_read(ioaddr, PHY_STAT_REG) &
+			    PHY_Auto_Neco_Comp) {
+				udelay(100);
+				option = RTL_R8(PHYstatus);
+				if (option & _1000bpsF) {
+					printk(KERN_INFO
+					       "%s: 1000Mbps Full-duplex operation.\n",
+					       dev->name);
+				} else {
+					printk(KERN_INFO
+					       "%s: %sMbps %s-duplex operation.\n",
+					       dev->name,
+					       (option & _100bps) ? "100" :
+					       "10",
+					       (option & FullDup) ? "Full" :
+					       "Half");
+				}
+				break;
+			} else {
+				udelay(100);
+			}
+		}		// end for-loop to wait for auto-negotiation process
+
+	} else {
+		udelay(100);
+		printk(KERN_INFO
+		       "%s: 1000Mbps Full-duplex operation, TBI Link %s!\n",
+		       dev->name,
+		       (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed");
+
+	}
+
+	return 0;
+}
+
+static void __devexit
+rtl8169_remove_one(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct rtl8169_private *tp = (struct rtl8169_private *) (dev->priv);
+
+	assert(dev != NULL);
+	assert(tp != NULL);
+
+	unregister_netdev(dev);
+	iounmap(tp->mmio_addr);
+	pci_release_regions(pdev);
+
+	// poison memory before freeing 
+	memset(dev, 0xBC,
+	       sizeof (struct net_device) + sizeof (struct rtl8169_private));
+
+	kfree(dev);
+	pci_set_drvdata(pdev, NULL);
+}
+
+static int
+rtl8169_open(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev->priv;
+	int retval;
+	u8 diff;
+	u32 TxPhyAddr, RxPhyAddr;
+
+	retval =
+	    request_irq(dev->irq, rtl8169_interrupt, SA_SHIRQ, dev->name, dev);
+	if (retval) {
+		return retval;
+	}
+
+	tp->TxDescArrays =
+	    kmalloc(NUM_TX_DESC * sizeof (struct TxDesc) + 256, GFP_KERNEL);
+	// Tx Desscriptor needs 256 bytes alignment;
+	TxPhyAddr = virt_to_bus(tp->TxDescArrays);
+	diff = 256 - (TxPhyAddr - ((TxPhyAddr >> 8) << 8));
+	TxPhyAddr += diff;
+	tp->TxDescArray = (struct TxDesc *) (tp->TxDescArrays + diff);
+
+	tp->RxDescArrays =
+	    kmalloc(NUM_RX_DESC * sizeof (struct RxDesc) + 256, GFP_KERNEL);
+	// Rx Desscriptor needs 256 bytes alignment;
+	RxPhyAddr = virt_to_bus(tp->RxDescArrays);
+	diff = 256 - (RxPhyAddr - ((RxPhyAddr >> 8) << 8));
+	RxPhyAddr += diff;
+	tp->RxDescArray = (struct RxDesc *) (tp->RxDescArrays + diff);
+
+	if (tp->TxDescArrays == NULL || tp->RxDescArrays == NULL) {
+		printk(KERN_INFO
+		       "Allocate RxDescArray or TxDescArray failed\n");
+		free_irq(dev->irq, dev);
+		if (tp->TxDescArrays)
+			kfree(tp->TxDescArrays);
+		if (tp->RxDescArrays)
+			kfree(tp->RxDescArrays);
+		return -ENOMEM;
+	}
+	tp->RxBufferRings = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL);
+	if (tp->RxBufferRings == NULL) {
+		printk(KERN_INFO "Allocate RxBufferRing failed\n");
+	}
+
+	rtl8169_init_ring(dev);
+	rtl8169_hw_start(dev);
+
+	return 0;
+
+}
+
+static void
+rtl8169_hw_start(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	u32 i;
+
+	/* Soft reset the chip. */
+	RTL_W8(ChipCmd, CmdReset);
+
+	/* Check that the chip has finished the reset. */
+	for (i = 1000; i > 0; i--) {
+		if ((RTL_R8(ChipCmd) & CmdReset) == 0)
+			break;
+		else
+			udelay(10);
+	}
+
+	RTL_W8(Cfg9346, Cfg9346_Unlock);
+	RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+	RTL_W8(EarlyTxThres, EarlyTxThld);
+
+	// For gigabit rtl8169
+	RTL_W16(RxMaxSize, RxPacketMaxSize);
+
+	// Set Rx Config register
+	i = rtl8169_rx_config | (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].
+				 RxConfigMask);
+	RTL_W32(RxConfig, i);
+
+	/* Set DMA burst size and Interframe Gap Time */
+	RTL_W32(TxConfig,
+		(TX_DMA_BURST << TxDMAShift) | (InterFrameGap <<
+						TxInterFrameGapShift));
+
+	tp->cur_rx = 0;
+
+	RTL_W32(TxDescStartAddr, virt_to_bus(tp->TxDescArray));
+	RTL_W32(RxDescStartAddr, virt_to_bus(tp->RxDescArray));
+	RTL_W8(Cfg9346, Cfg9346_Lock);
+	udelay(10);
+
+	RTL_W32(RxMissed, 0);
+
+	rtl8169_set_rx_mode(dev);
+
+	/* no early-rx interrupts */
+	RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
+
+	/* Enable all known interrupts by setting the interrupt mask. */
+	RTL_W16(IntrMask, rtl8169_intr_mask);
+
+	netif_start_queue(dev);
+
+}
+
+static void
+rtl8169_init_ring(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev->priv;
+	int i;
+
+	tp->cur_rx = 0;
+	tp->cur_tx = 0;
+	tp->dirty_tx = 0;
+	memset(tp->TxDescArray, 0x0, NUM_TX_DESC * sizeof (struct TxDesc));
+	memset(tp->RxDescArray, 0x0, NUM_RX_DESC * sizeof (struct RxDesc));
+
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		tp->Tx_skbuff[i] = NULL;
+	}
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		if (i == (NUM_RX_DESC - 1))
+			tp->RxDescArray[i].status =
+			    (OWNbit | EORbit) + RX_BUF_SIZE;
+		else
+			tp->RxDescArray[i].status = OWNbit + RX_BUF_SIZE;
+
+		tp->RxBufferRing[i] = &(tp->RxBufferRings[i * RX_BUF_SIZE]);
+		tp->RxDescArray[i].buf_addr = virt_to_bus(tp->RxBufferRing[i]);
+	}
+}
+
+static void
+rtl8169_tx_clear(struct rtl8169_private *tp)
+{
+	int i;
+
+	tp->cur_tx = 0;
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		if (tp->Tx_skbuff[i] != NULL) {
+			dev_kfree_skb(tp->Tx_skbuff[i]);
+			tp->Tx_skbuff[i] = NULL;
+			tp->stats.tx_dropped++;
+		}
+	}
+}
+
+static void
+rtl8169_tx_timeout(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	u8 tmp8;
+
+	/* disable Tx, if not already */
+	tmp8 = RTL_R8(ChipCmd);
+	if (tmp8 & CmdTxEnb)
+		RTL_W8(ChipCmd, tmp8 & ~CmdTxEnb);
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	RTL_W16(IntrMask, 0x0000);
+
+	/* Stop a shared interrupt from scavenging while we are. */
+	spin_lock_irq(&tp->lock);
+	rtl8169_tx_clear(tp);
+	spin_unlock_irq(&tp->lock);
+
+	/* ...and finally, reset everything */
+	rtl8169_hw_start(dev);
+
+	netif_wake_queue(dev);
+}
+
+static int
+rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	int entry = tp->cur_tx % NUM_TX_DESC;
+
+	if(skb->len < ETH_ZLEN)
+	{
+		skb = skb_padto(skb, ETH_ZLEN);
+		if(skb == NULL)
+			return 0;
+	}
+	
+	spin_lock_irq(&tp->lock);
+
+	if ((tp->TxDescArray[entry].status & OWNbit) == 0) {
+		tp->Tx_skbuff[entry] = skb;
+		tp->TxDescArray[entry].buf_addr = virt_to_bus(skb->data);
+		if (entry != (NUM_TX_DESC - 1))
+			tp->TxDescArray[entry].status =
+			    (OWNbit | FSbit | LSbit) | ((skb->len > ETH_ZLEN) ?
+							skb->len : ETH_ZLEN);
+		else
+			tp->TxDescArray[entry].status =
+			    (OWNbit | EORbit | FSbit | LSbit) |
+			    ((skb->len > ETH_ZLEN) ? skb->len : ETH_ZLEN);
+
+		RTL_W8(TxPoll, 0x40);	//set polling bit
+
+		dev->trans_start = jiffies;
+
+		tp->cur_tx++;
+	}
+
+	spin_unlock_irq(&tp->lock);
+
+	if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) {
+		netif_stop_queue(dev);
+	}
+
+	return 0;
+}
+
+static void
+rtl8169_tx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
+		     void *ioaddr)
+{
+	unsigned long dirty_tx, tx_left = 0;
+	int entry = tp->cur_tx % NUM_TX_DESC;
+
+	assert(dev != NULL);
+	assert(tp != NULL);
+	assert(ioaddr != NULL);
+
+	dirty_tx = tp->dirty_tx;
+	tx_left = tp->cur_tx - dirty_tx;
+
+	while (tx_left > 0) {
+		if ((tp->TxDescArray[entry].status & OWNbit) == 0) {
+			dev_kfree_skb_irq(tp->
+					  Tx_skbuff[dirty_tx % NUM_TX_DESC]);
+			tp->Tx_skbuff[dirty_tx % NUM_TX_DESC] = NULL;
+			tp->stats.tx_packets++;
+			dirty_tx++;
+			tx_left--;
+			entry++;
+		}
+	}
+
+	if (tp->dirty_tx != dirty_tx) {
+		tp->dirty_tx = dirty_tx;
+		if (netif_queue_stopped(dev))
+			netif_wake_queue(dev);
+	}
+}
+
+static void
+rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
+		     void *ioaddr)
+{
+	int cur_rx;
+	struct sk_buff *skb;
+	int pkt_size = 0;
+
+	assert(dev != NULL);
+	assert(tp != NULL);
+	assert(ioaddr != NULL);
+
+	cur_rx = tp->cur_rx;
+
+	while ((tp->RxDescArray[cur_rx].status & OWNbit) == 0) {
+
+		if (tp->RxDescArray[cur_rx].status & RxRES) {
+			printk(KERN_INFO "%s: Rx ERROR!!!\n", dev->name);
+			tp->stats.rx_errors++;
+			if (tp->RxDescArray[cur_rx].status & (RxRWT | RxRUNT))
+				tp->stats.rx_length_errors++;
+			if (tp->RxDescArray[cur_rx].status & RxCRC)
+				tp->stats.rx_crc_errors++;
+		} else {
+			pkt_size =
+			    (int) (tp->RxDescArray[cur_rx].
+				   status & 0x00001FFF) - 4;
+			skb = dev_alloc_skb(pkt_size + 2);
+			if (skb != NULL) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);	// 16 byte align the IP fields. //
+				eth_copy_and_sum(skb, tp->RxBufferRing[cur_rx],
+						 pkt_size, 0);
+				skb_put(skb, pkt_size);
+				skb->protocol = eth_type_trans(skb, dev);
+				netif_rx(skb);
+
+				if (cur_rx == (NUM_RX_DESC - 1))
+					tp->RxDescArray[cur_rx].status =
+					    (OWNbit | EORbit) + RX_BUF_SIZE;
+				else
+					tp->RxDescArray[cur_rx].status =
+					    OWNbit + RX_BUF_SIZE;
+
+				tp->RxDescArray[cur_rx].buf_addr =
+				    virt_to_bus(tp->RxBufferRing[cur_rx]);
+				dev->last_rx = jiffies;
+				tp->stats.rx_bytes += pkt_size;
+				tp->stats.rx_packets++;
+			} else {
+				printk(KERN_WARNING
+				       "%s: Memory squeeze, deferring 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++;
+			}
+		}
+
+		cur_rx = (cur_rx + 1) % NUM_RX_DESC;
+
+	}
+
+	tp->cur_rx = cur_rx;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */
+static void
+rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_instance;
+	struct rtl8169_private *tp = dev->priv;
+	int boguscnt = max_interrupt_work;
+	void *ioaddr = tp->mmio_addr;
+	int status = 0;
+
+	do {
+		status = RTL_R16(IntrStatus);
+
+		/* h/w no longer present (hotplug?) or major error, bail */
+		if (status == 0xFFFF)
+			break;
+
+/*
+		if (status & RxUnderrun)
+			link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
+*/
+		RTL_W16(IntrStatus,
+			(status & RxFIFOOver) ? (status | RxOverflow) : status);
+
+		if ((status &
+		     (SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
+		      | TxErr | TxOK | RxErr | RxOK)) == 0)
+			break;
+
+		// Rx interrupt 
+		if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver)) {
+			rtl8169_rx_interrupt(dev, tp, ioaddr);
+		}
+		// Tx interrupt
+		if (status & (TxOK | TxErr)) {
+			spin_lock(&tp->lock);
+			rtl8169_tx_interrupt(dev, tp, ioaddr);
+			spin_unlock(&tp->lock);
+		}
+
+		boguscnt--;
+	} while (boguscnt > 0);
+
+	if (boguscnt <= 0) {
+		printk(KERN_WARNING "%s: Too much work at interrupt!\n",
+		       dev->name);
+		/* Clear all interrupt sources. */
+		RTL_W16(IntrStatus, 0xffff);
+	}
+}
+
+static int
+rtl8169_close(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	int i;
+
+	netif_stop_queue(dev);
+
+	spin_lock_irq(&tp->lock);
+
+	/* Stop the chip's Tx and Rx DMA processes. */
+	RTL_W8(ChipCmd, 0x00);
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	RTL_W16(IntrMask, 0x0000);
+
+	/* Update the error counts. */
+	tp->stats.rx_missed_errors += RTL_R32(RxMissed);
+	RTL_W32(RxMissed, 0);
+
+	spin_unlock_irq(&tp->lock);
+
+	synchronize_irq();
+	free_irq(dev->irq, dev);
+
+	rtl8169_tx_clear(tp);
+	kfree(tp->TxDescArrays);
+	kfree(tp->RxDescArrays);
+	tp->TxDescArrays = NULL;
+	tp->RxDescArrays = NULL;
+	tp->TxDescArray = NULL;
+	tp->RxDescArray = NULL;
+	kfree(tp->RxBufferRings);
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		tp->RxBufferRing[i] = NULL;
+	}
+
+	return 0;
+}
+
+static void
+rtl8169_set_rx_mode(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev->priv;
+	void *ioaddr = tp->mmio_addr;
+	unsigned long flags;
+	u32 mc_filter[2];	/* Multicast hash filter */
+	int i, rx_mode;
+	u32 tmp = 0;
+
+	if (dev->flags & IFF_PROMISC) {
+		/* Unconditionally log net taps. */
+		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n",
+		       dev->name);
+		rx_mode =
+		    AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+		    AcceptAllPhys;
+		mc_filter[1] = mc_filter[0] = 0xffffffff;
+	} else if ((dev->mc_count > multicast_filter_limit)
+		   || (dev->flags & IFF_ALLMULTI)) {
+		/* Too many to filter perfectly -- accept all multicasts. */
+		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+		mc_filter[1] = mc_filter[0] = 0xffffffff;
+	} else {
+		struct dev_mc_list *mclist;
+		rx_mode = AcceptBroadcast | AcceptMyPhys;
+		mc_filter[1] = mc_filter[0] = 0;
+		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+		     i++, mclist = mclist->next) {
+			int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+			mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+			rx_mode |= AcceptMulticast;
+		}
+	}
+
+	spin_lock_irqsave(&tp->lock, flags);
+
+	tmp =
+	    rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) &
+					   rtl_chip_info[tp->chipset].
+					   RxConfigMask);
+
+	RTL_W32(RxConfig, tmp);
+	RTL_W32(MAR0 + 0, mc_filter[0]);
+	RTL_W32(MAR0 + 4, mc_filter[1]);
+
+	spin_unlock_irqrestore(&tp->lock, flags);
+}
+
+struct net_device_stats *
+rtl8169_get_stats(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev->priv;
+
+	return &tp->stats;
+}
+
+static struct pci_driver rtl8169_pci_driver = {
+	.name		= MODULENAME,
+	.id_table	= rtl8169_pci_tbl,
+	.probe		= rtl8169_init_one,
+	.remove		= __devexit_p(rtl8169_remove_one),
+	.suspend	= NULL,
+	.resume		= NULL,
+};
+
+static int __init
+rtl8169_init_module(void)
+{
+	return pci_module_init(&rtl8169_pci_driver);
+}
+
+static void __exit
+rtl8169_cleanup_module(void)
+{
+	pci_unregister_driver(&rtl8169_pci_driver);
+}
+
+module_init(rtl8169_init_module);
+module_exit(rtl8169_cleanup_module);

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