<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">Parent repository is bk://gkernel.bkbits.net/netdev-2.6
======== ChangeSet 1.1615 ========
D 1.1615 04/03/02 17:56:15-08:00 akpm@mnm.(none) 37890 37887 0/0/1
P ChangeSet
C Merge mnm.(none):/usr/src/bk25 into mnm.(none):/usr/src/bk-netdev
------------------------------------------------

diff -Nru a/Documentation/networking/netconsole.txt b/Documentation/networking/netconsole.txt
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/Documentation/networking/netconsole.txt	Tue Mar  2 17:57:45 2004
@@ -0,0 +1,57 @@
+
+started by Ingo Molnar &lt;mingo@redhat.com&gt;, 2001.09.17
+2.6 port and netpoll api by Matt Mackall &lt;mpm@selenic.com&gt;, Sep 9 2003
+
+Please send bug reports to Matt Mackall &lt;mpm@selenic.com&gt;
+
+This module logs kernel printk messages over UDP allowing debugging of
+problem where disk logging fails and serial consoles are impractical.
+
+It can be used either built-in or as a module. As a built-in,
+netconsole initializes immediately after NIC cards and will bring up
+the specified interface as soon as possible. While this doesn't allow
+capture of early kernel panics, it does capture most of the boot
+process.
+
+It takes a string configuration parameter "netconsole" in the
+following format:
+
+ netconsole=[src-port]@[src-ip]/[&lt;dev&gt;],[tgt-port]@&lt;tgt-ip&gt;/[tgt-macaddr]
+
+   where
+        src-port      source for UDP packets (defaults to 6665)
+        src-ip        source IP to use (interface address)
+        dev           network interface (eth0)
+        tgt-port      port for logging agent (6666)
+        tgt-ip        IP address for logging agent
+        tgt-macaddr   ethernet MAC address for logging agent (broadcast)
+
+Examples:
+
+ linux netconsole=4444@10.0.0.1/eth1,9353@10.0.0.2/12:34:56:78:9a:bc
+
+  or
+
+ insmod netconsole netconsole=@/,@10.0.0.2/
+
+Built-in netconsole starts immediately after the TCP stack is
+initialized and attempts to bring up the supplied dev at the supplied
+address.
+
+The remote host can run either 'netcat -u -l -p &lt;port&gt;' or syslogd.
+
+WARNING: the default target ethernet setting uses the broadcast
+ethernet address to send packets, which can cause increased load on
+other systems on the same ethernet segment.
+
+NOTE: the network device (eth1 in the above case) can run any kind
+of other network traffic, netconsole is not intrusive. Netconsole
+might cause slight delays in other traffic if the volume of kernel
+messages is high, but should have no other impact.
+
+Netconsole was designed to be as instantaneous as possible, to
+enable the logging of even the most critical kernel bugs. It works
+from IRQ contexts as well, and does not enable interrupts while
+sending packets. Due to these unique needs, configuration can not
+be more automatic, and some fundamental limitations will remain:
+only IP networks, UDP packets and ethernet devices are supported.
diff -Nru a/drivers/net/3c503.c b/drivers/net/3c503.c
--- a/drivers/net/3c503.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/3c503.c	Tue Mar  2 17:57:45 2004
@@ -337,6 +337,9 @@
     dev-&gt;open = &amp;el2_open;
     dev-&gt;stop = &amp;el2_close;
     dev-&gt;ethtool_ops = &amp;netdev_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev-&gt;poll_controller = ei_poll;
+#endif
 
     if (dev-&gt;mem_start)
 	printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
diff -Nru a/drivers/net/3c59x.c b/drivers/net/3c59x.c
--- a/drivers/net/3c59x.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/3c59x.c	Tue Mar  2 17:57:45 2004
@@ -927,6 +927,18 @@
 
 static int vortex_cards_found;
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void poll_vortex(struct net_device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev-&gt;priv;
+	unsigned long flags;
+	local_save_flags(flags);
+	local_irq_disable();
+	(vp-&gt;full_bus_master_rx ? boomerang_interrupt:vortex_interrupt)(dev-&gt;irq,dev,NULL);
+	local_irq_restore(flags);
+} 
+#endif
+
 #ifdef CONFIG_PM
 
 static int vortex_suspend (struct pci_dev *pdev, u32 state)
@@ -1463,6 +1475,9 @@
 	dev-&gt;set_multicast_list = set_rx_mode;
 	dev-&gt;tx_timeout = vortex_tx_timeout;
 	dev-&gt;watchdog_timeo = (watchdog * HZ) / 1000;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = poll_vortex; 
+#endif
 	if (pdev &amp;&amp; vp-&gt;enable_wol) {
 		vp-&gt;pm_state_valid = 1;
  		pci_save_state(VORTEX_PCI(vp), vp-&gt;power_state);
diff -Nru a/drivers/net/8390.c b/drivers/net/8390.c
--- a/drivers/net/8390.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/8390.c	Tue Mar  2 17:57:45 2004
@@ -516,6 +516,15 @@
 	return IRQ_RETVAL(nr_serviced &gt; 0);
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void ei_poll(struct net_device *dev)
+{
+	disable_irq(dev-&gt;irq);
+	ei_interrupt(dev-&gt;irq, dev, NULL);
+	enable_irq(dev-&gt;irq);
+}
+#endif
+
 /**
  * ei_tx_err - handle transmitter error
  * @dev: network device which threw the exception
@@ -1124,6 +1133,9 @@
 EXPORT_SYMBOL(ei_open);
 EXPORT_SYMBOL(ei_close);
 EXPORT_SYMBOL(ei_interrupt);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+EXPORT_SYMBOL(ei_poll);
+#endif
 EXPORT_SYMBOL(ei_tx_timeout);
 EXPORT_SYMBOL(NS8390_init);
 EXPORT_SYMBOL(__alloc_ei_netdev);
diff -Nru a/drivers/net/8390.h b/drivers/net/8390.h
--- a/drivers/net/8390.h	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/8390.h	Tue Mar  2 17:57:45 2004
@@ -39,6 +39,10 @@
 #define ei_debug 1
 #endif
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+extern void ei_poll(struct net_device *dev);
+#endif
+
 extern void NS8390_init(struct net_device *dev, int startp);
 extern int ei_open(struct net_device *dev);
 extern int ei_close(struct net_device *dev);
diff -Nru a/drivers/net/Kconfig b/drivers/net/Kconfig
--- a/drivers/net/Kconfig	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/Kconfig	Tue Mar  2 17:57:45 2004
@@ -2495,6 +2495,13 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called shaper.  If unsure, say N.
 
+config NETCONSOLE
+	tristate "Network console logging support (EXPERIMENTAL)"
+	depends on NETDEVICES &amp;&amp; EXPERIMENTAL
+	---help---
+	If you want to log kernel messages over the network, enable this.
+	See Documentation/networking/netconsole.txt for details.
+
 source "drivers/net/wan/Kconfig"
 
 source "drivers/net/pcmcia/Kconfig"
diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/Makefile	Tue Mar  2 17:57:45 2004
@@ -188,3 +188,4 @@
 obj-$(CONFIG_HAMRADIO) += hamradio/
 obj-$(CONFIG_IRDA) += irda/
 
+obj-$(CONFIG_NETCONSOLE) += netconsole.o
diff -Nru a/drivers/net/ac3200.c b/drivers/net/ac3200.c
--- a/drivers/net/ac3200.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/ac3200.c	Tue Mar  2 17:57:45 2004
@@ -276,6 +276,9 @@
 
 	dev-&gt;open = &amp;ac_open;
 	dev-&gt;stop = &amp;ac_close_card;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 out1:
diff -Nru a/drivers/net/acenic.c b/drivers/net/acenic.c
--- a/drivers/net/acenic.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/acenic.c	Tue Mar  2 17:57:45 2004
@@ -131,7 +131,6 @@
 #define PCI_DEVICE_ID_SGI_ACENIC	0x0009
 #endif
 
-#if LINUX_VERSION_CODE &gt;= 0x20400
 static struct pci_device_id acenic_pci_tbl[] = {
 	{ PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE,
 	  PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET &lt;&lt; 8, 0xffff00, },
@@ -156,37 +155,6 @@
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, acenic_pci_tbl);
-#endif
-
-
-#ifndef MODULE_LICENSE
-#define MODULE_LICENSE(a)
-#endif
-
-#ifndef wmb
-#define wmb()	mb()
-#endif
-
-#ifndef __exit
-#define __exit
-#endif
-
-#ifndef __devinit
-#define __devinit	__init
-#endif
-
-#ifndef SMP_CACHE_BYTES
-#define SMP_CACHE_BYTES	L1_CACHE_BYTES
-#endif
-
-#ifndef SET_MODULE_OWNER
-#define SET_MODULE_OWNER(dev)		do{} while(0)
-#define ACE_MOD_INC_USE_COUNT		MOD_INC_USE_COUNT
-#define ACE_MOD_DEC_USE_COUNT		MOD_DEC_USE_COUNT
-#else
-#define ACE_MOD_INC_USE_COUNT		do{} while(0)
-#define ACE_MOD_DEC_USE_COUNT		do{} while(0)
-#endif
 
 #ifndef SET_NETDEV_DEV
 #define SET_NETDEV_DEV(net, pdev)	do{} while(0)
@@ -198,151 +166,8 @@
 #define ace_sync_irq(irq)	synchronize_irq()
 #endif
 
-#if LINUX_VERSION_CODE &lt; 0x2051e
-#define local_irq_save(flags)		do{__save_flags(flags) ; \
-					   __cli();} while(0)
-#define local_irq_restore(flags)	__restore_flags(flags)
-#endif
-
-#if (LINUX_VERSION_CODE &lt; 0x02030d)
-#define pci_resource_start(dev, bar)	dev-&gt;base_address[bar]
-#elif (LINUX_VERSION_CODE &lt; 0x02032c)
-#define pci_resource_start(dev, bar)	dev-&gt;resource[bar].start
-#endif
-
-#if (LINUX_VERSION_CODE &lt; 0x02030e)
-#define net_device device
-#endif
-
-
-#if (LINUX_VERSION_CODE &lt; 0x02032a)
-typedef u32 dma_addr_t;
-
-static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
-					 dma_addr_t *dma_handle)
-{
-	void *virt_ptr;
-
-	virt_ptr = kmalloc(size, GFP_KERNEL);
-	if (!virt_ptr)
-		return NULL;
-	*dma_handle = virt_to_bus(virt_ptr);
-	return virt_ptr;
-}
-
-#define pci_free_consistent(cookie, size, ptr, dma_ptr)	kfree(ptr)
-#define pci_map_page(cookie, page, off, size, dir)	\
-	virt_to_bus(page_address(page)+(off))
-#define pci_unmap_page(cookie, address, size, dir)
-#define pci_set_dma_mask(dev, mask)		\
-	(((u64)(mask) &amp; 0xffffffff00000000) == 0 ? 0 : -EIO)
-#define pci_dma_supported(dev, mask)		\
-	(((u64)(mask) &amp; 0xffffffff00000000) == 0 ? 1 : 0)
-
-#elif (LINUX_VERSION_CODE &lt; 0x02040d)
-
-/*
- * 2.4.13 introduced pci_map_page()/pci_unmap_page() - for 2.4.12 and prior,
- * fall back on pci_map_single()/pci_unnmap_single().
- *
- * We are guaranteed that the page is mapped at this point since
- * pci_map_page() is only used upon valid struct skb's.
- */
-static inline dma_addr_t
-pci_map_page(struct pci_dev *cookie, struct page *page, unsigned long off,
-	     size_t size, int dir)
-{
-	void *page_virt;
-
-	page_virt = page_address(page);
-	if (!page_virt)
-		BUG();
-	return pci_map_single(cookie, (page_virt + off), size, dir);
-}
-#define pci_unmap_page(cookie, dma_addr, size, dir)	\
-	pci_unmap_single(cookie, dma_addr, size, dir)
-#endif
-
-#if (LINUX_VERSION_CODE &lt; 0x020412)
-#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
-#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
-#define pci_unmap_addr(PTR, ADDR_NAME)		0
-#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do{} while(0)
-#define pci_unmap_len(PTR, LEN_NAME)		0
-#define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do{} while(0)
-#endif
-
-
-#if (LINUX_VERSION_CODE &lt; 0x02032b)
-/*
- * SoftNet
- *
- * For pre-softnet kernels we need to tell the upper layer not to
- * re-enter start_xmit() while we are in there. However softnet
- * guarantees not to enter while we are in there so there is no need
- * to do the netif_stop_queue() dance unless the transmit queue really
- * gets stuck. This should also improve performance according to tests
- * done by Aman Singla.
- */
-#define dev_kfree_skb_irq(a)			dev_kfree_skb(a)
-#define netif_wake_queue(dev)			clear_bit(0, &amp;dev-&gt;tbusy)
-#define netif_stop_queue(dev)			set_bit(0, &amp;dev-&gt;tbusy)
-#define late_stop_netif_stop_queue(dev)		do{} while(0)
-#define early_stop_netif_stop_queue(dev)	test_and_set_bit(0,&amp;dev-&gt;tbusy)
-#define early_stop_netif_wake_queue(dev)	netif_wake_queue(dev)
-
-static inline void netif_start_queue(struct net_device *dev)
-{
-	dev-&gt;tbusy = 0;
-	dev-&gt;interrupt = 0;
-	dev-&gt;start = 1;
-}
-
-#define ace_mark_net_bh()			mark_bh(NET_BH)
-#define netif_queue_stopped(dev)		dev-&gt;tbusy
-#define netif_running(dev)			dev-&gt;start
-#define ace_if_down(dev)			do{dev-&gt;start = 0;} while(0)
-
-#define tasklet_struct				tq_struct
-static inline void tasklet_schedule(struct tasklet_struct *tasklet)
-{
-	queue_task(tasklet, &amp;tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-}
-
-static inline void tasklet_init(struct tasklet_struct *tasklet,
-				void (*func)(unsigned long),
-				unsigned long data)
-{
-	tasklet-&gt;next = NULL;
-	tasklet-&gt;sync = 0;
-	tasklet-&gt;routine = (void (*)(void *))func;
-	tasklet-&gt;data = (void *)data;
-}
-#define tasklet_kill(tasklet)			do{} while(0)
-#else
-#define late_stop_netif_stop_queue(dev)		netif_stop_queue(dev)
-#define early_stop_netif_stop_queue(dev)	0
-#define early_stop_netif_wake_queue(dev)	do{} while(0)
-#define ace_mark_net_bh()			do{} while(0)
-#define ace_if_down(dev)			do{} while(0)
-#endif
-
-#if (LINUX_VERSION_CODE &gt;= 0x02031b)
-#define NEW_NETINIT
-#define ACE_PROBE_ARG				void
-#else
-#define ACE_PROBE_ARG				struct net_device *dev
-#endif
-
-#ifndef min_t
-#define min_t(type,a,b)	(((a)&lt;(b))?(a):(b))
-#endif
-
-#ifndef ARCH_HAS_PREFETCHW
-#ifndef prefetchw
-#define prefetchw(x)				do{} while(0)
-#endif
+#ifndef offset_in_page
+#define offset_in_page(ptr)	((unsigned long)(ptr) &amp; ~PAGE_MASK)
 #endif
 
 #define ACE_MAX_MOD_PARMS	8
@@ -595,407 +420,323 @@
 static int tx_ratio[ACE_MAX_MOD_PARMS];
 static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
 
+MODULE_AUTHOR("Jes Sorensen &lt;jes@trained-monkey.org&gt;");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver");
+MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(tx_ratio, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM_DESC(link, "AceNIC/3C985/NetGear link state");
+MODULE_PARM_DESC(trace, "AceNIC/3C985/NetGear firmware trace level");
+MODULE_PARM_DESC(tx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first tx descriptor arrives");
+MODULE_PARM_DESC(max_tx_desc, "AceNIC/3C985/GA620 max number of transmit descriptors to wait");
+MODULE_PARM_DESC(rx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first rx descriptor arrives");
+MODULE_PARM_DESC(max_rx_desc, "AceNIC/3C985/GA620 max number of receive descriptors to wait");
+MODULE_PARM_DESC(tx_ratio, "AceNIC/3C985/GA620 ratio of NIC memory used for TX/RX descriptors (range 0-63)");
+
+
 static char version[] __initdata = 
   "acenic.c: v0.92 08/05/2002  Jes Sorensen, linux-acenic@SunSITE.dk\n"
   "                            http://home.cern.ch/~jes/gige/acenic.html\n";
 
-static struct net_device *root_dev;
-
-static int probed __initdata = 0;
-
-
-int __devinit acenic_probe (ACE_PROBE_ARG)
+static int __devinit acenic_probe_one(struct pci_dev *pdev,
+		const struct pci_device_id *id)
 {
-#ifdef NEW_NETINIT
 	struct net_device *dev;
-#endif
 	struct ace_private *ap;
-	struct pci_dev *pdev = NULL;
-	int boards_found = 0;
-	int version_disp;
-
-	if (probed)
-		return -ENODEV;
-	probed++;
-
-	version_disp = 0;
-
-	while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET&lt;&lt;8, pdev))) {
-
-		if (!((pdev-&gt;vendor == PCI_VENDOR_ID_ALTEON) &amp;&amp;
-		      ((pdev-&gt;device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) ||
-		       (pdev-&gt;device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&amp;&amp;
-		    !((pdev-&gt;vendor == PCI_VENDOR_ID_3COM) &amp;&amp;
-		      (pdev-&gt;device == PCI_DEVICE_ID_3COM_3C985)) &amp;&amp;
-		    !((pdev-&gt;vendor == PCI_VENDOR_ID_NETGEAR) &amp;&amp;
-		      ((pdev-&gt;device == PCI_DEVICE_ID_NETGEAR_GA620) || 
-		       (pdev-&gt;device == PCI_DEVICE_ID_NETGEAR_GA620T))) &amp;&amp;
-		/*
-		 * Farallon used the DEC vendor ID on their cards by
-		 * mistake for a while
-		 */
-		    !((pdev-&gt;vendor == PCI_VENDOR_ID_DEC) &amp;&amp;
-		      (pdev-&gt;device == PCI_DEVICE_ID_FARALLON_PN9000SX)) &amp;&amp;
-		    !((pdev-&gt;vendor == PCI_VENDOR_ID_ALTEON) &amp;&amp;
-		      (pdev-&gt;device == PCI_DEVICE_ID_FARALLON_PN9100T)) &amp;&amp;
-		    !((pdev-&gt;vendor == PCI_VENDOR_ID_SGI) &amp;&amp;
-		      (pdev-&gt;device == PCI_DEVICE_ID_SGI_ACENIC)))
-			continue;
-
-		dev = alloc_etherdev(sizeof(struct ace_private));
-		if (dev == NULL) {
-			printk(KERN_ERR "acenic: Unable to allocate "
-			       "net_device structure!\n");
-			break;
-		}
+	static int boards_found;
 
-		SET_MODULE_OWNER(dev);
-		SET_NETDEV_DEV(dev, &amp;pdev-&gt;dev);
+	dev = alloc_etherdev(sizeof(struct ace_private));
+	if (dev == NULL) {
+		printk(KERN_ERR "acenic: Unable to allocate "
+		       "net_device structure!\n");
+		return -ENOMEM;
+	}
+
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &amp;pdev-&gt;dev);
 
-		ap = dev-&gt;priv;
-		ap-&gt;pdev = pdev;
+	ap = dev-&gt;priv;
+	ap-&gt;pdev = pdev;
 
-		dev-&gt;open = &amp;ace_open;
-		dev-&gt;hard_start_xmit = &amp;ace_start_xmit;
-		dev-&gt;features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+	dev-&gt;features |= NETIF_F_SG | NETIF_F_IP_CSUM;
 #if ACENIC_DO_VLAN
-		dev-&gt;features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-		dev-&gt;vlan_rx_register = ace_vlan_rx_register;
-		dev-&gt;vlan_rx_kill_vid = ace_vlan_rx_kill_vid;
-#endif
-		if (1) {
-			static void ace_watchdog(struct net_device *dev);
-			dev-&gt;tx_timeout = &amp;ace_watchdog;
-			dev-&gt;watchdog_timeo = 5*HZ;
-		}
-		dev-&gt;stop = &amp;ace_close;
-		dev-&gt;get_stats = &amp;ace_get_stats;
-		dev-&gt;set_multicast_list = &amp;ace_set_multicast_list;
-		dev-&gt;do_ioctl = &amp;ace_ioctl;
-		dev-&gt;set_mac_address = &amp;ace_set_mac_addr;
-		dev-&gt;change_mtu = &amp;ace_change_mtu;
+	dev-&gt;features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+	dev-&gt;vlan_rx_register = ace_vlan_rx_register;
+	dev-&gt;vlan_rx_kill_vid = ace_vlan_rx_kill_vid;
+#endif
+	if (1) {
+		static void ace_watchdog(struct net_device *dev);
+		dev-&gt;tx_timeout = &amp;ace_watchdog;
+		dev-&gt;watchdog_timeo = 5*HZ;
+	}
 
-		/* display version info if adapter is found */
-		if (!version_disp)
-		{
-			/* set display flag to TRUE so that */
-			/* we only display this string ONCE */
-			version_disp = 1;
-			printk(version);
-		}
+	dev-&gt;open = &amp;ace_open;
+	dev-&gt;stop = &amp;ace_close;
+	dev-&gt;hard_start_xmit = &amp;ace_start_xmit;
+	dev-&gt;get_stats = &amp;ace_get_stats;
+	dev-&gt;set_multicast_list = &amp;ace_set_multicast_list;
+	dev-&gt;do_ioctl = &amp;ace_ioctl;
+	dev-&gt;set_mac_address = &amp;ace_set_mac_addr;
+	dev-&gt;change_mtu = &amp;ace_change_mtu;
 
-		if (pci_enable_device(pdev)) {
-			free_netdev(dev);
-			continue;
-		}
+	/* we only display this string ONCE */
+	if (!boards_found)
+		printk(version);
 
-		/*
-		 * Enable master mode before we start playing with the
-		 * pci_command word since pci_set_master() will modify
-		 * it.
-		 */
-		pci_set_master(pdev);
+	if (pci_enable_device(pdev))
+		goto fail_free_netdev;
 
-		pci_read_config_word(pdev, PCI_COMMAND, &amp;ap-&gt;pci_command);
+	/*
+	 * Enable master mode before we start playing with the
+	 * pci_command word since pci_set_master() will modify
+	 * it.
+	 */
+	pci_set_master(pdev);
 
-		/* OpenFirmware on Mac's does not set this - DOH.. */ 
-		if (!(ap-&gt;pci_command &amp; PCI_COMMAND_MEMORY)) {
-			printk(KERN_INFO "%s: Enabling PCI Memory Mapped "
-			       "access - was not enabled by BIOS/Firmware\n",
-			       dev-&gt;name);
-			ap-&gt;pci_command = ap-&gt;pci_command | PCI_COMMAND_MEMORY;
-			pci_write_config_word(ap-&gt;pdev, PCI_COMMAND,
-					      ap-&gt;pci_command);
-			wmb();
-		}
+	pci_read_config_word(pdev, PCI_COMMAND, &amp;ap-&gt;pci_command);
 
-		pci_read_config_byte(pdev, PCI_LATENCY_TIMER,
-				     &amp;ap-&gt;pci_latency);
-		if (ap-&gt;pci_latency &lt;= 0x40) {
-			ap-&gt;pci_latency = 0x40;
-			pci_write_config_byte(pdev, PCI_LATENCY_TIMER,
-					      ap-&gt;pci_latency);
-		}
+	/* OpenFirmware on Mac's does not set this - DOH.. */ 
+	if (!(ap-&gt;pci_command &amp; PCI_COMMAND_MEMORY)) {
+		printk(KERN_INFO "%s: Enabling PCI Memory Mapped "
+		       "access - was not enabled by BIOS/Firmware\n",
+		       dev-&gt;name);
+		ap-&gt;pci_command = ap-&gt;pci_command | PCI_COMMAND_MEMORY;
+		pci_write_config_word(ap-&gt;pdev, PCI_COMMAND,
+				      ap-&gt;pci_command);
+		wmb();
+	}
 
-		/*
-		 * Remap the regs into kernel space - this is abuse of
-		 * dev-&gt;base_addr since it was means for I/O port
-		 * addresses but who gives a damn.
-		 */
-		dev-&gt;base_addr = pci_resource_start(pdev, 0);
-		ap-&gt;regs = (struct ace_regs *)ioremap(dev-&gt;base_addr, 0x4000);
-		if (!ap-&gt;regs) {
-			printk(KERN_ERR "%s:  Unable to map I/O register, "
-			       "AceNIC %i will be disabled.\n",
-			       dev-&gt;name, boards_found);
-			break;
-		}
+	pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &amp;ap-&gt;pci_latency);
+	if (ap-&gt;pci_latency &lt;= 0x40) {
+		ap-&gt;pci_latency = 0x40;
+		pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ap-&gt;pci_latency);
+	}
 
-		switch(pdev-&gt;vendor) {
-		case PCI_VENDOR_ID_ALTEON:
-			if (pdev-&gt;device == PCI_DEVICE_ID_FARALLON_PN9100T) {
-				strncpy(ap-&gt;name, "Farallon PN9100-T "
-					"Gigabit Ethernet", sizeof (ap-&gt;name));
-				printk(KERN_INFO "%s: Farallon PN9100-T ",
-				       dev-&gt;name);
-			} else {
-				strncpy(ap-&gt;name, "AceNIC Gigabit Ethernet",
-					sizeof (ap-&gt;name));
-				printk(KERN_INFO "%s: Alteon AceNIC ",
-				       dev-&gt;name);
-			}
-			break;
-		case PCI_VENDOR_ID_3COM:
-			strncpy(ap-&gt;name, "3Com 3C985 Gigabit Ethernet",
-				sizeof (ap-&gt;name));
-			printk(KERN_INFO "%s: 3Com 3C985 ", dev-&gt;name);
-			break;
-		case PCI_VENDOR_ID_NETGEAR:
-			strncpy(ap-&gt;name, "NetGear GA620 Gigabit Ethernet",
-				sizeof (ap-&gt;name));
-			printk(KERN_INFO "%s: NetGear GA620 ", dev-&gt;name);
-			break;
-		case PCI_VENDOR_ID_DEC:
-			if (pdev-&gt;device == PCI_DEVICE_ID_FARALLON_PN9000SX) {
-				strncpy(ap-&gt;name, "Farallon PN9000-SX "
-					"Gigabit Ethernet", sizeof (ap-&gt;name));
-				printk(KERN_INFO "%s: Farallon PN9000-SX ",
-				       dev-&gt;name);
-				break;
-			}
-		case PCI_VENDOR_ID_SGI:
-			strncpy(ap-&gt;name, "SGI AceNIC Gigabit Ethernet",
+	/*
+	 * Remap the regs into kernel space - this is abuse of
+	 * dev-&gt;base_addr since it was means for I/O port
+	 * addresses but who gives a damn.
+	 */
+	dev-&gt;base_addr = pci_resource_start(pdev, 0);
+	ap-&gt;regs = (struct ace_regs *)ioremap(dev-&gt;base_addr, 0x4000);
+	if (!ap-&gt;regs) {
+		printk(KERN_ERR "%s:  Unable to map I/O register, "
+		       "AceNIC %i will be disabled.\n",
+		       dev-&gt;name, boards_found);
+		goto fail_free_netdev;
+	}
+
+	switch(pdev-&gt;vendor) {
+	case PCI_VENDOR_ID_ALTEON:
+		if (pdev-&gt;device == PCI_DEVICE_ID_FARALLON_PN9100T) {
+			strncpy(ap-&gt;name, "Farallon PN9100-T "
+				"Gigabit Ethernet", sizeof (ap-&gt;name));
+			printk(KERN_INFO "%s: Farallon PN9100-T ",
+			       dev-&gt;name);
+		} else {
+			strncpy(ap-&gt;name, "AceNIC Gigabit Ethernet",
 				sizeof (ap-&gt;name));
-			printk(KERN_INFO "%s: SGI AceNIC ", dev-&gt;name);
-			break;
-		default:
- 			strncpy(ap-&gt;name, "Unknown AceNIC based Gigabit "
-				"Ethernet", sizeof (ap-&gt;name));
-			printk(KERN_INFO "%s: Unknown AceNIC ", dev-&gt;name);
+			printk(KERN_INFO "%s: Alteon AceNIC ",
+			       dev-&gt;name);
+		}
+		break;
+	case PCI_VENDOR_ID_3COM:
+		strncpy(ap-&gt;name, "3Com 3C985 Gigabit Ethernet",
+			sizeof (ap-&gt;name));
+		printk(KERN_INFO "%s: 3Com 3C985 ", dev-&gt;name);
+		break;
+	case PCI_VENDOR_ID_NETGEAR:
+		strncpy(ap-&gt;name, "NetGear GA620 Gigabit Ethernet",
+			sizeof (ap-&gt;name));
+		printk(KERN_INFO "%s: NetGear GA620 ", dev-&gt;name);
+		break;
+	case PCI_VENDOR_ID_DEC:
+		if (pdev-&gt;device == PCI_DEVICE_ID_FARALLON_PN9000SX) {
+			strncpy(ap-&gt;name, "Farallon PN9000-SX "
+				"Gigabit Ethernet", sizeof (ap-&gt;name));
+			printk(KERN_INFO "%s: Farallon PN9000-SX ",
+			       dev-&gt;name);
 			break;
 		}
-		ap-&gt;name [sizeof (ap-&gt;name) - 1] = '\0';
-		printk("Gigabit Ethernet at 0x%08lx, ", dev-&gt;base_addr);
+	case PCI_VENDOR_ID_SGI:
+		strncpy(ap-&gt;name, "SGI AceNIC Gigabit Ethernet",
+			sizeof (ap-&gt;name));
+		printk(KERN_INFO "%s: SGI AceNIC ", dev-&gt;name);
+		break;
+	default:
+ 		strncpy(ap-&gt;name, "Unknown AceNIC based Gigabit "
+			"Ethernet", sizeof (ap-&gt;name));
+		printk(KERN_INFO "%s: Unknown AceNIC ", dev-&gt;name);
+		break;
+	}
+
+	ap-&gt;name [sizeof (ap-&gt;name) - 1] = '\0';
+	printk("Gigabit Ethernet at 0x%08lx, ", dev-&gt;base_addr);
 #ifdef __sparc__
-		printk("irq %s\n", __irq_itoa(pdev-&gt;irq));
+	printk("irq %s\n", __irq_itoa(pdev-&gt;irq));
 #else
-		printk("irq %i\n", pdev-&gt;irq);
+	printk("irq %i\n", pdev-&gt;irq);
 #endif
 
 #ifdef CONFIG_ACENIC_OMIT_TIGON_I
-		if ((readl(&amp;ap-&gt;regs-&gt;HostCtrl) &gt;&gt; 28) == 4) {
-			printk(KERN_ERR "%s: Driver compiled without Tigon I"
-			       " support - NIC disabled\n", dev-&gt;name);
-			ace_init_cleanup(dev);
-			free_netdev(dev);
-			continue;
-		}
+	if ((readl(&amp;ap-&gt;regs-&gt;HostCtrl) &gt;&gt; 28) == 4) {
+		printk(KERN_ERR "%s: Driver compiled without Tigon I"
+		       " support - NIC disabled\n", dev-&gt;name);
+		goto fail_uninit;
+	}
 #endif
 
-		if (ace_allocate_descriptors(dev)) {
-			/*
-			 * ace_allocate_descriptors() calls
-			 * ace_init_cleanup() on error.
-			 */
-			free_netdev(dev);
-			continue;
-		}
+	if (ace_allocate_descriptors(dev))
+		goto fail_free_netdev;
 
 #ifdef MODULE
-		if (boards_found &gt;= ACE_MAX_MOD_PARMS)
-			ap-&gt;board_idx = BOARD_IDX_OVERFLOW;
-		else
-			ap-&gt;board_idx = boards_found;
+	if (boards_found &gt;= ACE_MAX_MOD_PARMS)
+		ap-&gt;board_idx = BOARD_IDX_OVERFLOW;
+	else
+		ap-&gt;board_idx = boards_found;
 #else
-		ap-&gt;board_idx = BOARD_IDX_STATIC;
+	ap-&gt;board_idx = BOARD_IDX_STATIC;
 #endif
 
-		if (ace_init(dev)) {
-			/*
-			 * ace_init() calls ace_init_cleanup() on error.
-			 */
-			free_netdev(dev);
-			continue;
-		}
+	if (ace_init(dev))
+		goto fail_free_netdev;
 
-		if (register_netdev(dev)) {
-			printk(KERN_ERR "acenic: device registration failed\n");
-			ace_init_cleanup(dev);
-			free_netdev(dev);
-			continue;
-		}
-
-		if (ap-&gt;pci_using_dac)
-			dev-&gt;features |= NETIF_F_HIGHDMA;
-
-		boards_found++;
+	if (register_netdev(dev)) {
+		printk(KERN_ERR "acenic: device registration failed\n");
+		goto fail_uninit;
 	}
 
-	/*
-	 * If we're at this point we're going through ace_probe() for
-	 * the first time.  Return success (0) if we've initialized 1
-	 * or more boards. Otherwise, return failure (-ENODEV).
-	 */
-
-	if (boards_found &gt; 0)
-		return 0;
-	else
-		return -ENODEV;
-}
+	if (ap-&gt;pci_using_dac)
+		dev-&gt;features |= NETIF_F_HIGHDMA;
 
+	pci_set_drvdata(pdev, dev);
 
-#ifdef MODULE
-MODULE_AUTHOR("Jes Sorensen &lt;jes@trained-monkey.org&gt;");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver");
-MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(tx_ratio, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM_DESC(link, "AceNIC/3C985/NetGear link state");
-MODULE_PARM_DESC(trace, "AceNIC/3C985/NetGear firmware trace level");
-MODULE_PARM_DESC(tx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first tx descriptor arrives");
-MODULE_PARM_DESC(max_tx_desc, "AceNIC/3C985/GA620 max number of transmit descriptors to wait");
-MODULE_PARM_DESC(rx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first rx descriptor arrives");
-MODULE_PARM_DESC(max_rx_desc, "AceNIC/3C985/GA620 max number of receive descriptors to wait");
-MODULE_PARM_DESC(tx_ratio, "AceNIC/3C985/GA620 ratio of NIC memory used for TX/RX descriptors (range 0-63)");
-#endif
+	boards_found++;
+	return 0;
 
+ fail_uninit:
+	ace_init_cleanup(dev);
+ fail_free_netdev:
+	free_netdev(dev);
+	return -ENODEV;
+}
 
-static void __exit ace_module_cleanup(void)
+static void __devexit acenic_remove_one(struct pci_dev *pdev)
 {
-	struct ace_private *ap;
-	struct ace_regs *regs;
-	struct net_device *next;
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct ace_private *ap = dev-&gt;priv;
+	struct ace_regs *regs = ap-&gt;regs;
 	short i;
 
-	while (root_dev) {
-		ap = root_dev-&gt;priv;
-		next = ap-&gt;next;
-		unregister_netdev(root_dev);
-
-		regs = ap-&gt;regs;
+	unregister_netdev(dev);
 
-		writel(readl(&amp;regs-&gt;CpuCtrl) | CPU_HALT, &amp;regs-&gt;CpuCtrl);
-		if (ap-&gt;version &gt;= 2)
-			writel(readl(&amp;regs-&gt;CpuBCtrl) | CPU_HALT,
-			       &amp;regs-&gt;CpuBCtrl);
-		/*
-		 * This clears any pending interrupts
-		 */
-		writel(1, &amp;regs-&gt;Mb0Lo);
-		readl(&amp;regs-&gt;CpuCtrl);	/* flush */
+	writel(readl(&amp;regs-&gt;CpuCtrl) | CPU_HALT, &amp;regs-&gt;CpuCtrl);
+	if (ap-&gt;version &gt;= 2)
+		writel(readl(&amp;regs-&gt;CpuBCtrl) | CPU_HALT, &amp;regs-&gt;CpuBCtrl);
+	
+	/*
+	 * This clears any pending interrupts
+	 */
+	writel(1, &amp;regs-&gt;Mb0Lo);
+	readl(&amp;regs-&gt;CpuCtrl);	/* flush */
 
-		/*
-		 * Make sure no other CPUs are processing interrupts
-		 * on the card before the buffers are being released.
-		 * Otherwise one might experience some `interesting'
-		 * effects.
-		 *
-		 * Then release the RX buffers - jumbo buffers were
-		 * already released in ace_close().
-		 */
-		ace_sync_irq(root_dev-&gt;irq);
+	/*
+	 * Make sure no other CPUs are processing interrupts
+	 * on the card before the buffers are being released.
+	 * Otherwise one might experience some `interesting'
+	 * effects.
+	 *
+	 * Then release the RX buffers - jumbo buffers were
+	 * already released in ace_close().
+	 */
+	ace_sync_irq(dev-&gt;irq);
 
-		for (i = 0; i &lt; RX_STD_RING_ENTRIES; i++) {
-			struct sk_buff *skb = ap-&gt;skb-&gt;rx_std_skbuff[i].skb;
+	for (i = 0; i &lt; RX_STD_RING_ENTRIES; i++) {
+		struct sk_buff *skb = ap-&gt;skb-&gt;rx_std_skbuff[i].skb;
 
-			if (skb) {
-				struct ring_info *ringp;
-				dma_addr_t mapping;
+		if (skb) {
+			struct ring_info *ringp;
+			dma_addr_t mapping;
 
-				ringp = &amp;ap-&gt;skb-&gt;rx_std_skbuff[i];
-				mapping = pci_unmap_addr(ringp, mapping);
-				pci_unmap_page(ap-&gt;pdev, mapping,
-					       ACE_STD_BUFSIZE - (2 + 16),
-					       PCI_DMA_FROMDEVICE);
+			ringp = &amp;ap-&gt;skb-&gt;rx_std_skbuff[i];
+			mapping = pci_unmap_addr(ringp, mapping);
+			pci_unmap_page(ap-&gt;pdev, mapping,
+				       ACE_STD_BUFSIZE - (2 + 16),
+				       PCI_DMA_FROMDEVICE);
 
-				ap-&gt;rx_std_ring[i].size = 0;
-				ap-&gt;skb-&gt;rx_std_skbuff[i].skb = NULL;
-				dev_kfree_skb(skb);
-			}
-		}
-		if (ap-&gt;version &gt;= 2) {
-			for (i = 0; i &lt; RX_MINI_RING_ENTRIES; i++) {
-				struct sk_buff *skb = ap-&gt;skb-&gt;rx_mini_skbuff[i].skb;
-
-				if (skb) {
-					struct ring_info *ringp;
-					dma_addr_t mapping;
-
-					ringp = &amp;ap-&gt;skb-&gt;rx_mini_skbuff[i];
-					mapping = pci_unmap_addr(ringp,mapping);
-					pci_unmap_page(ap-&gt;pdev, mapping,
-						       ACE_MINI_BUFSIZE - (2 + 16),
-						       PCI_DMA_FROMDEVICE);
-
-					ap-&gt;rx_mini_ring[i].size = 0;
-					ap-&gt;skb-&gt;rx_mini_skbuff[i].skb = NULL;
-					dev_kfree_skb(skb);
-				}
-			}
+			ap-&gt;rx_std_ring[i].size = 0;
+			ap-&gt;skb-&gt;rx_std_skbuff[i].skb = NULL;
+			dev_kfree_skb(skb);
 		}
-		for (i = 0; i &lt; RX_JUMBO_RING_ENTRIES; i++) {
-			struct sk_buff *skb = ap-&gt;skb-&gt;rx_jumbo_skbuff[i].skb;
+	}
+
+	if (ap-&gt;version &gt;= 2) {
+		for (i = 0; i &lt; RX_MINI_RING_ENTRIES; i++) {
+			struct sk_buff *skb = ap-&gt;skb-&gt;rx_mini_skbuff[i].skb;
+
 			if (skb) {
 				struct ring_info *ringp;
 				dma_addr_t mapping;
 
-				ringp = &amp;ap-&gt;skb-&gt;rx_jumbo_skbuff[i];
-				mapping = pci_unmap_addr(ringp, mapping);
+				ringp = &amp;ap-&gt;skb-&gt;rx_mini_skbuff[i];
+				mapping = pci_unmap_addr(ringp,mapping);
 				pci_unmap_page(ap-&gt;pdev, mapping,
-					       ACE_JUMBO_BUFSIZE - (2 + 16),
+					       ACE_MINI_BUFSIZE - (2 + 16),
 					       PCI_DMA_FROMDEVICE);
 
-				ap-&gt;rx_jumbo_ring[i].size = 0;
-				ap-&gt;skb-&gt;rx_jumbo_skbuff[i].skb = NULL;
+				ap-&gt;rx_mini_ring[i].size = 0;
+				ap-&gt;skb-&gt;rx_mini_skbuff[i].skb = NULL;
 				dev_kfree_skb(skb);
 			}
 		}
-
-		ace_init_cleanup(root_dev);
-		free_netdev(root_dev);
-		root_dev = next;
 	}
-}
 
+	for (i = 0; i &lt; RX_JUMBO_RING_ENTRIES; i++) {
+		struct sk_buff *skb = ap-&gt;skb-&gt;rx_jumbo_skbuff[i].skb;
+		if (skb) {
+			struct ring_info *ringp;
+			dma_addr_t mapping;
 
-int __init ace_module_init(void)
-{
-	int status;
+			ringp = &amp;ap-&gt;skb-&gt;rx_jumbo_skbuff[i];
+			mapping = pci_unmap_addr(ringp, mapping);
+			pci_unmap_page(ap-&gt;pdev, mapping,
+				       ACE_JUMBO_BUFSIZE - (2 + 16),
+				       PCI_DMA_FROMDEVICE);
 
-	root_dev = NULL;
+			ap-&gt;rx_jumbo_ring[i].size = 0;
+			ap-&gt;skb-&gt;rx_jumbo_skbuff[i].skb = NULL;
+			dev_kfree_skb(skb);
+		}
+	}
 
-#ifdef NEW_NETINIT
-	status = acenic_probe();
-#else
-	status = acenic_probe(NULL);
-#endif
-	return status;
+	ace_init_cleanup(dev);
+	free_netdev(dev);
 }
 
+static struct pci_driver acenic_pci_driver = {
+	.name		= "acenic",
+	.id_table	= acenic_pci_tbl,
+	.probe		= acenic_probe_one,
+	.remove		= __devexit_p(acenic_remove_one),
+};
 
-#if (LINUX_VERSION_CODE &lt; 0x02032a)
-#ifdef MODULE
-int init_module(void)
+static int __init acenic_init(void)
 {
-	return ace_module_init();
+	return pci_module_init(&amp;acenic_pci_driver);
 }
 
-
-void cleanup_module(void)
+static void __exit acenic_exit(void)
 {
-	ace_module_cleanup();
+	pci_unregister_driver(&amp;acenic_pci_driver);
 }
-#endif
-#else
-module_init(ace_module_init);
-module_exit(ace_module_cleanup);
-#endif
 
+module_init(acenic_init);
+module_exit(acenic_exit);
 
 static void ace_free_descriptors(struct net_device *dev)
 {
@@ -1462,13 +1203,6 @@
 	} else
 		dev-&gt;irq = pdev-&gt;irq;
 
-	/*
-	 * Register the device here to be able to catch allocated
-	 * interrupt handlers in case the firmware doesn't come up.
-	 */
-	ap-&gt;next = root_dev;
-	root_dev = dev;
-
 #ifdef INDEX_DEBUG
 	spin_lock_init(&amp;ap-&gt;debug_lock);
 	ap-&gt;last_tx = ACE_TX_RING_ENTRIES(ap) - 1;
@@ -2642,8 +2376,6 @@
 
 	netif_start_queue(dev);
 
-	ACE_MOD_INC_USE_COUNT;
-
 	/*
 	 * Setup the bottom half rx ring refill handler
 	 */
@@ -2660,8 +2392,6 @@
 	unsigned long flags;
 	short i;
 
-	ace_if_down(dev);
-
 	/*
 	 * Without (or before) releasing irq and stopping hardware, this
 	 * is an absolute non-sense, by the way. It will be reset instantly
@@ -2733,7 +2463,6 @@
 	ace_unmask_irq(dev);
 	local_irq_restore(flags);
 
-	ACE_MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -2789,12 +2518,6 @@
 	struct ace_regs *regs = ap-&gt;regs;
 	struct tx_desc *desc;
 	u32 idx, flagsize;
-
- 	/*
-	 * This only happens with pre-softnet, ie. 2.2.x kernels.
- 	 */
-	if (early_stop_netif_stop_queue(dev))
- 		return 1;
 
 restart:
 	idx = ap-&gt;tx_prd;
diff -Nru a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c
--- a/drivers/net/amd8111e.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/amd8111e.c	Tue Mar  2 17:57:45 2004
@@ -1153,6 +1153,17 @@
 	return IRQ_RETVAL(handled);
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void amd8111e_poll(struct net_device *dev)
+{ 
+	unsigned long flags;
+	local_save_flags(flags); 
+	local_irq_disable();
+	amd8111e_interrupt(0, dev, NULL);
+	local_irq_restore(flags); 
+} 
+#endif
+
 /*
 This function closes the network interface and updates the statistics so that most recent statistics will be available after the interface is down.
 */
@@ -1884,6 +1895,9 @@
 	dev-&gt;irq =pdev-&gt;irq;
 	dev-&gt;tx_timeout = amd8111e_tx_timeout; 
 	dev-&gt;watchdog_timeo = AMD8111E_TX_TIMEOUT; 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = amd8111e_poll; 
+#endif
 
 #if AMD8111E_VLAN_TAG_USED
 	dev-&gt;features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
diff -Nru a/drivers/net/apne.c b/drivers/net/apne.c
--- a/drivers/net/apne.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/apne.c	Tue Mar  2 17:57:45 2004
@@ -333,6 +333,9 @@
     ei_status.get_8390_hdr = &amp;apne_get_8390_hdr;
     dev-&gt;open = &amp;apne_open;
     dev-&gt;stop = &amp;apne_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev-&gt;poll_controller = ei_poll;
+#endif
     NS8390_init(dev, 0);
 
     pcmcia_ack_int(pcmcia_get_intreq());		/* ack PCMCIA int req */
diff -Nru a/drivers/net/e2100.c b/drivers/net/e2100.c
--- a/drivers/net/e2100.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/e2100.c	Tue Mar  2 17:57:45 2004
@@ -269,6 +269,9 @@
 	ei_status.get_8390_hdr = &amp;e21_get_8390_hdr;
 	dev-&gt;open = &amp;e21_open;
 	dev-&gt;stop = &amp;e21_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 	return 0;
diff -Nru a/drivers/net/eepro100.c b/drivers/net/eepro100.c
--- a/drivers/net/eepro100.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/eepro100.c	Tue Mar  2 17:57:45 2004
@@ -654,6 +654,23 @@
 	return -ENODEV;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void poll_speedo (struct net_device *dev)
+{
+	/* disable_irq is not very nice, but with the funny lockless design
+	   we have no other choice. */
+	disable_irq(dev-&gt;irq);
+	speedo_interrupt (dev-&gt;irq, dev, NULL);
+	enable_irq(dev-&gt;irq);
+}
+#endif
+
 static int __devinit speedo_found1(struct pci_dev *pdev,
 		long ioaddr, int card_idx, int acpi_idle_state)
 {
@@ -885,6 +902,9 @@
 	dev-&gt;get_stats = &amp;speedo_get_stats;
 	dev-&gt;set_multicast_list = &amp;set_rx_mode;
 	dev-&gt;do_ioctl = &amp;speedo_ioctl;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = &amp;poll_speedo;
+#endif
 
 	if (register_netdevice(dev))
 		goto err_free_unlock;
diff -Nru a/drivers/net/es3210.c b/drivers/net/es3210.c
--- a/drivers/net/es3210.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/es3210.c	Tue Mar  2 17:57:45 2004
@@ -298,6 +298,9 @@
 
 	dev-&gt;open = &amp;es_open;
 	dev-&gt;stop = &amp;es_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 out1:
diff -Nru a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c
--- a/drivers/net/hp-plus.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/hp-plus.c	Tue Mar  2 17:57:45 2004
@@ -236,6 +236,9 @@
 
 	dev-&gt;open = &amp;hpp_open;
 	dev-&gt;stop = &amp;hpp_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 
 	ei_status.name = name;
 	ei_status.word16 = 0;		/* Agggghhhhh! Debug time: 2 days! */
diff -Nru a/drivers/net/hp.c b/drivers/net/hp.c
--- a/drivers/net/hp.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/hp.c	Tue Mar  2 17:57:45 2004
@@ -207,6 +207,9 @@
 	dev-&gt;base_addr = ioaddr + NIC_OFFSET;
 	dev-&gt;open = &amp;hp_open;
 	dev-&gt;stop = &amp;hp_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 
 	ei_status.name = name;
 	ei_status.word16 = wordmode;
diff -Nru a/drivers/net/hydra.c b/drivers/net/hydra.c
--- a/drivers/net/hydra.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/hydra.c	Tue Mar  2 17:57:45 2004
@@ -142,6 +142,10 @@
     ei_status.reg_offset = hydra_offsets;
     dev-&gt;open = &amp;hydra_open;
     dev-&gt;stop = &amp;hydra_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev-&gt;poll_controller = ei_poll;
+#endif
+
     NS8390_init(dev, 0);
 
     err = register_netdev(dev);
diff -Nru a/drivers/net/lne390.c b/drivers/net/lne390.c
--- a/drivers/net/lne390.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/lne390.c	Tue Mar  2 17:57:45 2004
@@ -299,6 +299,9 @@
 
 	dev-&gt;open = &amp;lne390_open;
 	dev-&gt;stop = &amp;lne390_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 cleanup:
diff -Nru a/drivers/net/mac8390.c b/drivers/net/mac8390.c
--- a/drivers/net/mac8390.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/mac8390.c	Tue Mar  2 17:57:45 2004
@@ -442,6 +442,9 @@
 	/* Now fill in our stuff */
 	dev-&gt;open = &amp;mac8390_open;
 	dev-&gt;stop = &amp;mac8390_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 
 	/* GAR, ei_status is actually a macro even though it looks global */
 	ei_status.name = cardname[type];
diff -Nru a/drivers/net/ne.c b/drivers/net/ne.c
--- a/drivers/net/ne.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/ne.c	Tue Mar  2 17:57:45 2004
@@ -498,6 +498,9 @@
 	ei_status.priv = 0;
 	dev-&gt;open = &amp;ne_open;
 	dev-&gt;stop = &amp;ne_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 
diff -Nru a/drivers/net/ne2.c b/drivers/net/ne2.c
--- a/drivers/net/ne2.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/ne2.c	Tue Mar  2 17:57:45 2004
@@ -509,6 +509,9 @@
 	
 	dev-&gt;open = &amp;ne_open;
 	dev-&gt;stop = &amp;ne_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 out:
diff -Nru a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c
--- a/drivers/net/ne2k-pci.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/ne2k-pci.c	Tue Mar  2 17:57:45 2004
@@ -359,6 +359,9 @@
 	dev-&gt;open = &amp;ne2k_pci_open;
 	dev-&gt;stop = &amp;ne2k_pci_close;
 	dev-&gt;ethtool_ops = &amp;ne2k_pci_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 	i = register_netdev(dev);
diff -Nru a/drivers/net/ne2k_cbus.c b/drivers/net/ne2k_cbus.c
--- a/drivers/net/ne2k_cbus.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/ne2k_cbus.c	Tue Mar  2 17:57:45 2004
@@ -534,6 +534,9 @@
 	ei_status.priv = 0;
 	dev-&gt;open = &amp;ne_open;
 	dev-&gt;stop = &amp;ne_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 
diff -Nru a/drivers/net/ne3210.c b/drivers/net/ne3210.c
--- a/drivers/net/ne3210.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/ne3210.c	Tue Mar  2 17:57:45 2004
@@ -205,6 +205,9 @@
 
 	dev-&gt;open = &amp;ne3210_open;
 	dev-&gt;stop = &amp;ne3210_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	dev-&gt;if_port = ifmap_val[port_index];
 
 	if ((retval = register_netdev (dev)))
diff -Nru a/drivers/net/netconsole.c b/drivers/net/netconsole.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/net/netconsole.c	Tue Mar  2 17:57:45 2004
@@ -0,0 +1,127 @@
+/*
+ *  linux/drivers/net/netconsole.c
+ *
+ *  Copyright (C) 2001  Ingo Molnar &lt;mingo@redhat.com&gt;
+ *
+ *  This file contains the implementation of an IRQ-safe, crash-safe
+ *  kernel console implementation that outputs kernel messages to the
+ *  network.
+ *
+ * Modification history:
+ *
+ * 2001-09-17    started by Ingo Molnar.
+ * 2003-08-11    2.6 port by Matt Mackall
+ *               simplified options
+ *               generic card hooks
+ *               works non-modular
+ * 2003-09-07    rewritten with netpoll api
+ */
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include &lt;linux/mm.h&gt;
+#include &lt;linux/tty.h&gt;
+#include &lt;linux/init.h&gt;
+#include &lt;linux/module.h&gt;
+#include &lt;linux/console.h&gt;
+#include &lt;linux/tty_driver.h&gt;
+#include &lt;linux/module.h&gt;
+#include &lt;linux/moduleparam.h&gt;
+#include &lt;linux/string.h&gt;
+#include &lt;linux/sysrq.h&gt;
+#include &lt;linux/smp.h&gt;
+#include &lt;linux/netpoll.h&gt;
+
+MODULE_AUTHOR("Maintainer: Matt Mackall &lt;mpm@selenic.com&gt;");
+MODULE_DESCRIPTION("Console driver for network interfaces");
+MODULE_LICENSE("GPL");
+
+static char config[256];
+module_param_string(netconsole, config, 256, 0);
+MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@&lt;tgt-ip&gt;/[tgt-macaddr]\n");
+
+static struct netpoll np = {
+	.name = "netconsole",
+	.dev_name = "eth0",
+	.local_port = 6665,
+	.remote_port = 6666,
+	.remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+};
+static int configured = 0;
+
+#define MAX_PRINT_CHUNK 1000
+
+static void write_msg(struct console *con, const char *msg, unsigned int len)
+{
+	int frag, left;
+	unsigned long flags;
+
+	if (!np.dev)
+		return;
+
+	local_irq_save(flags);
+
+	for(left = len; left; ) {
+		frag = min(left, MAX_PRINT_CHUNK);
+		netpoll_send_udp(&amp;np, msg, frag);
+		msg += frag;
+		left -= frag;
+	}
+
+	local_irq_restore(flags);
+}
+
+static struct console netconsole = {
+	.flags = CON_ENABLED | CON_PRINTBUFFER,
+	.write = write_msg
+};
+
+static int option_setup(char *opt)
+{
+	configured = !netpoll_parse_options(&amp;np, opt);
+	return 0;
+}
+
+__setup("netconsole=", option_setup);
+
+static int init_netconsole(void)
+{
+	if(strlen(config))
+		option_setup(config);
+
+	if(!configured) {
+		printk("netconsole: not configured, aborting\n");
+		return -EINVAL;
+	}
+
+	if(netpoll_setup(&amp;np))
+		return -EINVAL;
+
+	register_console(&amp;netconsole);
+	printk(KERN_INFO "netconsole: network logging started\n");
+	return 0;
+}
+
+static void cleanup_netconsole(void)
+{
+	unregister_console(&amp;netconsole);
+	netpoll_cleanup(&amp;np);
+}
+
+module_init(init_netconsole);
+module_exit(cleanup_netconsole);
diff -Nru a/drivers/net/oaknet.c b/drivers/net/oaknet.c
--- a/drivers/net/oaknet.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/oaknet.c	Tue Mar  2 17:57:45 2004
@@ -192,6 +192,9 @@
 
 	dev-&gt;open = oaknet_open;
 	dev-&gt;stop = oaknet_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 
 	NS8390_init(dev, FALSE);
 	ret = register_netdev(dev);
diff -Nru a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
--- a/drivers/net/pcnet32.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/pcnet32.c	Tue Mar  2 17:57:45 2004
@@ -479,6 +479,14 @@
     .reset	= pcnet32_dwio_reset
 };
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void pcnet32_poll_controller(struct net_device *dev)
+{ 
+	disable_irq(dev-&gt;irq);
+	pcnet32_interrupt(0, dev, NULL);
+	enable_irq(dev-&gt;irq);
+} 
+#endif
 
 
 static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
@@ -1105,6 +1113,10 @@
     dev-&gt;ethtool_ops = &amp;pcnet32_ethtool_ops;
     dev-&gt;tx_timeout = pcnet32_tx_timeout;
     dev-&gt;watchdog_timeo = (5*HZ);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev-&gt;poll_controller = pcnet32_poll_controller;
+#endif    
 
     /* Fill in the generic fields of the device structure. */
     if (register_netdev(dev))
diff -Nru a/drivers/net/r8169.c b/drivers/net/r8169.c
--- a/drivers/net/r8169.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/r8169.c	Tue Mar  2 17:57:45 2004
@@ -56,9 +56,11 @@
 	        printk( "Assertion failed! %s,%s,%s,line=%d\n",	\
         	#expr,__FILE__,__FUNCTION__,__LINE__);		\
         }
+#define dprintk(fmt, args...)	do { printk(PFX fmt, ## args) } while (0)
 #else
 #define assert(expr) do {} while (0)
-#endif
+#define dprintk(fmt, args...)	do {} while (0)
+#endif /* RTL8169_DEBUG */
 
 /* media options */
 #define MAX_UNITS 8
@@ -89,9 +91,12 @@
 #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 R8169_TX_RING_BYTES	(NUM_TX_DESC * sizeof(struct TxDesc))
+#define R8169_RX_RING_BYTES	(NUM_RX_DESC * sizeof(struct RxDesc))
 
 #define RTL_MIN_IO_SIZE 0x80
-#define TX_TIMEOUT  (6*HZ)
+#define RTL8169_TX_TIMEOUT	(6*HZ)
+#define RTL8169_PHY_TIMEOUT	(HZ) 
 
 /* write/read MMIO register */
 #define RTL_W8(reg, val8)	writeb ((val8), ioaddr + (reg))
@@ -101,11 +106,35 @@
 #define RTL_R16(reg)		readw (ioaddr + (reg))
 #define RTL_R32(reg)		((unsigned long) readl (ioaddr + (reg)))
 
-static struct {
+enum mac_version {
+	RTL_GIGA_MAC_VER_B = 0x00,
+	/* RTL_GIGA_MAC_VER_C = 0x03, */
+	RTL_GIGA_MAC_VER_D = 0x01,
+	RTL_GIGA_MAC_VER_E = 0x02
+};
+
+enum phy_version {
+	RTL_GIGA_PHY_VER_C = 0x03, /* PHY Reg 0x03 bit0-3 == 0x0000 */
+	RTL_GIGA_PHY_VER_D = 0x04, /* PHY Reg 0x03 bit0-3 == 0x0000 */
+	RTL_GIGA_PHY_VER_E = 0x05, /* PHY Reg 0x03 bit0-3 == 0x0000 */
+	RTL_GIGA_PHY_VER_F = 0x06, /* PHY Reg 0x03 bit0-3 == 0x0001 */
+	RTL_GIGA_PHY_VER_G = 0x07, /* PHY Reg 0x03 bit0-3 == 0x0002 */
+};
+
+
+#define _R(NAME,MAC,MASK) \
+	{ .name = NAME, .mac_version = MAC, .RxConfigMask = MASK }
+
+const static struct {
 	const char *name;
-} board_info[] __devinitdata = {
-	{
-"RealTek RTL8169 Gigabit Ethernet"},};
+	u8 mac_version;
+	u32 RxConfigMask;	/* Clears the bits supported by this chip */
+} rtl_chip_info[] = {
+	_R("RTL8169",		RTL_GIGA_MAC_VER_B, 0xff7e1880),
+	_R("RTL8169s/8110s",	RTL_GIGA_MAC_VER_D, 0xff7e1880),
+	_R("RTL8169s/8110s",	RTL_GIGA_MAC_VER_E, 0xff7e1880)
+};
+#undef _R
 
 static struct pci_device_id rtl8169_pci_tbl[] = {
 	{0x10ec, 0x8169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -114,6 +143,8 @@
 
 MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);
 
+static int rx_copybreak = 200;
+
 enum RTL8169_registers {
 	MAC0 = 0,		/* Ethernet hardware address. */
 	MAR0 = 8,		/* Multicast filter. */
@@ -242,14 +273,6 @@
 	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,
@@ -257,6 +280,8 @@
 	LSbit = 0x10000000,
 };
 
+#define RsvdMask	0x3fffc000
+
 struct TxDesc {
 	u32 status;
 	u32 vlan_tag;
@@ -277,28 +302,33 @@
 	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 */
+	int mac_version;
+	int phy_version;
+	u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
+	u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
+	u32 dirty_rx;
+	u32 dirty_tx;
 	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 */
+	dma_addr_t TxPhyAddr;
+	dma_addr_t RxPhyAddr;
+	struct sk_buff *Rx_skbuff[NUM_RX_DESC];	/* Rx data buffers */
 	struct sk_buff *Tx_skbuff[NUM_TX_DESC];	/* Index of Transmit data buffer */
+	struct timer_list timer;
+	unsigned long phy_link_down_cnt;
 };
 
 MODULE_AUTHOR("Realtek");
 MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver");
 MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(rx_copybreak, "i");
 MODULE_LICENSE("GPL");
 
 static int rtl8169_open(struct net_device *dev);
 static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance,
 			      struct pt_regs *regs);
-static void rtl8169_init_ring(struct net_device *dev);
+static int 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);
@@ -306,11 +336,15 @@
 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;
+    RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK;
 static const unsigned int rtl8169_rx_config =
     (RX_FIFO_THRESH &lt;&lt; RxCfgFIFOShift) | (RX_DMA_BURST &lt;&lt; RxCfgDMAShift);
 
+#define PHY_Cap_10_Half_Or_Less PHY_Cap_10_Half
+#define PHY_Cap_10_Full_Or_Less PHY_Cap_10_Full | PHY_Cap_10_Half_Or_Less
+#define PHY_Cap_100_Half_Or_Less PHY_Cap_100_Half | PHY_Cap_10_Full_Or_Less
+#define PHY_Cap_100_Full_Or_Less PHY_Cap_100_Full | PHY_Cap_100_Half_Or_Less
+
 void
 mdio_write(void *ioaddr, int RegAddr, int value)
 {
@@ -342,13 +376,258 @@
 		if (RTL_R32(PHYAR) &amp; 0x80000000) {
 			value = (int) (RTL_R32(PHYAR) &amp; 0xFFFF);
 			break;
-		} else {
-			udelay(100);
 		}
+		udelay(100);
 	}
 	return value;
 }
 
+static void rtl8169_write_gmii_reg_bit(void *ioaddr, int reg, int bitnum,
+				       int bitval)
+{
+	int val;
+
+	val = mdio_read(ioaddr, reg);
+	val = (bitval == 1) ?
+		val | (bitval &lt;&lt; bitnum) :  val &amp; ~(0x0001 &lt;&lt; bitnum);
+	mdio_write(ioaddr, reg, val &amp; 0xffff); 
+}
+
+static void rtl8169_get_mac_version(struct rtl8169_private *tp, void *ioaddr)
+{
+	const struct {
+		u32 mask;
+		int mac_version;
+	} mac_info[] = {
+		{ 0x1 &lt;&lt; 26,	RTL_GIGA_MAC_VER_E },
+		{ 0x1 &lt;&lt; 23,	RTL_GIGA_MAC_VER_D }, 
+		{ 0x00000000,	RTL_GIGA_MAC_VER_B } /* Catch-all */
+	}, *p = mac_info;
+	u32 reg;
+
+	reg = RTL_R32(TxConfig) &amp; 0x7c800000;
+	while ((reg &amp; p-&gt;mask) != p-&gt;mask)
+		p++;
+	tp-&gt;mac_version = p-&gt;mac_version;
+}
+
+static void rtl8169_print_mac_version(struct rtl8169_private *tp)
+{
+	struct {
+		int version;
+		char *msg;
+	} mac_print[] = {
+		{ RTL_GIGA_MAC_VER_E, "RTL_GIGA_MAC_VER_E" },
+		{ RTL_GIGA_MAC_VER_D, "RTL_GIGA_MAC_VER_D" },
+		{ RTL_GIGA_MAC_VER_B, "RTL_GIGA_MAC_VER_B" },
+		{ 0, NULL }
+	}, *p;
+
+	for (p = mac_print; p-&gt;msg; p++) {
+		if (tp-&gt;mac_version == p-&gt;version) {
+			dprintk("mac_version == %s (%04d)\n", p-&gt;msg,
+				  p-&gt;version);
+			return;
+		}
+	}
+	dprintk("mac_version == Unknown\n");
+}
+
+static void rtl8169_get_phy_version(struct rtl8169_private *tp, void *ioaddr)
+{
+	const struct {
+		u16 mask;
+		u16 set;
+		int phy_version;
+	} phy_info[] = {
+		{ 0x000f, 0x0002, RTL_GIGA_PHY_VER_G },
+		{ 0x000f, 0x0001, RTL_GIGA_PHY_VER_F },
+		{ 0x000f, 0x0000, RTL_GIGA_PHY_VER_E },
+		{ 0x0000, 0x0000, RTL_GIGA_PHY_VER_D } /* Catch-all */
+	}, *p = phy_info;
+	u16 reg;
+
+	reg = mdio_read(ioaddr, 3) &amp; 0xffff;
+	while ((reg &amp; p-&gt;mask) != p-&gt;set)
+		p++;
+	tp-&gt;phy_version = p-&gt;phy_version;
+}
+
+static void rtl8169_print_phy_version(struct rtl8169_private *tp)
+{
+	struct {
+		int version;
+		char *msg;
+		u32 reg;
+	} phy_print[] = {
+		{ RTL_GIGA_PHY_VER_G, "RTL_GIGA_PHY_VER_G", 0x0002 },
+		{ RTL_GIGA_PHY_VER_F, "RTL_GIGA_PHY_VER_F", 0x0001 },
+		{ RTL_GIGA_PHY_VER_E, "RTL_GIGA_PHY_VER_E", 0x0000 },
+		{ RTL_GIGA_PHY_VER_D, "RTL_GIGA_PHY_VER_D", 0x0000 },
+		{ 0, NULL, 0x0000 }
+	}, *p;
+
+	for (p = phy_print; p-&gt;msg; p++) {
+		if (tp-&gt;phy_version == p-&gt;version) {
+			dprintk("phy_version == %s (%04x)\n", p-&gt;msg, p-&gt;reg);
+			return;
+		}
+	}
+	dprintk("phy_version == Unknown\n");
+}
+
+static void rtl8169_hw_phy_config(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev-&gt;priv;
+	void *ioaddr = tp-&gt;mmio_addr;
+	struct {
+		u16 regs[5]; /* Beware of bit-sign propagation */
+	} phy_magic[5] = { {
+		{ 0x0000,	//w 4 15 12 0
+		  0x00a1,	//w 3 15 0 00a1
+		  0x0008,	//w 2 15 0 0008
+		  0x1020,	//w 1 15 0 1020
+		  0x1000 } },{	//w 0 15 0 1000
+		{ 0x7000,	//w 4 15 12 7
+		  0xff41,	//w 3 15 0 ff41
+		  0xde60,	//w 2 15 0 de60
+		  0x0140,	//w 1 15 0 0140
+		  0x0077 } },{	//w 0 15 0 0077
+		{ 0xa000,	//w 4 15 12 a
+		  0xdf01,	//w 3 15 0 df01
+		  0xdf20,	//w 2 15 0 df20
+		  0xff95,	//w 1 15 0 ff95
+		  0xfa00 } },{	//w 0 15 0 fa00
+		{ 0xb000,	//w 4 15 12 b
+		  0xff41,	//w 3 15 0 ff41
+		  0xde20,	//w 2 15 0 de20
+		  0x0140,	//w 1 15 0 0140
+		  0x00bb } },{	//w 0 15 0 00bb
+		{ 0xf000,	//w 4 15 12 f
+		  0xdf01,	//w 3 15 0 df01
+		  0xdf20,	//w 2 15 0 df20
+		  0xff95,	//w 1 15 0 ff95
+		  0xbf00 }	//w 0 15 0 bf00
+		}
+	}, *p = phy_magic;
+	int i;
+
+	rtl8169_print_mac_version(tp);
+	rtl8169_print_phy_version(tp);
+
+	if (tp-&gt;mac_version &lt;= RTL_GIGA_MAC_VER_B)
+		return;
+	if (tp-&gt;phy_version &gt;= RTL_GIGA_PHY_VER_F) 
+		return;
+
+	dprintk("MAC version != 0 &amp;&amp; PHY version == 0 or 1\n");
+	dprintk("Do final_reg2.cfg\n");
+
+	/* Shazam ! */
+
+	// phy config for RTL8169s mac_version C chip
+	mdio_write(ioaddr, 31, 0x0001);			//w 31 2 0 1
+	mdio_write(ioaddr, 21, 0x1000);			//w 21 15 0 1000
+	mdio_write(ioaddr, 24, 0x65c7);			//w 24 15 0 65c7
+	rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0);	//w 4 11 11 0
+
+	for (i = 0; i &lt; ARRAY_SIZE(phy_magic); i++, p++) {
+		int val, pos = 4;
+
+		val = (mdio_read(ioaddr, pos) &amp; 0x0fff) | (p-&gt;regs[0] &amp; 0xffff);
+		mdio_write(ioaddr, pos, val);
+		while (--pos &gt;= 0)
+			mdio_write(ioaddr, pos, p-&gt;regs[4 - pos] &amp; 0xffff);
+		rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 1); //w 4 11 11 1
+		rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0
+	}
+	mdio_write(ioaddr, 31, 0x0000); //w 31 2 0 0
+}
+
+static void rtl8169_hw_phy_reset(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev-&gt;priv;
+	void *ioaddr = tp-&gt;mmio_addr;
+	int i, val;
+
+	printk(KERN_WARNING PFX "%s: Reset RTL8169s PHY\n", dev-&gt;name);
+
+	val = (mdio_read(ioaddr, 0) | 0x8000) &amp; 0xffff;
+	mdio_write(ioaddr, 0, val);
+
+	for (i = 50; i &gt;= 0; i--) {
+		if (!(mdio_read(ioaddr, 0) &amp; 0x8000))
+			break;
+		udelay(100); /* Gross */
+	}
+
+	if (i &lt; 0) {
+		printk(KERN_WARNING PFX "%s: no PHY Reset ack. Giving up.\n",
+		       dev-&gt;name);
+	}
+}
+
+static void rtl8169_phy_timer(unsigned long __opaque)
+{
+	struct net_device *dev = (struct net_device *)__opaque;
+	struct rtl8169_private *tp = dev-&gt;priv;
+	struct timer_list *timer = &amp;tp-&gt;timer;
+	void *ioaddr = tp-&gt;mmio_addr;
+
+	assert(tp-&gt;mac_version &gt; RTL_GIGA_MAC_VER_B);
+	assert(tp-&gt;phy_version &lt; RTL_GIGA_PHY_VER_G);
+
+	if (RTL_R8(PHYstatus) &amp; LinkStatus)
+		tp-&gt;phy_link_down_cnt = 0;
+	else {
+		tp-&gt;phy_link_down_cnt++;
+		if (tp-&gt;phy_link_down_cnt &gt;= 12) {
+			int reg;
+
+			// If link on 1000, perform phy reset.
+			reg = mdio_read(ioaddr, PHY_1000_CTRL_REG);
+			if (reg &amp; PHY_Cap_1000_Full) 
+				rtl8169_hw_phy_reset(dev);
+
+			tp-&gt;phy_link_down_cnt = 0;
+		}
+	}
+
+	mod_timer(timer, RTL8169_PHY_TIMEOUT);
+}
+
+static inline void rtl8169_delete_timer(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev-&gt;priv;
+	struct timer_list *timer = &amp;tp-&gt;timer;
+
+	if ((tp-&gt;mac_version &lt;= RTL_GIGA_MAC_VER_B) ||
+	    (tp-&gt;phy_version &gt;= RTL_GIGA_PHY_VER_G))
+		return;
+
+	del_timer_sync(timer);
+
+	tp-&gt;phy_link_down_cnt = 0;
+}
+
+static inline void rtl8169_request_timer(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev-&gt;priv;
+	struct timer_list *timer = &amp;tp-&gt;timer;
+
+	if ((tp-&gt;mac_version &lt;= RTL_GIGA_MAC_VER_B) ||
+	    (tp-&gt;phy_version &gt;= RTL_GIGA_PHY_VER_G))
+		return;
+
+	tp-&gt;phy_link_down_cnt = 0;
+
+	init_timer(timer);
+	timer-&gt;expires = jiffies + RTL8169_PHY_TIMEOUT;
+	timer-&gt;data = (unsigned long)(dev);
+	timer-&gt;function = rtl8169_phy_timer;
+	add_timer(timer);
+}
+
 static int __devinit
 rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out,
 		   void **ioaddr_out)
@@ -356,9 +635,9 @@
 	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;
+	int rc, i, acpi_idle_state = 0, pm_cap;
+
 
 	assert(pdev != NULL);
 	assert(ioaddr_out != NULL);
@@ -379,8 +658,22 @@
 
 	// enable device (incl. PCI PM wakeup and hotplug setup)
 	rc = pci_enable_device(pdev);
-	if (rc)
+	if (rc) {
+		printk(KERN_ERR PFX "%s: unable to enable device\n", pdev-&gt;slot_name);
 		goto err_out;
+	}
+
+	/* save power state before pci_enable_device overwrites it */
+	pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+	if (pm_cap) {
+		u16 pwr_command;
+
+		pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &amp;pwr_command);
+		acpi_idle_state = pwr_command &amp; PCI_PM_CTRL_STATE_MASK;
+	} else {
+		printk(KERN_ERR PFX "Cannot find PowerManagement capability, aborting.\n");
+		goto err_out_free_res;
+	}
 
 	mmio_start = pci_resource_start(pdev, 1);
 	mmio_end = pci_resource_end(pdev, 1);
@@ -402,8 +695,10 @@
 	}
 
 	rc = pci_request_regions(pdev, dev-&gt;name);
-	if (rc)
+	if (rc) {
+		printk(KERN_ERR PFX "%s: Could not request regions.\n", pdev-&gt;slot_name);
 		goto err_out_disable;
+	}
 
 	// enable PCI bus-mastering
 	pci_set_master(pdev);
@@ -420,30 +715,32 @@
 	RTL_W8(ChipCmd, CmdReset);
 
 	// Check that the chip has finished the reset.
-	for (i = 1000; i &gt; 0; i--)
+	for (i = 1000; i &gt; 0; i--) {
 		if ((RTL_R8(ChipCmd) &amp; CmdReset) == 0)
 			break;
-		else
-			udelay(10);
+		udelay(10);
+	}
 
-	// identify chip attached to board
-	tmp = RTL_R32(TxConfig);
-	tmp = ((tmp &amp; 0x7c000000) + ((tmp &amp; 0x00800000) &lt;&lt; 2)) &gt;&gt; 24;
-
-	for (i = ARRAY_SIZE(rtl_chip_info) - 1; i &gt;= 0; i--)
-		if (tmp == rtl_chip_info[i].version) {
-			tp-&gt;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",
-	       pci_name(pdev));
-	printk(KERN_DEBUG PFX "PCI device %s: TxConfig = 0x%lx\n",
-	       pci_name(pdev), (unsigned long) RTL_R32(TxConfig));
-	tp-&gt;chipset = 0;
+	// Identify chip attached to board
+	rtl8169_get_mac_version(tp, ioaddr);
+	rtl8169_get_phy_version(tp, ioaddr);
+
+	rtl8169_print_mac_version(tp);
+	rtl8169_print_phy_version(tp);
+
+	for (i = ARRAY_SIZE(rtl_chip_info) - 1; i &gt;= 0; i--) {
+		if (tp-&gt;mac_version == rtl_chip_info[i].mac_version)
+			break;
+	}
+	if (i &lt; 0) {
+		/* Unknown chip: assume array element #0, original RTL-8169 */
+		printk(KERN_DEBUG PFX
+		       "PCI device %s: unknown chip version, assuming %s\n",
+		       pci_name(pdev), rtl_chip_info[0].name);
+		i++;
+	}
+	tp-&gt;chipset = i;
 
-match:
 	*ioaddr_out = ioaddr;
 	*dev_out = dev;
 	return 0;
@@ -499,7 +796,7 @@
 	dev-&gt;stop = rtl8169_close;
 	dev-&gt;tx_timeout = rtl8169_tx_timeout;
 	dev-&gt;set_multicast_list = rtl8169_set_rx_mode;
-	dev-&gt;watchdog_timeo = TX_TIMEOUT;
+	dev-&gt;watchdog_timeo = RTL8169_TX_TIMEOUT;
 	dev-&gt;irq = pdev-&gt;irq;
 	dev-&gt;base_addr = (unsigned long) ioaddr;
 //      dev-&gt;do_ioctl           = mii_ioctl;
@@ -528,12 +825,29 @@
 	       "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
 	       "IRQ %d\n",
 	       dev-&gt;name,
-	       board_info[ent-&gt;driver_data].name,
+	       rtl_chip_info[ent-&gt;driver_data].name,
 	       dev-&gt;base_addr,
 	       dev-&gt;dev_addr[0], dev-&gt;dev_addr[1],
 	       dev-&gt;dev_addr[2], dev-&gt;dev_addr[3],
 	       dev-&gt;dev_addr[4], dev-&gt;dev_addr[5], dev-&gt;irq);
 
+	rtl8169_hw_phy_config(dev);
+
+	dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
+	RTL_W8(0x82, 0x01);
+
+	if (tp-&gt;mac_version &lt; RTL_GIGA_MAC_VER_E) {
+		dprintk("Set PCI Latency=0x40\n");
+		pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40);
+	}
+
+	if (tp-&gt;mac_version == RTL_GIGA_MAC_VER_D) {
+		dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n");
+		RTL_W8(0x82, 0x01);
+		dprintk("Set PHY Reg 0x0bh = 0x00h\n");
+		mdio_write(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0
+	}
+
 	// if TBI is not endbled
 	if (!(RTL_R8(PHYstatus) &amp; TBI_Enable)) {
 		int val = mdio_read(ioaddr, PHY_AUTO_NEGO_REG);
@@ -546,23 +860,23 @@
 			Cap10_100 = 0, Cap1000 = 0;
 			switch (option) {
 			case _10_Half:
-				Cap10_100 = PHY_Cap_10_Half;
+				Cap10_100 = PHY_Cap_10_Half_Or_Less;
 				Cap1000 = PHY_Cap_Null;
 				break;
 			case _10_Full:
-				Cap10_100 = PHY_Cap_10_Full;
+				Cap10_100 = PHY_Cap_10_Full_Or_Less;
 				Cap1000 = PHY_Cap_Null;
 				break;
 			case _100_Half:
-				Cap10_100 = PHY_Cap_100_Half;
+				Cap10_100 = PHY_Cap_100_Half_Or_Less;
 				Cap1000 = PHY_Cap_Null;
 				break;
 			case _100_Full:
-				Cap10_100 = PHY_Cap_100_Full;
+				Cap10_100 = PHY_Cap_100_Full_Or_Less;
 				Cap1000 = PHY_Cap_Null;
 				break;
 			case _1000_Full:
-				Cap10_100 = PHY_Cap_Null;
+				Cap10_100 = PHY_Cap_100_Full_Or_Less;
 				Cap1000 = PHY_Cap_1000_Full;
 				break;
 			default:
@@ -576,9 +890,7 @@
 
 			// 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 &amp;
-									  0x1F));
+				   PHY_Cap_100_Full_Or_Less | (val &amp; 0x1f));
 
 			// enable 1000 Full Mode
 			mdio_write(ioaddr, PHY_1000_CTRL_REG,
@@ -647,56 +959,96 @@
 	pci_set_drvdata(pdev, NULL);
 }
 
+#ifdef CONFIG_PM
+
+static int rtl8169_suspend(struct pci_dev *pdev, u32 state)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct rtl8169_private *tp = dev-&gt;priv;
+	void *ioaddr = tp-&gt;mmio_addr;
+	unsigned long flags;
+
+	if (!netif_running(dev))
+		return 0;
+	
+	netif_device_detach(dev);
+	netif_stop_queue(dev);
+	spin_lock_irqsave(&amp;tp-&gt;lock, flags);
+
+	/* Disable interrupts, stop Rx and Tx */
+	RTL_W16(IntrMask, 0);
+	RTL_W8(ChipCmd, 0);
+		
+	/* Update the error counts. */
+	tp-&gt;stats.rx_missed_errors += RTL_R32(RxMissed);
+	RTL_W32(RxMissed, 0);
+	spin_unlock_irqrestore(&amp;tp-&gt;lock, flags);
+	
+	return 0;
+}
+
+static int rtl8169_resume(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+
+	if (!netif_running(dev))
+	    return 0;
+
+	netif_device_attach(dev);
+	rtl8169_hw_start(dev);
+
+	return 0;
+}
+                                                                                
+#endif /* CONFIG_PM */
+
 static int
 rtl8169_open(struct net_device *dev)
 {
 	struct rtl8169_private *tp = dev-&gt;priv;
+	struct pci_dev *pdev = tp-&gt;pci_dev;
 	int retval;
-	u8 diff;
-	u32 TxPhyAddr, RxPhyAddr;
 
 	retval =
 	    request_irq(dev-&gt;irq, rtl8169_interrupt, SA_SHIRQ, dev-&gt;name, dev);
-	if (retval) {
-		return retval;
-	}
+	if (retval &lt; 0)
+		goto out;
 
-	tp-&gt;TxDescArrays =
-	    kmalloc(NUM_TX_DESC * sizeof (struct TxDesc) + 256, GFP_KERNEL);
-	// Tx Desscriptor needs 256 bytes alignment;
-	TxPhyAddr = virt_to_bus(tp-&gt;TxDescArrays);
-	diff = 256 - (TxPhyAddr - ((TxPhyAddr &gt;&gt; 8) &lt;&lt; 8));
-	TxPhyAddr += diff;
-	tp-&gt;TxDescArray = (struct TxDesc *) (tp-&gt;TxDescArrays + diff);
-
-	tp-&gt;RxDescArrays =
-	    kmalloc(NUM_RX_DESC * sizeof (struct RxDesc) + 256, GFP_KERNEL);
-	// Rx Desscriptor needs 256 bytes alignment;
-	RxPhyAddr = virt_to_bus(tp-&gt;RxDescArrays);
-	diff = 256 - (RxPhyAddr - ((RxPhyAddr &gt;&gt; 8) &lt;&lt; 8));
-	RxPhyAddr += diff;
-	tp-&gt;RxDescArray = (struct RxDesc *) (tp-&gt;RxDescArrays + diff);
+	retval = -ENOMEM;
 
-	if (tp-&gt;TxDescArrays == NULL || tp-&gt;RxDescArrays == NULL) {
-		printk(KERN_INFO
-		       "Allocate RxDescArray or TxDescArray failed\n");
-		free_irq(dev-&gt;irq, dev);
-		if (tp-&gt;TxDescArrays)
-			kfree(tp-&gt;TxDescArrays);
-		if (tp-&gt;RxDescArrays)
-			kfree(tp-&gt;RxDescArrays);
-		return -ENOMEM;
-	}
-	tp-&gt;RxBufferRings = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL);
-	if (tp-&gt;RxBufferRings == NULL) {
-		printk(KERN_INFO "Allocate RxBufferRing failed\n");
-	}
+	/*
+	 * Rx and Tx desscriptors needs 256 bytes alignment.
+	 * pci_alloc_consistent provides more.
+	 */
+	tp-&gt;TxDescArray = pci_alloc_consistent(pdev, R8169_TX_RING_BYTES,
+					       &amp;tp-&gt;TxPhyAddr);
+	if (!tp-&gt;TxDescArray)
+		goto err_free_irq;
+
+	tp-&gt;RxDescArray = pci_alloc_consistent(pdev, R8169_RX_RING_BYTES,
+					       &amp;tp-&gt;RxPhyAddr);
+	if (!tp-&gt;RxDescArray)
+		goto err_free_tx;
+
+	retval = rtl8169_init_ring(dev);
+	if (retval &lt; 0)
+		goto err_free_rx;
 
-	rtl8169_init_ring(dev);
 	rtl8169_hw_start(dev);
 
-	return 0;
-
+	rtl8169_request_timer(dev);
+out:
+	return retval;
+
+err_free_rx:
+	pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp-&gt;RxDescArray,
+			    tp-&gt;RxPhyAddr);
+err_free_tx:
+	pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp-&gt;TxDescArray,
+			    tp-&gt;TxPhyAddr);
+err_free_irq:
+	free_irq(dev-&gt;irq, dev);
+	goto out;
 }
 
 static void
@@ -733,11 +1085,17 @@
 	RTL_W32(TxConfig,
 		(TX_DMA_BURST &lt;&lt; TxDMAShift) | (InterFrameGap &lt;&lt;
 						TxInterFrameGapShift));
+	RTL_W16(CPlusCmd, RTL_R16(CPlusCmd));
+
+	if (tp-&gt;mac_version == RTL_GIGA_MAC_VER_D) {
+		dprintk(KERN_INFO PFX "Set MAC Reg C+CR Offset 0xE0: bit-3 and bit-14 MUST be 1\n");
+		RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) | (1 &lt;&lt; 14) | (1 &lt;&lt; 3));
+	}
 
 	tp-&gt;cur_rx = 0;
 
-	RTL_W32(TxDescStartAddr, virt_to_bus(tp-&gt;TxDescArray));
-	RTL_W32(RxDescStartAddr, virt_to_bus(tp-&gt;RxDescArray));
+	RTL_W32(TxDescStartAddr, tp-&gt;TxPhyAddr);
+	RTL_W32(RxDescStartAddr, tp-&gt;RxPhyAddr);
 	RTL_W8(Cfg9346, Cfg9346_Lock);
 	udelay(10);
 
@@ -755,31 +1113,131 @@
 
 }
 
-static void
-rtl8169_init_ring(struct net_device *dev)
+static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc)
+{
+	desc-&gt;buf_addr = 0xdeadbeef;
+	desc-&gt;status &amp;= ~cpu_to_le32(OWNbit | RsvdMask);
+}
+
+static void rtl8169_free_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff,
+				struct RxDesc *desc)
+{
+	pci_unmap_single(pdev, le32_to_cpu(desc-&gt;buf_addr), RX_BUF_SIZE,
+			 PCI_DMA_FROMDEVICE);
+	dev_kfree_skb(*sk_buff);
+	*sk_buff = NULL;
+	rtl8169_make_unusable_by_asic(desc);
+}
+
+static inline void rtl8169_return_to_asic(struct RxDesc *desc)
+{
+	desc-&gt;status |= cpu_to_le32(OWNbit + RX_BUF_SIZE);
+}
+
+static inline void rtl8169_give_to_asic(struct RxDesc *desc, dma_addr_t mapping)
+{
+	desc-&gt;buf_addr = cpu_to_le32(mapping);
+	desc-&gt;status |= cpu_to_le32(OWNbit + RX_BUF_SIZE);
+}
+
+static int rtl8169_alloc_rx_skb(struct pci_dev *pdev, struct net_device *dev,
+				struct sk_buff **sk_buff, struct RxDesc *desc)
+{
+	struct sk_buff *skb;
+	dma_addr_t mapping;
+	int ret = 0;
+
+	skb = dev_alloc_skb(RX_BUF_SIZE);
+	if (!skb)
+		goto err_out;
+
+	skb-&gt;dev = dev;
+	skb_reserve(skb, 2);
+	*sk_buff = skb;
+
+	mapping = pci_map_single(pdev, skb-&gt;tail, RX_BUF_SIZE,
+				 PCI_DMA_FROMDEVICE);
+
+	rtl8169_give_to_asic(desc, mapping);
+
+out:
+	return ret;
+
+err_out:
+	ret = -ENOMEM;
+	rtl8169_make_unusable_by_asic(desc);
+	goto out;
+}
+
+static void rtl8169_rx_clear(struct rtl8169_private *tp)
 {
-	struct rtl8169_private *tp = dev-&gt;priv;
 	int i;
 
-	tp-&gt;cur_rx = 0;
-	tp-&gt;cur_tx = 0;
-	tp-&gt;dirty_tx = 0;
+	for (i = 0; i &lt; NUM_RX_DESC; i++) {
+		if (tp-&gt;Rx_skbuff[i]) {
+			rtl8169_free_rx_skb(tp-&gt;pci_dev, tp-&gt;Rx_skbuff + i,
+					    tp-&gt;RxDescArray + i);
+		}
+	}
+}
+
+static u32 rtl8169_rx_fill(struct rtl8169_private *tp, struct net_device *dev,
+			   u32 start, u32 end)
+{
+	u32 cur;
+	
+	for (cur = start; end - cur &gt; 0; cur++) {
+		int ret, i = cur % NUM_RX_DESC;
+
+		if (tp-&gt;Rx_skbuff[i])
+			continue;
+			
+		ret = rtl8169_alloc_rx_skb(tp-&gt;pci_dev, dev, tp-&gt;Rx_skbuff + i,
+					   tp-&gt;RxDescArray + i);
+		if (ret &lt; 0)
+			break;
+	}
+	return cur - start;
+}
+
+static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc)
+{
+	desc-&gt;status |= cpu_to_le32(EORbit);
+}
+
+static int rtl8169_init_ring(struct net_device *dev)
+{
+	struct rtl8169_private *tp = dev-&gt;priv;
+
+	tp-&gt;cur_rx = tp-&gt;dirty_rx = 0;
+	tp-&gt;cur_tx = tp-&gt;dirty_tx = 0;
 	memset(tp-&gt;TxDescArray, 0x0, NUM_TX_DESC * sizeof (struct TxDesc));
 	memset(tp-&gt;RxDescArray, 0x0, NUM_RX_DESC * sizeof (struct RxDesc));
 
-	for (i = 0; i &lt; NUM_TX_DESC; i++) {
-		tp-&gt;Tx_skbuff[i] = NULL;
-	}
-	for (i = 0; i &lt; NUM_RX_DESC; i++) {
-		if (i == (NUM_RX_DESC - 1))
-			tp-&gt;RxDescArray[i].status =
-			    (OWNbit | EORbit) + RX_BUF_SIZE;
-		else
-			tp-&gt;RxDescArray[i].status = OWNbit + RX_BUF_SIZE;
+	memset(tp-&gt;Tx_skbuff, 0x0, NUM_TX_DESC * sizeof(struct sk_buff *));
+	memset(tp-&gt;Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *));
 
-		tp-&gt;RxBufferRing[i] = &amp;(tp-&gt;RxBufferRings[i * RX_BUF_SIZE]);
-		tp-&gt;RxDescArray[i].buf_addr = virt_to_bus(tp-&gt;RxBufferRing[i]);
-	}
+	if (rtl8169_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC)
+		goto err_out;
+
+	rtl8169_mark_as_last_descriptor(tp-&gt;RxDescArray + NUM_RX_DESC - 1);
+
+	return 0;
+
+err_out:
+	rtl8169_rx_clear(tp);
+	return -ENOMEM;
+}
+
+static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff,
+				 struct TxDesc *desc)
+{
+	u32 len = sk_buff[0]-&gt;len;
+
+	pci_unmap_single(pdev, le32_to_cpu(desc-&gt;buf_addr),
+			 len &lt; ETH_ZLEN ? ETH_ZLEN : len, PCI_DMA_TODEVICE);
+	desc-&gt;buf_addr = 0x00;
+	*sk_buff = NULL;
 }
 
 static void
@@ -789,9 +1247,12 @@
 
 	tp-&gt;cur_tx = 0;
 	for (i = 0; i &lt; NUM_TX_DESC; i++) {
-		if (tp-&gt;Tx_skbuff[i] != NULL) {
-			dev_kfree_skb(tp-&gt;Tx_skbuff[i]);
-			tp-&gt;Tx_skbuff[i] = NULL;
+		struct sk_buff *skb = tp-&gt;Tx_skbuff[i];
+
+		if (skb) {
+			rtl8169_unmap_tx_skb(tp-&gt;pci_dev, tp-&gt;Tx_skbuff + i,
+					     tp-&gt;TxDescArray + i);
+			dev_kfree_skb(skb);
 			tp-&gt;stats.tx_dropped++;
 		}
 	}
@@ -829,48 +1290,58 @@
 	struct rtl8169_private *tp = dev-&gt;priv;
 	void *ioaddr = tp-&gt;mmio_addr;
 	int entry = tp-&gt;cur_tx % NUM_TX_DESC;
+	u32 len = skb-&gt;len;
 
-	if (skb-&gt;len &lt; ETH_ZLEN) {
+	if (unlikely(skb-&gt;len &lt; ETH_ZLEN)) {
 		skb = skb_padto(skb, ETH_ZLEN);
-		if (skb == NULL)
-			return 0;
+		if (!skb)
+			goto err_update_stats;
+		len = ETH_ZLEN;
 	}
 	
 	spin_lock_irq(&amp;tp-&gt;lock);
 
-	if ((tp-&gt;TxDescArray[entry].status &amp; OWNbit) == 0) {
+	if (!(le32_to_cpu(tp-&gt;TxDescArray[entry].status) &amp; OWNbit)) {
+		dma_addr_t mapping;
+
+		mapping = pci_map_single(tp-&gt;pci_dev, skb-&gt;data, len,
+					 PCI_DMA_TODEVICE);
+
 		tp-&gt;Tx_skbuff[entry] = skb;
-		tp-&gt;TxDescArray[entry].buf_addr = virt_to_bus(skb-&gt;data);
-		if (entry != (NUM_TX_DESC - 1))
-			tp-&gt;TxDescArray[entry].status =
-			    (OWNbit | FSbit | LSbit) | ((skb-&gt;len &gt; ETH_ZLEN) ?
-							skb-&gt;len : ETH_ZLEN);
-		else
-			tp-&gt;TxDescArray[entry].status =
-			    (OWNbit | EORbit | FSbit | LSbit) |
-			    ((skb-&gt;len &gt; ETH_ZLEN) ? skb-&gt;len : ETH_ZLEN);
+		tp-&gt;TxDescArray[entry].buf_addr = cpu_to_le32(mapping);
 
+		tp-&gt;TxDescArray[entry].status = cpu_to_le32(OWNbit | FSbit |
+			LSbit | len | (EORbit * !((entry + 1) % NUM_TX_DESC)));
+			
 		RTL_W8(TxPoll, 0x40);	//set polling bit
 
 		dev-&gt;trans_start = jiffies;
 
 		tp-&gt;cur_tx++;
-	}
+	} else
+		goto err_drop;
 
-	spin_unlock_irq(&amp;tp-&gt;lock);
 
 	if ((tp-&gt;cur_tx - NUM_TX_DESC) == tp-&gt;dirty_tx) {
 		netif_stop_queue(dev);
 	}
+out:
+	spin_unlock_irq(&amp;tp-&gt;lock);
 
 	return 0;
+
+err_drop:
+	dev_kfree_skb(skb);
+err_update_stats:
+	tp-&gt;stats.tx_dropped++;
+	goto out;
 }
 
 static void
 rtl8169_tx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
 		     void *ioaddr)
 {
-	unsigned long dirty_tx, tx_left = 0;
+	unsigned long dirty_tx, tx_left;
 
 	assert(dev != NULL);
 	assert(tp != NULL);
@@ -882,12 +1353,15 @@
 	while (tx_left &gt; 0) {
 		int entry = dirty_tx % NUM_TX_DESC;
 
-		if ((tp-&gt;TxDescArray[entry].status &amp; OWNbit) == 0) {
+		if (!(le32_to_cpu(tp-&gt;TxDescArray[entry].status) &amp; OWNbit)) {
 			struct sk_buff *skb = tp-&gt;Tx_skbuff[entry];
 
+			/* FIXME: is it really accurate for TxErr ? */
 			tp-&gt;stats.tx_bytes += skb-&gt;len &gt;= ETH_ZLEN ?
 					      skb-&gt;len : ETH_ZLEN;
 			tp-&gt;stats.tx_packets++;
+			rtl8169_unmap_tx_skb(tp-&gt;pci_dev, tp-&gt;Tx_skbuff + entry,
+					     tp-&gt;TxDescArray + entry);
 			dev_kfree_skb_irq(skb);
 			tp-&gt;Tx_skbuff[entry] = NULL;
 			dirty_tx++;
@@ -902,70 +1376,102 @@
 	}
 }
 
+static inline int rtl8169_try_rx_copy(struct sk_buff **sk_buff, int pkt_size,
+				      struct RxDesc *desc,
+				      struct net_device *dev)
+{
+	int ret = -1;
+
+	if (pkt_size &lt; rx_copybreak) {
+		struct sk_buff *skb;
+
+		skb = dev_alloc_skb(pkt_size + 2);
+		if (skb) {
+			skb-&gt;dev = dev;
+			skb_reserve(skb, 2);
+			eth_copy_and_sum(skb, sk_buff[0]-&gt;tail, pkt_size, 0);
+			*sk_buff = skb;
+			rtl8169_return_to_asic(desc);
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
 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;
+	unsigned long cur_rx, rx_left;
+	int delta;
 
 	assert(dev != NULL);
 	assert(tp != NULL);
 	assert(ioaddr != NULL);
 
 	cur_rx = tp-&gt;cur_rx;
+	rx_left = NUM_RX_DESC + tp-&gt;dirty_rx - cur_rx;
 
-	while ((tp-&gt;RxDescArray[cur_rx].status &amp; OWNbit) == 0) {
+	while (rx_left &gt; 0) {
+		int entry = cur_rx % NUM_RX_DESC;
+		u32 status = le32_to_cpu(tp-&gt;RxDescArray[entry].status);
 
-		if (tp-&gt;RxDescArray[cur_rx].status &amp; RxRES) {
+		if (status &amp; OWNbit)
+			break;
+		if (status &amp; RxRES) {
 			printk(KERN_INFO "%s: Rx ERROR!!!\n", dev-&gt;name);
 			tp-&gt;stats.rx_errors++;
-			if (tp-&gt;RxDescArray[cur_rx].status &amp; (RxRWT | RxRUNT))
+			if (status &amp; (RxRWT | RxRUNT))
 				tp-&gt;stats.rx_length_errors++;
-			if (tp-&gt;RxDescArray[cur_rx].status &amp; RxCRC)
+			if (status &amp; RxCRC)
 				tp-&gt;stats.rx_crc_errors++;
 		} else {
-			pkt_size =
-			    (int) (tp-&gt;RxDescArray[cur_rx].
-				   status &amp; 0x00001FFF) - 4;
-			skb = dev_alloc_skb(pkt_size + 2);
-			if (skb != NULL) {
-				skb-&gt;dev = dev;
-				skb_reserve(skb, 2);	// 16 byte align the IP fields. //
-				eth_copy_and_sum(skb, tp-&gt;RxBufferRing[cur_rx],
-						 pkt_size, 0);
-				skb_put(skb, pkt_size);
-				skb-&gt;protocol = eth_type_trans(skb, dev);
-				netif_rx(skb);
-
-				if (cur_rx == (NUM_RX_DESC - 1))
-					tp-&gt;RxDescArray[cur_rx].status =
-					    (OWNbit | EORbit) + RX_BUF_SIZE;
-				else
-					tp-&gt;RxDescArray[cur_rx].status =
-					    OWNbit + RX_BUF_SIZE;
-
-				tp-&gt;RxDescArray[cur_rx].buf_addr =
-				    virt_to_bus(tp-&gt;RxBufferRing[cur_rx]);
-				dev-&gt;last_rx = jiffies;
-				tp-&gt;stats.rx_bytes += pkt_size;
-				tp-&gt;stats.rx_packets++;
-			} else {
-				printk(KERN_WARNING
-				       "%s: Memory squeeze, deferring packet.\n",
-				       dev-&gt;name);
-				/* We should check that some rx space is free.
-				   If not, free one and mark stats-&gt;rx_dropped++. */
-				tp-&gt;stats.rx_dropped++;
+			struct RxDesc *desc = tp-&gt;RxDescArray + entry;
+			struct sk_buff *skb = tp-&gt;Rx_skbuff[entry];
+			int pkt_size = (status &amp; 0x00001FFF) - 4;
+
+			pci_dma_sync_single(tp-&gt;pci_dev,
+					    le32_to_cpu(desc-&gt;buf_addr),
+					    RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+
+			if (rtl8169_try_rx_copy(&amp;skb, pkt_size, desc, dev)) {
+				pci_unmap_single(tp-&gt;pci_dev,
+						 le32_to_cpu(desc-&gt;buf_addr),
+						 RX_BUF_SIZE,
+						 PCI_DMA_FROMDEVICE);
+				tp-&gt;Rx_skbuff[entry] = NULL;
 			}
-		}
-
-		cur_rx = (cur_rx + 1) % NUM_RX_DESC;
 
+			skb_put(skb, pkt_size);
+			skb-&gt;protocol = eth_type_trans(skb, dev);
+			netif_rx(skb);
+
+			dev-&gt;last_rx = jiffies;
+			tp-&gt;stats.rx_bytes += pkt_size;
+			tp-&gt;stats.rx_packets++;
+		}
+		
+		cur_rx++; 
+		rx_left--;
 	}
 
 	tp-&gt;cur_rx = cur_rx;
+
+	delta = rtl8169_rx_fill(tp, dev, tp-&gt;dirty_rx, tp-&gt;cur_rx);
+	if (delta &gt; 0)
+		tp-&gt;dirty_rx += delta;
+	else if (delta &lt; 0)
+		printk(KERN_INFO "%s: no Rx buffer allocated\n", dev-&gt;name);
+
+	/*
+	 * FIXME: until there is periodic timer to try and refill the ring,
+	 * a temporary shortage may definitely kill the Rx process.
+	 * - disable the asic to try and avoid an overflow and kick it again
+	 *   after refill ?
+	 * - how do others driver handle this condition (Uh oh...).
+	 */
+	if (tp-&gt;dirty_rx + NUM_RX_DESC == tp-&gt;cur_rx)
+		printk(KERN_EMERG "%s: Rx buffers exhausted\n", dev-&gt;name);
 }
 
 /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */
@@ -994,9 +1500,7 @@
 		RTL_W16(IntrStatus,
 			(status &amp; RxFIFOOver) ? (status | RxOverflow) : status);
 
-		if ((status &amp;
-		     (SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver
-		      | TxErr | TxOK | RxErr | RxOK)) == 0)
+		if (!(status &amp; rtl8169_intr_mask))
 			break;
 
 		// Rx interrupt 
@@ -1026,11 +1530,13 @@
 rtl8169_close(struct net_device *dev)
 {
 	struct rtl8169_private *tp = dev-&gt;priv;
+	struct pci_dev *pdev = tp-&gt;pci_dev;
 	void *ioaddr = tp-&gt;mmio_addr;
-	int i;
 
 	netif_stop_queue(dev);
 
+	rtl8169_delete_timer(dev);
+
 	spin_lock_irq(&amp;tp-&gt;lock);
 
 	/* Stop the chip's Tx and Rx DMA processes. */
@@ -1049,16 +1555,15 @@
 	free_irq(dev-&gt;irq, dev);
 
 	rtl8169_tx_clear(tp);
-	kfree(tp-&gt;TxDescArrays);
-	kfree(tp-&gt;RxDescArrays);
-	tp-&gt;TxDescArrays = NULL;
-	tp-&gt;RxDescArrays = NULL;
+
+	rtl8169_rx_clear(tp);
+
+	pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp-&gt;RxDescArray,
+			    tp-&gt;RxPhyAddr);
+	pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp-&gt;TxDescArray,
+			    tp-&gt;TxPhyAddr);
 	tp-&gt;TxDescArray = NULL;
 	tp-&gt;RxDescArray = NULL;
-	kfree(tp-&gt;RxBufferRings);
-	for (i = 0; i &lt; NUM_RX_DESC; i++) {
-		tp-&gt;RxBufferRing[i] = NULL;
-	}
 
 	return 0;
 }
@@ -1112,11 +1617,26 @@
 	spin_unlock_irqrestore(&amp;tp-&gt;lock, flags);
 }
 
+/**
+ *  rtl8169_get_stats - Get rtl8169 read/write statistics
+ *  @dev: The Ethernet Device to get statistics for
+ *
+ *  Get TX/RX statistics for rtl8169
+ */
 struct net_device_stats *
 rtl8169_get_stats(struct net_device *dev)
 {
 	struct rtl8169_private *tp = dev-&gt;priv;
+	void *ioaddr = tp-&gt;mmio_addr;
+	unsigned long flags;
 
+	if (netif_running(dev)) {
+		spin_lock_irqsave(&amp;tp-&gt;lock, flags);
+		tp-&gt;stats.rx_missed_errors += RTL_R32(RxMissed);
+		RTL_W32(RxMissed, 0);
+		spin_unlock_irqrestore(&amp;tp-&gt;lock, flags);
+	}
+		
 	return &amp;tp-&gt;stats;
 }
 
@@ -1125,8 +1645,10 @@
 	.id_table	= rtl8169_pci_tbl,
 	.probe		= rtl8169_init_one,
 	.remove		= __devexit_p(rtl8169_remove_one),
-	.suspend	= NULL,
-	.resume		= NULL,
+#ifdef CONFIG_PM
+	.suspend	= rtl8169_suspend,
+	.resume		= rtl8169_resume,
+#endif
 };
 
 static int __init
diff -Nru a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c
--- a/drivers/net/smc-mca.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/smc-mca.c	Tue Mar  2 17:57:45 2004
@@ -324,6 +324,9 @@
 
 	dev-&gt;open = &amp;ultramca_open;
 	dev-&gt;stop = &amp;ultramca_close_card;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 
 	NS8390_init(dev, 0);
 
diff -Nru a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c
--- a/drivers/net/smc-ultra.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/smc-ultra.c	Tue Mar  2 17:57:45 2004
@@ -121,6 +121,14 @@
 #define ULTRA_IO_EXTENT 32
 #define EN0_ERWCNT		0x08	/* Early receive warning count. */
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ultra_poll(struct net_device *dev)
+{
+	disable_irq(dev-&gt;irq);
+	ei_interrupt(dev-&gt;irq, dev, NULL);
+	enable_irq(dev-&gt;irq);
+}
+#endif
 /*	Probe for the Ultra.  This looks like a 8013 with the station
 	address PROM at I/O ports &lt;base&gt;+8 to &lt;base&gt;+13, with a checksum
 	following.
@@ -134,6 +142,9 @@
 
 	SET_MODULE_OWNER(dev);
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = &amp;ultra_poll;
+#endif
 	if (base_addr &gt; 0x1ff)		/* Check a single specified location. */
 		return ultra_probe1(dev, base_addr);
 	else if (base_addr != 0)	/* Don't probe at all. */
@@ -301,6 +312,9 @@
 	ei_status.reset_8390 = &amp;ultra_reset_8390;
 	dev-&gt;open = &amp;ultra_open;
 	dev-&gt;stop = &amp;ultra_close_card;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 	return 0;
diff -Nru a/drivers/net/smc-ultra32.c b/drivers/net/smc-ultra32.c
--- a/drivers/net/smc-ultra32.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/smc-ultra32.c	Tue Mar  2 17:57:45 2004
@@ -268,6 +268,9 @@
 	ei_status.reset_8390 = &amp;ultra32_reset_8390;
 	dev-&gt;open = &amp;ultra32_open;
 	dev-&gt;stop = &amp;ultra32_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 	return 0;
diff -Nru a/drivers/net/stnic.c b/drivers/net/stnic.c
--- a/drivers/net/stnic.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/stnic.c	Tue Mar  2 17:57:45 2004
@@ -124,6 +124,9 @@
   dev-&gt;irq = IRQ_STNIC;
   dev-&gt;open = &amp;stnic_open;
   dev-&gt;stop = &amp;stnic_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+  dev-&gt;poll_controller = ei_poll;
+#endif
 
   /* Snarf the interrupt now.  There's no point in waiting since we cannot
      share and the board will usually be enabled. */
diff -Nru a/drivers/net/tg3.c b/drivers/net/tg3.c
--- a/drivers/net/tg3.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/tg3.c	Tue Mar  2 17:57:45 2004
@@ -2513,6 +2513,13 @@
 static int tg3_init_hw(struct tg3 *);
 static int tg3_halt(struct tg3 *);
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void tg3_poll_controller(struct net_device *dev)
+{
+	tg3_interrupt(dev-&gt;irq, dev, NULL);
+}
+#endif
+
 static void tg3_reset_task(void *_data)
 {
 	struct tg3 *tp = _data;
@@ -7708,6 +7715,9 @@
 	dev-&gt;watchdog_timeo = TG3_TX_TIMEOUT;
 	dev-&gt;change_mtu = tg3_change_mtu;
 	dev-&gt;irq = pdev-&gt;irq;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = tg3_poll_controller;
+#endif
 
 	err = tg3_get_invariants(tp);
 	if (err) {
diff -Nru a/drivers/net/tlan.c b/drivers/net/tlan.c
--- a/drivers/net/tlan.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/tlan.c	Tue Mar  2 17:57:45 2004
@@ -814,6 +814,14 @@
 
 } /* TLan_EisaProbe */
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void TLan_Poll(struct net_device *dev)
+{
+	disable_irq(dev-&gt;irq);
+	TLan_HandleInterrupt(dev-&gt;irq, dev, NULL);
+	enable_irq(dev-&gt;irq);
+}
+#endif
 
 	
 
@@ -893,6 +901,9 @@
 	dev-&gt;get_stats = &amp;TLan_GetStats;
 	dev-&gt;set_multicast_list = &amp;TLan_SetMulticastList;
 	dev-&gt;do_ioctl = &amp;TLan_ioctl;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = &amp;TLan_Poll;
+#endif
 	dev-&gt;tx_timeout = &amp;TLan_tx_timeout;
 	dev-&gt;watchdog_timeo = TX_TIMEOUT;
 
diff -Nru a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
--- a/drivers/net/tulip/tulip_core.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/tulip/tulip_core.c	Tue Mar  2 17:57:45 2004
@@ -253,7 +253,7 @@
 static struct net_device_stats *tulip_get_stats(struct net_device *dev);
 static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static void set_rx_mode(struct net_device *dev);
-
+static void poll_tulip(struct net_device *dev);
 
 
 static void tulip_set_power_state (struct tulip_private *tp,
@@ -1618,6 +1618,9 @@
 	dev-&gt;get_stats = tulip_get_stats;
 	dev-&gt;do_ioctl = private_ioctl;
 	dev-&gt;set_multicast_list = set_rx_mode;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = &amp;poll_tulip;
+#endif
 
 	if (register_netdev(dev))
 		goto err_out_free_ring;
@@ -1774,6 +1777,22 @@
 	/* pci_power_off (pdev, -1); */
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void poll_tulip (struct net_device *dev)
+{
+	/* disable_irq here is not very nice, but with the lockless
+	   interrupt handler we have no other choice. */
+	disable_irq(dev-&gt;irq);
+	tulip_interrupt (dev-&gt;irq, dev, NULL);
+	enable_irq(dev-&gt;irq);
+}
+#endif
 
 static struct pci_driver tulip_driver = {
 	.name		= DRV_NAME,
diff -Nru a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
--- a/drivers/net/via-rhine.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/via-rhine.c	Tue Mar  2 17:57:45 2004
@@ -615,6 +615,15 @@
 			break;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void via_rhine_poll(struct net_device *dev)
+{
+	disable_irq(dev-&gt;irq);
+	via_rhine_interrupt(dev-&gt;irq, (void *)dev, NULL);
+	enable_irq(dev-&gt;irq);
+}
+#endif
+
 static int __devinit via_rhine_init_one (struct pci_dev *pdev,
 					 const struct pci_device_id *ent)
 {
@@ -784,6 +793,9 @@
 	dev-&gt;ethtool_ops = &amp;netdev_ethtool_ops;
 	dev-&gt;tx_timeout = via_rhine_tx_timeout;
 	dev-&gt;watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = via_rhine_poll;
+#endif
 	if (np-&gt;drv_flags &amp; ReqTxAlign)
 		dev-&gt;features |= NETIF_F_SG|NETIF_F_HW_CSUM;
 
diff -Nru a/drivers/net/wan/lmc/lmc_ver.h b/drivers/net/wan/lmc/lmc_ver.h
--- a/drivers/net/wan/lmc/lmc_ver.h	Tue Mar  2 17:57:45 2004
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,123 +0,0 @@
-#include &lt;linux/version.h&gt;
-
-#ifndef _IF_LMC_LINUXVER_
-#define _IF_LMC_LINUXVER_
-
- /*
-  * Copyright (c) 1997-2000 LAN Media Corporation (LMC)
-  * All rights reserved.  www.lanmedia.com
-  *
-  * This code is written by:
-  * Andrew Stanley-Jones (asj@cban.com)
-  * Rob Braun (bbraun@vix.com),
-  * Michael Graff (explorer@vix.com) and
-  * Matt Thomas (matt@3am-software.com).
-  *
-  * This software may be used and distributed according to the terms
-  * of the GNU General Public License version 2, incorporated herein by reference.
-  */
-
- /*
-  * This file defines and controls all linux version
-  * differences.
-  *
-  * This is being done to keep 1 central location where all linux
-  * version differences can be kept and maintained.  as this code was
-  * found version issues where pepered throughout the source code and
-  * made the souce code not only hard to read but version problems hard
-  * to track down.  If I'm overiding a function/etc with something in
-  * this file it will be prefixed by "LMC_" which will mean look
-  * here for the version dependent change that's been done.
-  *
-  */
-
-#if LINUX_VERSION_CODE &lt; 0x20363
-#define net_device device
-#endif
-
-#if LINUX_VERSION_CODE &lt; 0x20363
-#define LMC_XMITTER_BUSY(x) (x)-&gt;tbusy = 1
-#define LMC_XMITTER_FREE(x) (x)-&gt;tbusy = 0
-#define LMC_XMITTER_INIT(x) (x)-&gt;tbusy = 0
-#else
-#define LMC_XMITTER_BUSY(x) netif_stop_queue(x)
-#define LMC_XMITTER_FREE(x) netif_wake_queue(x)
-#define LMC_XMITTER_INIT(x) netif_start_queue(x)
-
-#endif
-
-
-#if LINUX_VERSION_CODE &lt; 0x20100
-//typedef unsigned int u_int32_t;
-
-#define  LMC_SETUP_20_DEV {\
-                             int indx; \
-                             for (indx = 0; indx &lt; DEV_NUMBUFFS; indx++) \
-                                skb_queue_head_init (&amp;dev-&gt;buffs[indx]); \
-                          } \
-                          dev-&gt;family = AF_INET; \
-                          dev-&gt;pa_addr = 0; \
-                          dev-&gt;pa_brdaddr = 0; \
-                          dev-&gt;pa_mask = 0xFCFFFFFF; \
-                          dev-&gt;pa_alen = 4;		/* IP addr.  sizeof(u32) */
-
-#else
-
-#define LMC_SETUP_20_DEV
-
-#endif
-
-
-#if LINUX_VERSION_CODE &lt; 0x20155 /* basically 2.2 plus */
-
-#define LMC_DEV_KFREE_SKB(skb) dev_kfree_skb((skb), FREE_WRITE)
-
-#else /* Mostly 2.0 kernels */
-
-#define LMC_DEV_KFREE_SKB(skb) dev_kfree_skb(skb)
-
-#endif
-
-#if LINUX_VERSION_CODE &lt; 0x20200
-#else
-
-#endif
-
-#if LINUX_VERSION_CODE &lt; 0x20100
-#define LMC_SKB_FREE(skb, val) (skb-&gt;free = val)
-#else
-#define LMC_SKB_FREE(skb, val)
-#endif
-
-
-#if (LINUX_VERSION_CODE &gt;= 0x20200)
-
-#define LMC_SPIN_FLAGS                unsigned long flags;
-#define LMC_SPIN_LOCK_INIT(x)         spin_lock_init(&amp;(x)-&gt;lmc_lock);
-#define LMC_SPIN_UNLOCK(x)            ((x)-&gt;lmc_lock = SPIN_LOCK_UNLOCKED)
-#define LMC_SPIN_LOCK_IRQSAVE(x)      spin_lock_irqsave (&amp;(x)-&gt;lmc_lock, flags);
-#define LMC_SPIN_UNLOCK_IRQRESTORE(x) spin_unlock_irqrestore (&amp;(x)-&gt;lmc_lock, flags);
-#else
-#define LMC_SPIN_FLAGS
-#define LMC_SPIN_LOCK_INIT(x)
-#define LMC_SPIN_UNLOCK(x)
-#define LMC_SPIN_LOCK_IRQSAVE(x)
-#define LMC_SPIN_UNLOCK_IRQRESTORE(x)
-#endif
-
-
-#if LINUX_VERSION_CODE &gt;= 0x20100
-#define LMC_COPY_FROM_USER(x, y, z) if(copy_from_user ((x), (y), (z))) return -EFAULT
-#define LMC_COPY_TO_USER(x, y, z) if(copy_to_user ((x), (y), (z))) return -EFAULT
-#else
-#define LMC_COPY_FROM_USER(x, y, z) if(verify_area(VERIFY_READ, (y), (z))) \
-			               return -EFAULT; \
-                                    memcpy_fromfs ((x), (y), (z))
-
-#define LMC_COPY_TO_USER(x, y, z)   if(verify_area(VERIFY_WRITE, (x), (z))) \
-	                               return -EFAULT; \
-                                    memcpy_tofs ((x), (y), (z))
-#endif
-
-
-#endif
diff -Nru a/drivers/net/wd.c b/drivers/net/wd.c
--- a/drivers/net/wd.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/wd.c	Tue Mar  2 17:57:45 2004
@@ -333,6 +333,9 @@
 	ei_status.get_8390_hdr = &amp;wd_get_8390_hdr;
 	dev-&gt;open = &amp;wd_open;
 	dev-&gt;stop = &amp;wd_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev-&gt;poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 #if 1
diff -Nru a/drivers/net/zorro8390.c b/drivers/net/zorro8390.c
--- a/drivers/net/zorro8390.c	Tue Mar  2 17:57:45 2004
+++ b/drivers/net/zorro8390.c	Tue Mar  2 17:57:45 2004
@@ -227,6 +227,10 @@
     ei_status.reg_offset = zorro8390_offsets;
     dev-&gt;open = &amp;zorro8390_open;
     dev-&gt;stop = &amp;zorro8390_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev-&gt;poll_controller = ei_poll;
+#endif
+
     NS8390_init(dev, 0);
     err = register_netdev(dev);
     if (err)
diff -Nru a/include/linux/netdevice.h b/include/linux/netdevice.h
--- a/include/linux/netdevice.h	Tue Mar  2 17:57:45 2004
+++ b/include/linux/netdevice.h	Tue Mar  2 17:57:45 2004
@@ -456,6 +456,12 @@
 						     unsigned char *haddr);
 	int			(*neigh_setup)(struct net_device *dev, struct neigh_parms *);
 	int			(*accept_fastpath)(struct net_device *, struct dst_entry*);
+#ifdef CONFIG_NETPOLL_RX
+	int			netpoll_rx;
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	void                    (*poll_controller)(struct net_device *dev);
+#endif
 
 	/* bridge stuff */
 	struct net_bridge_port	*br_port;
@@ -540,6 +546,9 @@
 extern struct net_device	*dev_get_by_index(int ifindex);
 extern struct net_device	*__dev_get_by_index(int ifindex);
 extern int		dev_restart(struct net_device *dev);
+#ifdef CONFIG_NETPOLL_TRAP
+extern int		netpoll_trap(void);
+#endif
 
 typedef int gifconf_func_t(struct net_device * dev, char * bufptr, int len);
 extern int		register_gifconf(unsigned int family, gifconf_func_t * gifconf);
@@ -598,12 +607,20 @@
 
 static inline void netif_wake_queue(struct net_device *dev)
 {
+#ifdef CONFIG_NETPOLL_TRAP
+	if (netpoll_trap())
+		return;
+#endif
 	if (test_and_clear_bit(__LINK_STATE_XOFF, &amp;dev-&gt;state))
 		__netif_schedule(dev);
 }
 
 static inline void netif_stop_queue(struct net_device *dev)
 {
+#ifdef CONFIG_NETPOLL_TRAP
+	if (netpoll_trap())
+		return;
+#endif
 	set_bit(__LINK_STATE_XOFF, &amp;dev-&gt;state);
 }
 
diff -Nru a/include/linux/netpoll.h b/include/linux/netpoll.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/linux/netpoll.h	Tue Mar  2 17:57:45 2004
@@ -0,0 +1,38 @@
+/*
+ * Common code for low-level network console, dump, and debugger code
+ *
+ * Derived from netconsole, kgdb-over-ethernet, and netdump patches
+ */
+
+#ifndef _LINUX_NETPOLL_H
+#define _LINUX_NETPOLL_H
+
+#include &lt;linux/netdevice.h&gt;
+#include &lt;linux/interrupt.h&gt;
+#include &lt;linux/irq.h&gt;
+#include &lt;linux/list.h&gt;
+
+struct netpoll;
+
+struct netpoll {
+	struct net_device *dev;
+	char dev_name[16], *name;
+	void (*rx_hook)(struct netpoll *, int, char *, int);
+	u32 local_ip, remote_ip;
+	u16 local_port, remote_port;
+	unsigned char local_mac[6], remote_mac[6];
+	struct list_head rx_list;
+};
+
+void netpoll_poll(struct netpoll *np);
+void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb);
+void netpoll_send_udp(struct netpoll *np, const char *msg, int len);
+int netpoll_parse_options(struct netpoll *np, char *opt);
+int netpoll_setup(struct netpoll *np);
+int netpoll_trap(void);
+void netpoll_set_trap(int trap);
+void netpoll_cleanup(struct netpoll *np);
+int netpoll_rx(struct sk_buff *skb);
+
+
+#endif
diff -Nru a/net/Kconfig b/net/Kconfig
--- a/net/Kconfig	Tue Mar  2 17:57:45 2004
+++ b/net/Kconfig	Tue Mar  2 17:57:45 2004
@@ -658,4 +658,20 @@
 
 source "net/bluetooth/Kconfig"
 
+config NETPOLL
+	def_bool NETCONSOLE
+
+config NETPOLL_RX
+	bool "Netpoll support for trapping incoming packets"
+	default n
+	depends on NETPOLL
+
+config NETPOLL_TRAP
+	bool "Netpoll traffic trapping"
+	default n
+	depends on NETPOLL
+
+config NET_POLL_CONTROLLER
+	def_bool NETPOLL
+
 endmenu
diff -Nru a/net/core/Makefile b/net/core/Makefile
--- a/net/core/Makefile	Tue Mar  2 17:57:45 2004
+++ b/net/core/Makefile	Tue Mar  2 17:57:45 2004
@@ -13,3 +13,4 @@
 obj-$(CONFIG_NET_DIVERT) += dv.o
 obj-$(CONFIG_NET_PKTGEN) += pktgen.o
 obj-$(CONFIG_NET_RADIO) += wireless.o
+obj-$(CONFIG_NETPOLL) += netpoll.o
diff -Nru a/net/core/dev.c b/net/core/dev.c
--- a/net/core/dev.c	Tue Mar  2 17:57:45 2004
+++ b/net/core/dev.c	Tue Mar  2 17:57:45 2004
@@ -105,6 +105,7 @@
 #include &lt;linux/kmod.h&gt;
 #include &lt;linux/module.h&gt;
 #include &lt;linux/kallsyms.h&gt;
+#include &lt;linux/netpoll.h&gt;
 #ifdef CONFIG_NET_RADIO
 #include &lt;linux/wireless.h&gt;		/* Note : will define WIRELESS_EXT */
 #include &lt;net/iw_handler.h&gt;
@@ -1538,6 +1539,13 @@
 	struct softnet_data *queue;
 	unsigned long flags;
 
+#ifdef CONFIG_NETPOLL_RX
+	if (skb-&gt;dev-&gt;netpoll_rx &amp;&amp; netpoll_rx(skb)) {
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+#endif
+
 	if (!skb-&gt;stamp.tv_sec)
 		do_gettimeofday(&amp;skb-&gt;stamp);
 
@@ -1692,6 +1700,13 @@
 	struct packet_type *ptype, *pt_prev;
 	int ret = NET_RX_DROP;
 	unsigned short type;
+
+#ifdef CONFIG_NETPOLL_RX
+	if (skb-&gt;dev-&gt;netpoll_rx &amp;&amp; skb-&gt;dev-&gt;poll &amp;&amp; netpoll_rx(skb)) {
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+#endif
 
 	if (!skb-&gt;stamp.tv_sec)
 		do_gettimeofday(&amp;skb-&gt;stamp);
diff -Nru a/net/core/netpoll.c b/net/core/netpoll.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/core/netpoll.c	Tue Mar  2 17:57:45 2004
@@ -0,0 +1,651 @@
+/*
+ * Common framework for low-level network console, dump, and debugger code
+ *
+ * Sep 8 2003  Matt Mackall &lt;mpm@selenic.com&gt;
+ */
+
+#include &lt;linux/smp_lock.h&gt;
+#include &lt;linux/netdevice.h&gt;
+#include &lt;linux/etherdevice.h&gt;
+#include &lt;linux/string.h&gt;
+#include &lt;linux/inetdevice.h&gt;
+#include &lt;linux/inet.h&gt;
+#include &lt;linux/interrupt.h&gt;
+#include &lt;linux/netpoll.h&gt;
+#include &lt;linux/sched.h&gt;
+#include &lt;net/tcp.h&gt;
+#include &lt;net/udp.h&gt;
+
+/*
+ * We maintain a small pool of fully-sized skbs, to make sure the
+ * message gets out even in extreme OOM situations.
+ */
+
+#define MAX_SKBS 32
+#define MAX_UDP_CHUNK 1460
+
+static spinlock_t skb_list_lock = SPIN_LOCK_UNLOCKED;
+static int nr_skbs;
+static struct sk_buff *skbs;
+
+static spinlock_t rx_list_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(rx_list);
+
+static int trapped;
+
+#define MAX_SKB_SIZE \
+		(MAX_UDP_CHUNK + sizeof(struct udphdr) + \
+				sizeof(struct iphdr) + sizeof(struct ethhdr))
+
+static void zap_completion_queue(void);
+
+static int checksum_udp(struct sk_buff *skb, struct udphdr *uh,
+			     unsigned short ulen, u32 saddr, u32 daddr)
+{
+	if (uh-&gt;check == 0)
+		return 0;
+
+	if (skb-&gt;ip_summed == CHECKSUM_HW)
+		return csum_tcpudp_magic(
+			saddr, daddr, ulen, IPPROTO_UDP, skb-&gt;csum);
+
+	skb-&gt;csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
+
+	return csum_fold(skb_checksum(skb, 0, skb-&gt;len, skb-&gt;csum));
+}
+
+void netpoll_poll(struct netpoll *np)
+{
+	int budget = 1;
+
+	if(!np-&gt;dev || !netif_running(np-&gt;dev) || !np-&gt;dev-&gt;poll_controller)
+		return;
+
+	/* Process pending work on NIC */
+	np-&gt;dev-&gt;poll_controller(np-&gt;dev);
+
+	/* If scheduling is stopped, tickle NAPI bits */
+	if(trapped &amp;&amp; np-&gt;dev-&gt;poll &amp;&amp;
+	   test_bit(__LINK_STATE_RX_SCHED, &amp;np-&gt;dev-&gt;state))
+		np-&gt;dev-&gt;poll(np-&gt;dev, &amp;budget);
+	zap_completion_queue();
+}
+
+static void refill_skbs(void)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&amp;skb_list_lock, flags);
+	while (nr_skbs &lt; MAX_SKBS) {
+		skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
+		if (!skb)
+			break;
+
+		skb-&gt;next = skbs;
+		skbs = skb;
+		nr_skbs++;
+	}
+	spin_unlock_irqrestore(&amp;skb_list_lock, flags);
+}
+
+static void zap_completion_queue(void)
+{
+	unsigned long flags;
+	struct softnet_data *sd = &amp;get_cpu_var(softnet_data);
+
+	if (sd-&gt;completion_queue) {
+		struct sk_buff *clist;
+
+		local_irq_save(flags);
+		clist = sd-&gt;completion_queue;
+		sd-&gt;completion_queue = NULL;
+		local_irq_restore(flags);
+
+		while (clist != NULL) {
+			struct sk_buff *skb = clist;
+			clist = clist-&gt;next;
+			__kfree_skb(skb);
+		}
+	}
+
+	put_cpu_var(softnet_data);
+}
+
+static struct sk_buff * find_skb(struct netpoll *np, int len, int reserve)
+{
+	int once = 1, count = 0;
+	unsigned long flags;
+	struct sk_buff *skb = NULL;
+
+	zap_completion_queue();
+repeat:
+	if (nr_skbs &lt; MAX_SKBS)
+		refill_skbs();
+
+	skb = alloc_skb(len, GFP_ATOMIC);
+
+	if (!skb) {
+		spin_lock_irqsave(&amp;skb_list_lock, flags);
+		skb = skbs;
+		if (skb)
+			skbs = skb-&gt;next;
+		skb-&gt;next = NULL;
+		nr_skbs--;
+		spin_unlock_irqrestore(&amp;skb_list_lock, flags);
+	}
+
+	if(!skb) {
+		count++;
+		if (once &amp;&amp; (count == 1000000)) {
+			printk("out of netpoll skbs!\n");
+			once = 0;
+		}
+		netpoll_poll(np);
+		goto repeat;
+	}
+
+	atomic_set(&amp;skb-&gt;users, 1);
+	skb_reserve(skb, reserve);
+	return skb;
+}
+
+void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
+{
+	int status;
+
+repeat:
+	if(!np || !np-&gt;dev || !netif_running(np-&gt;dev)) {
+		__kfree_skb(skb);
+		return;
+	}
+
+	spin_lock(&amp;np-&gt;dev-&gt;xmit_lock);
+	np-&gt;dev-&gt;xmit_lock_owner = smp_processor_id();
+
+	if (netif_queue_stopped(np-&gt;dev)) {
+		np-&gt;dev-&gt;xmit_lock_owner = -1;
+		spin_unlock(&amp;np-&gt;dev-&gt;xmit_lock);
+
+		netpoll_poll(np);
+		goto repeat;
+	}
+
+	status = np-&gt;dev-&gt;hard_start_xmit(skb, np-&gt;dev);
+	np-&gt;dev-&gt;xmit_lock_owner = -1;
+	spin_unlock(&amp;np-&gt;dev-&gt;xmit_lock);
+
+	/* transmit busy */
+	if(status)
+		goto repeat;
+}
+
+void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
+{
+	int total_len, eth_len, ip_len, udp_len;
+	struct sk_buff *skb;
+	struct udphdr *udph;
+	struct iphdr *iph;
+	struct ethhdr *eth;
+
+	udp_len = len + sizeof(*udph);
+	ip_len = eth_len = udp_len + sizeof(*iph);
+	total_len = eth_len + ETH_HLEN;
+
+	skb = find_skb(np, total_len, total_len - len);
+	if (!skb)
+		return;
+
+	memcpy(skb-&gt;data, msg, len);
+	skb-&gt;len += len;
+
+	udph = (struct udphdr *) skb_push(skb, sizeof(*udph));
+	udph-&gt;source = htons(np-&gt;local_port);
+	udph-&gt;dest = htons(np-&gt;remote_port);
+	udph-&gt;len = htons(udp_len);
+	udph-&gt;check = 0;
+
+	iph = (struct iphdr *)skb_push(skb, sizeof(*iph));
+
+	iph-&gt;version  = 4;
+	iph-&gt;ihl      = 5;
+	iph-&gt;tos      = 0;
+	iph-&gt;tot_len  = htons(ip_len);
+	iph-&gt;id       = 0;
+	iph-&gt;frag_off = 0;
+	iph-&gt;ttl      = 64;
+	iph-&gt;protocol = IPPROTO_UDP;
+	iph-&gt;check    = 0;
+	iph-&gt;saddr    = htonl(np-&gt;local_ip);
+	iph-&gt;daddr    = htonl(np-&gt;remote_ip);
+	iph-&gt;check    = ip_fast_csum((unsigned char *)iph, iph-&gt;ihl);
+
+	eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
+
+	eth-&gt;h_proto = htons(ETH_P_IP);
+	memcpy(eth-&gt;h_source, np-&gt;local_mac, 6);
+	memcpy(eth-&gt;h_dest, np-&gt;remote_mac, 6);
+
+	netpoll_send_skb(np, skb);
+}
+
+static void arp_reply(struct sk_buff *skb)
+{
+	struct in_device *in_dev = (struct in_device *) skb-&gt;dev-&gt;ip_ptr;
+	struct arphdr *arp;
+	unsigned char *arp_ptr, *sha, *tha;
+	int size, type = ARPOP_REPLY, ptype = ETH_P_ARP;
+	u32 sip, tip;
+	struct sk_buff *send_skb;
+	unsigned long flags;
+	struct list_head *p;
+	struct netpoll *np = 0;
+
+	spin_lock_irqsave(&amp;rx_list_lock, flags);
+	list_for_each(p, &amp;rx_list) {
+		np = list_entry(p, struct netpoll, rx_list);
+		if ( np-&gt;dev == skb-&gt;dev )
+			break;
+		np = 0;
+	}
+	spin_unlock_irqrestore(&amp;rx_list_lock, flags);
+
+	if (!np) return;
+
+	/* No arp on this interface */
+	if (!in_dev || skb-&gt;dev-&gt;flags &amp; IFF_NOARP)
+		return;
+
+	if (!pskb_may_pull(skb, (sizeof(struct arphdr) +
+				 (2 * skb-&gt;dev-&gt;addr_len) +
+				 (2 * sizeof(u32)))))
+		return;
+
+	skb-&gt;h.raw = skb-&gt;nh.raw = skb-&gt;data;
+	arp = skb-&gt;nh.arph;
+
+	if ((arp-&gt;ar_hrd != htons(ARPHRD_ETHER) &amp;&amp;
+	     arp-&gt;ar_hrd != htons(ARPHRD_IEEE802)) ||
+	    arp-&gt;ar_pro != htons(ETH_P_IP) ||
+	    arp-&gt;ar_op != htons(ARPOP_REQUEST))
+		return;
+
+	arp_ptr= (unsigned char *)(arp+1);
+	sha = arp_ptr;
+	arp_ptr += skb-&gt;dev-&gt;addr_len;
+	memcpy(&amp;sip, arp_ptr, 4);
+	arp_ptr += 4;
+	tha = arp_ptr;
+	arp_ptr += skb-&gt;dev-&gt;addr_len;
+	memcpy(&amp;tip, arp_ptr, 4);
+
+	/* Should we ignore arp? */
+	if (tip != in_dev-&gt;ifa_list-&gt;ifa_address ||
+	    LOOPBACK(tip) || MULTICAST(tip))
+		return;
+
+
+	size = sizeof(struct arphdr) + 2 * (skb-&gt;dev-&gt;addr_len + 4);
+	send_skb = find_skb(np, size + LL_RESERVED_SPACE(np-&gt;dev),
+			    LL_RESERVED_SPACE(np-&gt;dev));
+
+	if (!send_skb)
+		return;
+
+	send_skb-&gt;nh.raw = send_skb-&gt;data;
+	arp = (struct arphdr *) skb_put(send_skb, size);
+	send_skb-&gt;dev = skb-&gt;dev;
+	send_skb-&gt;protocol = htons(ETH_P_ARP);
+
+	/* Fill the device header for the ARP frame */
+
+	if (np-&gt;dev-&gt;hard_header &amp;&amp;
+	    np-&gt;dev-&gt;hard_header(send_skb, skb-&gt;dev, ptype,
+				       np-&gt;remote_mac, np-&gt;local_mac,
+				       send_skb-&gt;len) &lt; 0) {
+		kfree_skb(send_skb);
+		return;
+	}
+
+	/*
+	 * Fill out the arp protocol part.
+	 *
+	 * we only support ethernet device type,
+	 * which (according to RFC 1390) should always equal 1 (Ethernet).
+	 */
+
+	arp-&gt;ar_hrd = htons(np-&gt;dev-&gt;type);
+	arp-&gt;ar_pro = htons(ETH_P_IP);
+	arp-&gt;ar_hln = np-&gt;dev-&gt;addr_len;
+	arp-&gt;ar_pln = 4;
+	arp-&gt;ar_op = htons(type);
+
+	arp_ptr=(unsigned char *)(arp + 1);
+	memcpy(arp_ptr, np-&gt;dev-&gt;dev_addr, np-&gt;dev-&gt;addr_len);
+	arp_ptr += np-&gt;dev-&gt;addr_len;
+	memcpy(arp_ptr, &amp;tip, 4);
+	arp_ptr += 4;
+	memcpy(arp_ptr, np-&gt;local_mac, np-&gt;dev-&gt;addr_len);
+	arp_ptr += np-&gt;dev-&gt;addr_len;
+	memcpy(arp_ptr, &amp;sip, 4);
+
+	netpoll_send_skb(np, send_skb);
+}
+
+int netpoll_rx(struct sk_buff *skb)
+{
+	int proto, len, ulen;
+	struct iphdr *iph;
+	struct udphdr *uh;
+	struct netpoll *np;
+	struct list_head *p;
+	unsigned long flags;
+
+	if (skb-&gt;dev-&gt;type != ARPHRD_ETHER)
+		goto out;
+
+	/* check if netpoll clients need ARP */
+	if (skb-&gt;protocol == __constant_htons(ETH_P_ARP) &amp;&amp; trapped) {
+		arp_reply(skb);
+		return 1;
+	}
+
+	proto = ntohs(skb-&gt;mac.ethernet-&gt;h_proto);
+	if (proto != ETH_P_IP)
+		goto out;
+	if (skb-&gt;pkt_type == PACKET_OTHERHOST)
+		goto out;
+	if (skb_shared(skb))
+		goto out;
+
+	iph = (struct iphdr *)skb-&gt;data;
+	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		goto out;
+	if (iph-&gt;ihl &lt; 5 || iph-&gt;version != 4)
+		goto out;
+	if (!pskb_may_pull(skb, iph-&gt;ihl*4))
+		goto out;
+	if (ip_fast_csum((u8 *)iph, iph-&gt;ihl) != 0)
+		goto out;
+
+	len = ntohs(iph-&gt;tot_len);
+	if (skb-&gt;len &lt; len || len &lt; iph-&gt;ihl*4)
+		goto out;
+
+	if (iph-&gt;protocol != IPPROTO_UDP)
+		goto out;
+
+	len -= iph-&gt;ihl*4;
+	uh = (struct udphdr *)(((char *)iph) + iph-&gt;ihl*4);
+	ulen = ntohs(uh-&gt;len);
+
+	if (ulen != len)
+		goto out;
+	if (checksum_udp(skb, uh, ulen, iph-&gt;saddr, iph-&gt;daddr) &lt; 0)
+		goto out;
+
+	spin_lock_irqsave(&amp;rx_list_lock, flags);
+	list_for_each(p, &amp;rx_list) {
+		np = list_entry(p, struct netpoll, rx_list);
+		if (np-&gt;dev &amp;&amp; np-&gt;dev != skb-&gt;dev)
+			continue;
+		if (np-&gt;local_ip &amp;&amp; np-&gt;local_ip != ntohl(iph-&gt;daddr))
+			continue;
+		if (np-&gt;remote_ip &amp;&amp; np-&gt;remote_ip != ntohl(iph-&gt;saddr))
+			continue;
+		if (np-&gt;local_port &amp;&amp; np-&gt;local_port != ntohs(uh-&gt;dest))
+			continue;
+
+		spin_unlock_irqrestore(&amp;rx_list_lock, flags);
+
+		if (np-&gt;rx_hook)
+			np-&gt;rx_hook(np, ntohs(uh-&gt;source),
+				    (char *)(uh+1),
+				    ulen - sizeof(struct udphdr));
+
+		return 1;
+	}
+	spin_unlock_irqrestore(&amp;rx_list_lock, flags);
+
+out:
+	return trapped;
+}
+
+int netpoll_parse_options(struct netpoll *np, char *opt)
+{
+	char *cur=opt, *delim;
+
+	if(*cur != '@') {
+		if ((delim = strchr(cur, '@')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np-&gt;local_port=simple_strtol(cur, 0, 10);
+		cur=delim;
+	}
+	cur++;
+	printk(KERN_INFO "%s: local port %d\n", np-&gt;name, np-&gt;local_port);
+
+	if(*cur != '/') {
+		if ((delim = strchr(cur, '/')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np-&gt;local_ip=ntohl(in_aton(cur));
+		cur=delim;
+
+		printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n",
+		       np-&gt;name, HIPQUAD(np-&gt;local_ip));
+	}
+	cur++;
+
+	if ( *cur != ',') {
+		/* parse out dev name */
+		if ((delim = strchr(cur, ',')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		strlcpy(np-&gt;dev_name, cur, sizeof(np-&gt;dev_name));
+		cur=delim;
+	}
+	cur++;
+
+	printk(KERN_INFO "%s: interface %s\n", np-&gt;name, np-&gt;dev_name);
+
+	if ( *cur != '@' ) {
+		/* dst port */
+		if ((delim = strchr(cur, '@')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np-&gt;remote_port=simple_strtol(cur, 0, 10);
+		cur=delim;
+	}
+	cur++;
+	printk(KERN_INFO "%s: remote port %d\n", np-&gt;name, np-&gt;remote_port);
+
+	/* dst ip */
+	if ((delim = strchr(cur, '/')) == NULL)
+		goto parse_failed;
+	*delim=0;
+	np-&gt;remote_ip=ntohl(in_aton(cur));
+	cur=delim+1;
+
+	printk(KERN_INFO "%s: remote IP %d.%d.%d.%d\n",
+		       np-&gt;name, HIPQUAD(np-&gt;remote_ip));
+
+	if( *cur != 0 )
+	{
+		/* MAC address */
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np-&gt;remote_mac[0]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np-&gt;remote_mac[1]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np-&gt;remote_mac[2]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np-&gt;remote_mac[3]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np-&gt;remote_mac[4]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		np-&gt;remote_mac[5]=simple_strtol(cur, 0, 16);
+	}
+
+	printk(KERN_INFO "%s: remote ethernet address "
+	       "%02x:%02x:%02x:%02x:%02x:%02x\n",
+	       np-&gt;name,
+	       np-&gt;remote_mac[0],
+	       np-&gt;remote_mac[1],
+	       np-&gt;remote_mac[2],
+	       np-&gt;remote_mac[3],
+	       np-&gt;remote_mac[4],
+	       np-&gt;remote_mac[5]);
+
+	return 0;
+
+ parse_failed:
+	printk(KERN_INFO "%s: couldn't parse config at %s!\n",
+	       np-&gt;name, cur);
+	return -1;
+}
+
+int netpoll_setup(struct netpoll *np)
+{
+	struct net_device *ndev = NULL;
+	struct in_device *in_dev;
+
+	if (np-&gt;dev_name)
+		ndev = dev_get_by_name(np-&gt;dev_name);
+	if (!ndev) {
+		printk(KERN_ERR "%s: %s doesn't exist, aborting.\n",
+		       np-&gt;name, np-&gt;dev_name);
+		return -1;
+	}
+	if (!ndev-&gt;poll_controller) {
+		printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n",
+		       np-&gt;name, np-&gt;dev_name);
+		goto release;
+	}
+
+	if (!(ndev-&gt;flags &amp; IFF_UP)) {
+		unsigned short oflags;
+		unsigned long atmost, atleast;
+
+		printk(KERN_INFO "%s: device %s not up yet, forcing it\n",
+		       np-&gt;name, np-&gt;dev_name);
+
+		oflags = ndev-&gt;flags;
+
+		rtnl_shlock();
+		if (dev_change_flags(ndev, oflags | IFF_UP) &lt; 0) {
+			printk(KERN_ERR "%s: failed to open %s\n",
+			       np-&gt;name, np-&gt;dev_name);
+			rtnl_shunlock();
+			goto release;
+		}
+		rtnl_shunlock();
+
+		atleast = jiffies + HZ/10;
+ 		atmost = jiffies + 10*HZ;
+		while (!netif_carrier_ok(ndev)) {
+			if (time_after(jiffies, atmost)) {
+				printk(KERN_NOTICE
+				       "%s: timeout waiting for carrier\n",
+				       np-&gt;name);
+				break;
+			}
+			cond_resched();
+		}
+
+		if (time_before(jiffies, atleast)) {
+			printk(KERN_NOTICE "%s: carrier detect appears flaky,"
+			       " waiting 10 seconds\n",
+			       np-&gt;name);
+			while (time_before(jiffies, atmost))
+				cond_resched();
+		}
+	}
+
+	if (!memcmp(np-&gt;local_mac, "\0\0\0\0\0\0", 6) &amp;&amp; ndev-&gt;dev_addr)
+		memcpy(np-&gt;local_mac, ndev-&gt;dev_addr, 6);
+
+	if (!np-&gt;local_ip) {
+		in_dev = in_dev_get(ndev);
+
+		if (!in_dev) {
+			printk(KERN_ERR "%s: no IP address for %s, aborting\n",
+			       np-&gt;name, np-&gt;dev_name);
+			goto release;
+		}
+
+		np-&gt;local_ip = ntohl(in_dev-&gt;ifa_list-&gt;ifa_local);
+		in_dev_put(in_dev);
+		printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n",
+		       np-&gt;name, HIPQUAD(np-&gt;local_ip));
+	}
+
+	np-&gt;dev = ndev;
+
+	if(np-&gt;rx_hook) {
+		unsigned long flags;
+
+#ifdef CONFIG_NETPOLL_RX
+		np-&gt;dev-&gt;netpoll_rx = 1;
+#endif
+
+		spin_lock_irqsave(&amp;rx_list_lock, flags);
+		list_add(&amp;np-&gt;rx_list, &amp;rx_list);
+		spin_unlock_irqrestore(&amp;rx_list_lock, flags);
+	}
+
+	return 0;
+ release:
+	dev_put(ndev);
+	return -1;
+}
+
+void netpoll_cleanup(struct netpoll *np)
+{
+	if(np-&gt;rx_hook) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&amp;rx_list_lock, flags);
+		list_del(&amp;np-&gt;rx_list);
+#ifdef CONFIG_NETPOLL_RX
+		np-&gt;dev-&gt;netpoll_rx = 0;
+#endif
+		spin_unlock_irqrestore(&amp;rx_list_lock, flags);
+	}
+
+	dev_put(np-&gt;dev);
+	np-&gt;dev = 0;
+}
+
+int netpoll_trap()
+{
+	return trapped;
+}
+
+void netpoll_set_trap(int trap)
+{
+	trapped = trap;
+}
+
+EXPORT_SYMBOL(netpoll_set_trap);
+EXPORT_SYMBOL(netpoll_trap);
+EXPORT_SYMBOL(netpoll_parse_options);
+EXPORT_SYMBOL(netpoll_setup);
+EXPORT_SYMBOL(netpoll_cleanup);
+EXPORT_SYMBOL(netpoll_send_skb);
+EXPORT_SYMBOL(netpoll_send_udp);
+EXPORT_SYMBOL(netpoll_poll);
</pre></body></html>