Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6:
  [PKT_SCHED]: Fix error handling while dumping actions
  [PKT_SCHED]: Return ENOENT if action module is unavailable
  [PKT_SCHED]: Fix illegal memory dereferences when dumping actions
diff --git a/MAINTAINERS b/MAINTAINERS
index 5f76a4f5..196a31c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2039,9 +2039,10 @@
 S:	Maintained
 
 NI5010 NETWORK DRIVER
-P:	Jan-Pascal van Best and Andreas Mohr
-M:	Jan-Pascal van Best <jvbest@qv3pluto.leidenuniv.nl>
-M:	Andreas Mohr <100.30936@germany.net>
+P:	Jan-Pascal van Best
+M:	janpascal@vanbest.org
+P:	Andreas Mohr
+M:	andi@lisas.de
 L:	netdev@vger.kernel.org
 S:	Maintained
 
diff --git a/arch/arm/mach-at91rm9200/at91rm9200.c b/arch/arm/mach-at91rm9200/at91rm9200.c
index 7e1d072..0985b1c 100644
--- a/arch/arm/mach-at91rm9200/at91rm9200.c
+++ b/arch/arm/mach-at91rm9200/at91rm9200.c
@@ -107,3 +107,48 @@
 	iotable_init(at91rm9200_io_desc, ARRAY_SIZE(at91rm9200_io_desc));
 }
 
+/*
+ * The default interrupt priority levels (0 = lowest, 7 = highest).
+ */
+static unsigned int at91rm9200_default_irq_priority[NR_AIC_IRQS] __initdata = {
+	7,	/* Advanced Interrupt Controller (FIQ) */
+	7,	/* System Peripherals */
+	0,	/* Parallel IO Controller A */
+	0,	/* Parallel IO Controller B */
+	0,	/* Parallel IO Controller C */
+	0,	/* Parallel IO Controller D */
+	6,	/* USART 0 */
+	6,	/* USART 1 */
+	6,	/* USART 2 */
+	6,	/* USART 3 */
+	0,	/* Multimedia Card Interface */
+	4,	/* USB Device Port */
+	0,	/* Two-Wire Interface */
+	6,	/* Serial Peripheral Interface */
+	5,	/* Serial Synchronous Controller 0 */
+	5,	/* Serial Synchronous Controller 1 */
+	5,	/* Serial Synchronous Controller 2 */
+	0,	/* Timer Counter 0 */
+	0,	/* Timer Counter 1 */
+	0,	/* Timer Counter 2 */
+	0,	/* Timer Counter 3 */
+	0,	/* Timer Counter 4 */
+	0,	/* Timer Counter 5 */
+	3,	/* USB Host port */
+	3,	/* Ethernet MAC */
+	0,	/* Advanced Interrupt Controller (IRQ0) */
+	0,	/* Advanced Interrupt Controller (IRQ1) */
+	0,	/* Advanced Interrupt Controller (IRQ2) */
+	0,	/* Advanced Interrupt Controller (IRQ3) */
+	0,	/* Advanced Interrupt Controller (IRQ4) */
+	0,	/* Advanced Interrupt Controller (IRQ5) */
+	0	/* Advanced Interrupt Controller (IRQ6) */
+};
+
+void __init at91rm9200_init_irq(unsigned int priority[NR_AIC_IRQS])
+{
+	if (!priority)
+		priority = at91rm9200_default_irq_priority;
+
+	at91_aic_init(priority);
+}
diff --git a/arch/arm/mach-at91rm9200/generic.h b/arch/arm/mach-at91rm9200/generic.h
index f0d969d..7979d8a 100644
--- a/arch/arm/mach-at91rm9200/generic.h
+++ b/arch/arm/mach-at91rm9200/generic.h
@@ -8,13 +8,19 @@
  * published by the Free Software Foundation.
  */
 
-void at91_gpio_irq_setup(unsigned banks);
+ /* Interrupts */
+extern void __init at91rm9200_init_irq(unsigned int priority[]);
+extern void __init at91_aic_init(unsigned int priority[]);
+extern void __init at91_gpio_irq_setup(unsigned banks);
 
+ /* Timer */
 struct sys_timer;
 extern struct sys_timer at91rm9200_timer;
 
+ /* Memory Map */
 extern void __init at91rm9200_map_io(void);
 
+ /* Clocks */
 extern int __init at91_clock_init(unsigned long main_clock);
 struct device;
 extern void __init at91_clock_associate(const char *id, struct device *dev, const char *func);
diff --git a/arch/arm/mach-at91rm9200/irq.c b/arch/arm/mach-at91rm9200/irq.c
index dcd560d..9b09113 100644
--- a/arch/arm/mach-at91rm9200/irq.c
+++ b/arch/arm/mach-at91rm9200/irq.c
@@ -36,58 +36,20 @@
 
 #include "generic.h"
 
-/*
- * The default interrupt priority levels (0 = lowest, 7 = highest).
- */
-static unsigned int at91rm9200_default_irq_priority[NR_AIC_IRQS] __initdata = {
-	7,	/* Advanced Interrupt Controller */
-	7,	/* System Peripheral */
-	0,	/* Parallel IO Controller A */
-	0,	/* Parallel IO Controller B */
-	0,	/* Parallel IO Controller C */
-	0,	/* Parallel IO Controller D */
-	6,	/* USART 0 */
-	6,	/* USART 1 */
-	6,	/* USART 2 */
-	6,	/* USART 3 */
-	0,	/* Multimedia Card Interface */
-	4,	/* USB Device Port */
-	0,	/* Two-Wire Interface */
-	6,	/* Serial Peripheral Interface */
-	5,	/* Serial Synchronous Controller */
-	5,	/* Serial Synchronous Controller */
-	5,	/* Serial Synchronous Controller */
-	0,	/* Timer Counter 0 */
-	0,	/* Timer Counter 1 */
-	0,	/* Timer Counter 2 */
-	0,	/* Timer Counter 3 */
-	0,	/* Timer Counter 4 */
-	0,	/* Timer Counter 5 */
-	3,	/* USB Host port */
-	3,	/* Ethernet MAC */
-	0,	/* Advanced Interrupt Controller */
-	0,	/* Advanced Interrupt Controller */
-	0,	/* Advanced Interrupt Controller */
-	0,	/* Advanced Interrupt Controller */
-	0,	/* Advanced Interrupt Controller */
-	0,	/* Advanced Interrupt Controller */
-	0	/* Advanced Interrupt Controller */
-};
 
-
-static void at91rm9200_mask_irq(unsigned int irq)
+static void at91_aic_mask_irq(unsigned int irq)
 {
 	/* Disable interrupt on AIC */
 	at91_sys_write(AT91_AIC_IDCR, 1 << irq);
 }
 
-static void at91rm9200_unmask_irq(unsigned int irq)
+static void at91_aic_unmask_irq(unsigned int irq)
 {
 	/* Enable interrupt on AIC */
 	at91_sys_write(AT91_AIC_IECR, 1 << irq);
 }
 
-static int at91rm9200_irq_type(unsigned irq, unsigned type)
+static int at91_aic_set_type(unsigned irq, unsigned type)
 {
 	unsigned int smr, srctype;
 
@@ -122,7 +84,7 @@
 static u32 wakeups;
 static u32 backups;
 
-static int at91rm9200_irq_set_wake(unsigned irq, unsigned value)
+static int at91_aic_set_wake(unsigned irq, unsigned value)
 {
 	if (unlikely(irq >= 32))
 		return -EINVAL;
@@ -149,28 +111,24 @@
 }
 
 #else
-#define at91rm9200_irq_set_wake	NULL
+#define at91_aic_set_wake	NULL
 #endif
 
-static struct irqchip at91rm9200_irq_chip = {
-	.ack		= at91rm9200_mask_irq,
-	.mask		= at91rm9200_mask_irq,
-	.unmask		= at91rm9200_unmask_irq,
-	.set_type	= at91rm9200_irq_type,
-	.set_wake	= at91rm9200_irq_set_wake,
+static struct irqchip at91_aic_chip = {
+	.ack		= at91_aic_mask_irq,
+	.mask		= at91_aic_mask_irq,
+	.unmask		= at91_aic_unmask_irq,
+	.set_type	= at91_aic_set_type,
+	.set_wake	= at91_aic_set_wake,
 };
 
 /*
  * Initialize the AIC interrupt controller.
  */
-void __init at91rm9200_init_irq(unsigned int priority[NR_AIC_IRQS])
+void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS])
 {
 	unsigned int i;
 
-	/* No priority list specified for this board -> use defaults */
-	if (priority == NULL)
-		priority = at91rm9200_default_irq_priority;
-
 	/*
 	 * The IVR is used by macro get_irqnr_and_base to read and verify.
 	 * The irq number is NR_AIC_IRQS when a spurious interrupt has occurred.
@@ -178,10 +136,10 @@
 	for (i = 0; i < NR_AIC_IRQS; i++) {
 		/* Put irq number in Source Vector Register: */
 		at91_sys_write(AT91_AIC_SVR(i), i);
-		/* Store the Source Mode Register as defined in table above */
+		/* Active Low interrupt, with the specified priority */
 		at91_sys_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]);
 
-		set_irq_chip(i, &at91rm9200_irq_chip);
+		set_irq_chip(i, &at91_aic_chip);
 		set_irq_handler(i, do_level_IRQ);
 		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
 
diff --git a/arch/arm/mach-pnx4008/core.c b/arch/arm/mach-pnx4008/core.c
index ba91daa..3d73c1e 100644
--- a/arch/arm/mach-pnx4008/core.c
+++ b/arch/arm/mach-pnx4008/core.c
@@ -27,7 +27,6 @@
 #include <linux/spi/spi.h>
 
 #include <asm/hardware.h>
-#include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/setup.h>
 #include <asm/mach-types.h>
@@ -36,7 +35,6 @@
 #include <asm/system.h>
 
 #include <asm/mach/arch.h>
-#include <asm/mach/irq.h>
 #include <asm/mach/map.h>
 #include <asm/mach/time.h>
 
diff --git a/arch/arm/mach-pnx4008/dma.c b/arch/arm/mach-pnx4008/dma.c
index 981aa9d..ec01574 100644
--- a/arch/arm/mach-pnx4008/dma.c
+++ b/arch/arm/mach-pnx4008/dma.c
@@ -23,7 +23,6 @@
 #include <linux/clk.h>
 
 #include <asm/system.h>
-#include <asm/irq.h>
 #include <asm/hardware.h>
 #include <asm/dma.h>
 #include <asm/dma-mapping.h>
diff --git a/arch/arm/mach-pnx4008/irq.c b/arch/arm/mach-pnx4008/irq.c
index 9b0a8e0..3a4bcf3 100644
--- a/arch/arm/mach-pnx4008/irq.c
+++ b/arch/arm/mach-pnx4008/irq.c
@@ -22,8 +22,8 @@
 #include <linux/init.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
+#include <linux/irq.h>
 #include <asm/hardware.h>
-#include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/setup.h>
 #include <asm/mach-types.h>
@@ -96,26 +96,24 @@
 {
 	unsigned int i;
 
-	/* configure and enable IRQ 0,1,30,31 (cascade interrupts) mask all others */
+	/* configure IRQ's */
+	for (i = 0; i < NR_IRQS; i++) {
+		set_irq_flags(i, IRQF_VALID);
+		set_irq_chip(i, &pnx4008_irq_chip);
+		pnx4008_set_irq_type(i, pnx4008_irq_type[i]);
+	}
+
+	/* configure and enable IRQ 0,1,30,31 (cascade interrupts) */
 	pnx4008_set_irq_type(SUB1_IRQ_N, pnx4008_irq_type[SUB1_IRQ_N]);
 	pnx4008_set_irq_type(SUB2_IRQ_N, pnx4008_irq_type[SUB2_IRQ_N]);
 	pnx4008_set_irq_type(SUB1_FIQ_N, pnx4008_irq_type[SUB1_FIQ_N]);
 	pnx4008_set_irq_type(SUB2_FIQ_N, pnx4008_irq_type[SUB2_FIQ_N]);
 
+	/* mask all others */
 	__raw_writel((1 << SUB2_FIQ_N) | (1 << SUB1_FIQ_N) |
 			(1 << SUB2_IRQ_N) | (1 << SUB1_IRQ_N),
 		INTC_ER(MAIN_BASE_INT));
 	__raw_writel(0, INTC_ER(SIC1_BASE_INT));
 	__raw_writel(0, INTC_ER(SIC2_BASE_INT));
-
-	/* configure all other IRQ's */
-	for (i = 0; i < NR_IRQS; i++) {
-		if (i == SUB2_FIQ_N || i == SUB1_FIQ_N ||
-			i == SUB2_IRQ_N || i == SUB1_IRQ_N)
-			continue;
-		set_irq_flags(i, IRQF_VALID);
-		set_irq_chip(i, &pnx4008_irq_chip);
-		pnx4008_set_irq_type(i, pnx4008_irq_type[i]);
-	}
 }
 
diff --git a/arch/arm/mach-pnx4008/time.c b/arch/arm/mach-pnx4008/time.c
index 888bf6c..756228d 100644
--- a/arch/arm/mach-pnx4008/time.c
+++ b/arch/arm/mach-pnx4008/time.c
@@ -20,17 +20,15 @@
 #include <linux/spinlock.h>
 #include <linux/module.h>
 #include <linux/kallsyms.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/irq.h>
 
 #include <asm/system.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
 #include <asm/leds.h>
-#include <asm/irq.h>
-#include <asm/mach/irq.h>
 #include <asm/mach/time.h>
-
-#include <linux/time.h>
-#include <linux/timex.h>
 #include <asm/errno.h>
 
 /*! Note: all timers are UPCOUNTING */
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index fa484d4..99daeee 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -1032,7 +1032,9 @@
 static void irq_trans_init(struct device_node *dp)
 {
 	const char *model;
+#ifdef CONFIG_PCI
 	int i;
+#endif
 
 	model = of_get_property(dp, "model", NULL);
 	if (!model)
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index 4173de4..237524d 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -124,11 +124,6 @@
 EXPORT_SYMBOL(__write_unlock);
 EXPORT_SYMBOL(__write_trylock);
 
-#if defined(CONFIG_MCOUNT)
-extern void _mcount(void);
-EXPORT_SYMBOL(_mcount);
-#endif
-
 /* CPU online map and active count.  */
 EXPORT_SYMBOL(cpu_online_map);
 EXPORT_SYMBOL(phys_cpu_present_map);
@@ -136,6 +131,11 @@
 EXPORT_SYMBOL(smp_call_function);
 #endif /* CONFIG_SMP */
 
+#if defined(CONFIG_MCOUNT)
+extern void _mcount(void);
+EXPORT_SYMBOL(_mcount);
+#endif
+
 EXPORT_SYMBOL(sparc64_get_clock_tick);
 
 /* semaphores */
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c
index 8dcbfbf..b43de64 100644
--- a/arch/sparc64/kernel/time.c
+++ b/arch/sparc64/kernel/time.c
@@ -788,12 +788,15 @@
 	if (!regs)
 		return -ENOMEM;
 
+#ifdef CONFIG_PCI
 	if (!strcmp(model, "ds1287") ||
 	    !strcmp(model, "m5819") ||
 	    !strcmp(model, "m5819p") ||
 	    !strcmp(model, "m5823")) {
 		ds1287_regs = (unsigned long) regs;
-	} else if (model[5] == '0' && model[6] == '2') {
+	} else
+#endif
+	if (model[5] == '0' && model[6] == '2') {
 		mstk48t02_regs = regs;
 	} else if(model[5] == '0' && model[6] == '8') {
 		mstk48t08_regs = regs;
diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c
index 1959654..d2150ba 100644
--- a/drivers/net/8139cp.c
+++ b/drivers/net/8139cp.c
@@ -1836,9 +1836,10 @@
 
 	if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
 	    pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev < 0x20) {
-		printk(KERN_ERR PFX "pci dev %s (id %04x:%04x rev %02x) is not an 8139C+ compatible chip\n",
-		       pci_name(pdev), pdev->vendor, pdev->device, pci_rev);
-		printk(KERN_ERR PFX "Try the \"8139too\" driver instead.\n");
+		dev_err(&pdev->dev,
+			   "This (id %04x:%04x rev %02x) is not an 8139C+ compatible chip\n",
+		           pdev->vendor, pdev->device, pci_rev);
+		dev_err(&pdev->dev, "Try the \"8139too\" driver instead.\n");
 		return -ENODEV;
 	}
 
@@ -1876,14 +1877,13 @@
 	pciaddr = pci_resource_start(pdev, 1);
 	if (!pciaddr) {
 		rc = -EIO;
-		printk(KERN_ERR PFX "no MMIO resource for pci dev %s\n",
-		       pci_name(pdev));
+		dev_err(&pdev->dev, "no MMIO resource\n");
 		goto err_out_res;
 	}
 	if (pci_resource_len(pdev, 1) < CP_REGS_SIZE) {
 		rc = -EIO;
-		printk(KERN_ERR PFX "MMIO resource (%llx) too small on pci dev %s\n",
-		       (unsigned long long)pci_resource_len(pdev, 1), pci_name(pdev));
+		dev_err(&pdev->dev, "MMIO resource (%llx) too small\n",
+		       (unsigned long long)pci_resource_len(pdev, 1));
 		goto err_out_res;
 	}
 
@@ -1897,14 +1897,15 @@
 
 		rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
 		if (rc) {
-			printk(KERN_ERR PFX "No usable DMA configuration, "
-			       "aborting.\n");
+			dev_err(&pdev->dev,
+				   "No usable DMA configuration, aborting.\n");
 			goto err_out_res;
 		}
 		rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
 		if (rc) {
-			printk(KERN_ERR PFX "No usable consistent DMA configuration, "
-			       "aborting.\n");
+			dev_err(&pdev->dev,
+				   "No usable consistent DMA configuration, "
+			           "aborting.\n");
 			goto err_out_res;
 		}
 	}
@@ -1915,9 +1916,9 @@
 	regs = ioremap(pciaddr, CP_REGS_SIZE);
 	if (!regs) {
 		rc = -EIO;
-		printk(KERN_ERR PFX "Cannot map PCI MMIO (%llx@%llx) on pci dev %s\n",
-			(unsigned long long)pci_resource_len(pdev, 1),
-			(unsigned long long)pciaddr, pci_name(pdev));
+		dev_err(&pdev->dev, "Cannot map PCI MMIO (%lx@%lx)\n",
+		       (unsigned long long)pci_resource_len(pdev, 1),
+		       (unsigned long long)pciaddr);
 		goto err_out_res;
 	}
 	dev->base_addr = (unsigned long) regs;
@@ -1986,7 +1987,8 @@
 	/* enable busmastering and memory-write-invalidate */
 	pci_set_master(pdev);
 
-	if (cp->wol_enabled) cp_set_d3_state (cp);
+	if (cp->wol_enabled)
+		cp_set_d3_state (cp);
 
 	return 0;
 
@@ -2011,7 +2013,8 @@
 	BUG_ON(!dev);
 	unregister_netdev(dev);
 	iounmap(cp->regs);
-	if (cp->wol_enabled) pci_set_power_state (pdev, PCI_D0);
+	if (cp->wol_enabled)
+		pci_set_power_state (pdev, PCI_D0);
 	pci_release_regions(pdev);
 	pci_clear_mwi(pdev);
 	pci_disable_device(pdev);
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c
index 717506b..cd97185 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/8139too.c
@@ -768,7 +768,7 @@
 	/* dev and priv zeroed in alloc_etherdev */
 	dev = alloc_etherdev (sizeof (*tp));
 	if (dev == NULL) {
-		printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev));
+		dev_err(&pdev->dev, "Unable to alloc new net device\n");
 		return -ENOMEM;
 	}
 	SET_MODULE_OWNER(dev);
@@ -800,31 +800,31 @@
 #ifdef USE_IO_OPS
 	/* make sure PCI base addr 0 is PIO */
 	if (!(pio_flags & IORESOURCE_IO)) {
-		printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev));
+		dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n");
 		rc = -ENODEV;
 		goto err_out;
 	}
 	/* check for weird/broken PCI region reporting */
 	if (pio_len < RTL_MIN_IO_SIZE) {
-		printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev));
+		dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n");
 		rc = -ENODEV;
 		goto err_out;
 	}
 #else
 	/* make sure PCI base addr 1 is MMIO */
 	if (!(mmio_flags & IORESOURCE_MEM)) {
-		printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev));
+		dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n");
 		rc = -ENODEV;
 		goto err_out;
 	}
 	if (mmio_len < RTL_MIN_IO_SIZE) {
-		printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev));
+		dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n");
 		rc = -ENODEV;
 		goto err_out;
 	}
 #endif
 
-	rc = pci_request_regions (pdev, "8139too");
+	rc = pci_request_regions (pdev, DRV_NAME);
 	if (rc)
 		goto err_out;
 	disable_dev_on_err = 1;
@@ -835,7 +835,7 @@
 #ifdef USE_IO_OPS
 	ioaddr = ioport_map(pio_start, pio_len);
 	if (!ioaddr) {
-		printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev));
+		dev_err(&pdev->dev, "cannot map PIO, aborting\n");
 		rc = -EIO;
 		goto err_out;
 	}
@@ -846,7 +846,7 @@
 	/* ioremap MMIO region */
 	ioaddr = pci_iomap(pdev, 1, 0);
 	if (ioaddr == NULL) {
-		printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev));
+		dev_err(&pdev->dev, "cannot remap MMIO, aborting\n");
 		rc = -EIO;
 		goto err_out;
 	}
@@ -860,8 +860,7 @@
 
 	/* check for missing/broken hardware */
 	if (RTL_R32 (TxConfig) == 0xFFFFFFFF) {
-		printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n",
-			pci_name(pdev));
+		dev_err(&pdev->dev, "Chip not responding, ignoring board\n");
 		rc = -EIO;
 		goto err_out;
 	}
@@ -875,9 +874,10 @@
 		}
 
 	/* if unknown chip, assume array element #0, original RTL-8139 in this case */
-	printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n",
-		pci_name(pdev));
-	printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig));
+	dev_printk (KERN_DEBUG, &pdev->dev,
+		    "unknown chip version, assuming RTL-8139\n");
+	dev_printk (KERN_DEBUG, &pdev->dev,
+		    "TxConfig = 0x%lx\n", RTL_R32 (TxConfig));
 	tp->chipset = 0;
 
 match:
@@ -954,9 +954,11 @@
 
 	if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
 	    pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) {
-		printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
-		       pci_name(pdev), pdev->vendor, pdev->device, pci_rev);
-		printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n");
+		dev_info(&pdev->dev,
+			   "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
+		       	   pdev->vendor, pdev->device, pci_rev);
+		dev_info(&pdev->dev,
+			   "Use the \"8139cp\" driver for improved performance and stability.\n");
 	}
 
 	i = rtl8139_init_board (pdev, &dev);
diff --git a/drivers/net/b44.c b/drivers/net/b44.c
index cd98d31..bea0fc0 100644
--- a/drivers/net/b44.c
+++ b/drivers/net/b44.c
@@ -2120,13 +2120,14 @@
 
 	err = pci_enable_device(pdev);
 	if (err) {
-		printk(KERN_ERR PFX "Cannot enable PCI device, "
+		dev_err(&pdev->dev, "Cannot enable PCI device, "
 		       "aborting.\n");
 		return err;
 	}
 
 	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-		printk(KERN_ERR PFX "Cannot find proper PCI device "
+		dev_err(&pdev->dev,
+			"Cannot find proper PCI device "
 		       "base address, aborting.\n");
 		err = -ENODEV;
 		goto err_out_disable_pdev;
@@ -2134,8 +2135,8 @@
 
 	err = pci_request_regions(pdev, DRV_MODULE_NAME);
 	if (err) {
-		printk(KERN_ERR PFX "Cannot obtain PCI resources, "
-		       "aborting.\n");
+		dev_err(&pdev->dev,
+			"Cannot obtain PCI resources, aborting.\n");
 		goto err_out_disable_pdev;
 	}
 
@@ -2143,15 +2144,13 @@
 
 	err = pci_set_dma_mask(pdev, (u64) B44_DMA_MASK);
 	if (err) {
-		printk(KERN_ERR PFX "No usable DMA configuration, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n");
 		goto err_out_free_res;
 	}
 
 	err = pci_set_consistent_dma_mask(pdev, (u64) B44_DMA_MASK);
 	if (err) {
-		printk(KERN_ERR PFX "No usable DMA configuration, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n");
 		goto err_out_free_res;
 	}
 
@@ -2160,7 +2159,7 @@
 
 	dev = alloc_etherdev(sizeof(*bp));
 	if (!dev) {
-		printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n");
+		dev_err(&pdev->dev, "Etherdev alloc failed, aborting.\n");
 		err = -ENOMEM;
 		goto err_out_free_res;
 	}
@@ -2181,8 +2180,7 @@
 
 	bp->regs = ioremap(b44reg_base, b44reg_len);
 	if (bp->regs == 0UL) {
-		printk(KERN_ERR PFX "Cannot map device registers, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "Cannot map device registers, aborting.\n");
 		err = -ENOMEM;
 		goto err_out_free_dev;
 	}
@@ -2212,8 +2210,8 @@
 
 	err = b44_get_invariants(bp);
 	if (err) {
-		printk(KERN_ERR PFX "Problem fetching invariants of chip, "
-		       "aborting.\n");
+		dev_err(&pdev->dev,
+			"Problem fetching invariants of chip, aborting.\n");
 		goto err_out_iounmap;
 	}
 
@@ -2233,8 +2231,7 @@
 
 	err = register_netdev(dev);
 	if (err) {
-		printk(KERN_ERR PFX "Cannot register net device, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
 		goto err_out_iounmap;
 	}
 
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 4f4db5a..64b6a72 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -5575,20 +5575,20 @@
 	/* enable device (incl. PCI PM wakeup), and bus-mastering */
 	rc = pci_enable_device(pdev);
 	if (rc) {
-		printk(KERN_ERR PFX "Cannot enable PCI device, aborting.");
+		dev_err(&pdev->dev, "Cannot enable PCI device, aborting.");
 		goto err_out;
 	}
 
 	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-		printk(KERN_ERR PFX "Cannot find PCI device base address, "
-		       "aborting.\n");
+		dev_err(&pdev->dev,
+			"Cannot find PCI device base address, aborting.\n");
 		rc = -ENODEV;
 		goto err_out_disable;
 	}
 
 	rc = pci_request_regions(pdev, DRV_MODULE_NAME);
 	if (rc) {
-		printk(KERN_ERR PFX "Cannot obtain PCI resources, aborting.\n");
+		dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting.\n");
 		goto err_out_disable;
 	}
 
@@ -5596,15 +5596,15 @@
 
 	bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
 	if (bp->pm_cap == 0) {
-		printk(KERN_ERR PFX "Cannot find power management capability, "
-			       "aborting.\n");
+		dev_err(&pdev->dev,
+			"Cannot find power management capability, aborting.\n");
 		rc = -EIO;
 		goto err_out_release;
 	}
 
 	bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX);
 	if (bp->pcix_cap == 0) {
-		printk(KERN_ERR PFX "Cannot find PCIX capability, aborting.\n");
+		dev_err(&pdev->dev, "Cannot find PCIX capability, aborting.\n");
 		rc = -EIO;
 		goto err_out_release;
 	}
@@ -5612,14 +5612,14 @@
 	if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) {
 		bp->flags |= USING_DAC_FLAG;
 		if (pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK) != 0) {
-			printk(KERN_ERR PFX "pci_set_consistent_dma_mask "
-			       "failed, aborting.\n");
+			dev_err(&pdev->dev,
+				"pci_set_consistent_dma_mask failed, aborting.\n");
 			rc = -EIO;
 			goto err_out_release;
 		}
 	}
 	else if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) {
-		printk(KERN_ERR PFX "System does not support DMA, aborting.\n");
+		dev_err(&pdev->dev, "System does not support DMA, aborting.\n");
 		rc = -EIO;
 		goto err_out_release;
 	}
@@ -5639,7 +5639,7 @@
 	bp->regview = ioremap_nocache(dev->base_addr, mem_len);
 
 	if (!bp->regview) {
-		printk(KERN_ERR PFX "Cannot map register space, aborting.\n");
+		dev_err(&pdev->dev, "Cannot map register space, aborting.\n");
 		rc = -ENOMEM;
 		goto err_out_release;
 	}
@@ -5711,8 +5711,8 @@
 	else if ((CHIP_ID(bp) == CHIP_ID_5706_A1) &&
 		!(bp->flags & PCIX_FLAG)) {
 
-		printk(KERN_ERR PFX "5706 A1 can only be used in a PCIX bus, "
-		       "aborting.\n");
+		dev_err(&pdev->dev,
+			"5706 A1 can only be used in a PCIX bus, aborting.\n");
 		goto err_out_unmap;
 	}
 
@@ -5733,7 +5733,7 @@
 
 	if ((reg & BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK) !=
 	    BNX2_DEV_INFO_SIGNATURE_MAGIC) {
-		printk(KERN_ERR PFX "Firmware not running, aborting.\n");
+		dev_err(&pdev->dev, "Firmware not running, aborting.\n");
 		rc = -ENODEV;
 		goto err_out_unmap;
 	}
@@ -5895,7 +5895,7 @@
 #endif
 
 	if ((rc = register_netdev(dev))) {
-		printk(KERN_ERR PFX "Cannot register net device\n");
+		dev_err(&pdev->dev, "Cannot register net device\n");
 		if (bp->regview)
 			iounmap(bp->regview);
 		pci_release_regions(pdev);
diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c
index d33130f..a31544c 100644
--- a/drivers/net/cassini.c
+++ b/drivers/net/cassini.c
@@ -4887,13 +4887,12 @@
 
 	err = pci_enable_device(pdev);
 	if (err) {
-		printk(KERN_ERR PFX "Cannot enable PCI device, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "Cannot enable PCI device, aborting.\n");
 		return err;
 	}
 
 	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-		printk(KERN_ERR PFX "Cannot find proper PCI device "
+		dev_err(&pdev->dev, "Cannot find proper PCI device "
 		       "base address, aborting.\n");
 		err = -ENODEV;
 		goto err_out_disable_pdev;
@@ -4901,7 +4900,7 @@
 
 	dev = alloc_etherdev(sizeof(*cp));
 	if (!dev) {
-		printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n");
+		dev_err(&pdev->dev, "Etherdev alloc failed, aborting.\n");
 		err = -ENOMEM;
 		goto err_out_disable_pdev;
 	}
@@ -4910,8 +4909,7 @@
 
 	err = pci_request_regions(pdev, dev->name);
 	if (err) {
-		printk(KERN_ERR PFX "Cannot obtain PCI resources, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting.\n");
 		goto err_out_free_netdev;
 	}
 	pci_set_master(pdev);
@@ -4941,7 +4939,7 @@
 		if (pci_write_config_byte(pdev, 
 					  PCI_CACHE_LINE_SIZE, 
 					  cas_cacheline_size)) {
-			printk(KERN_ERR PFX "Could not set PCI cache "
+			dev_err(&pdev->dev, "Could not set PCI cache "
 			       "line size\n");
 			goto err_write_cacheline;
 		}
@@ -4955,7 +4953,7 @@
 		err = pci_set_consistent_dma_mask(pdev,
 						  DMA_64BIT_MASK);
 		if (err < 0) {
-			printk(KERN_ERR PFX "Unable to obtain 64-bit DMA "
+			dev_err(&pdev->dev, "Unable to obtain 64-bit DMA "
 			       "for consistent allocations\n");
 			goto err_out_free_res;
 		}
@@ -4963,7 +4961,7 @@
 	} else {
 		err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
 		if (err) {
-			printk(KERN_ERR PFX "No usable DMA configuration, "
+			dev_err(&pdev->dev, "No usable DMA configuration, "
 			       "aborting.\n");
 			goto err_out_free_res;
 		}
@@ -5023,8 +5021,7 @@
 	/* give us access to cassini registers */
 	cp->regs = pci_iomap(pdev, 0, casreg_len);
 	if (cp->regs == 0UL) {
-		printk(KERN_ERR PFX "Cannot map device registers, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "Cannot map device registers, aborting.\n");
 		goto err_out_free_res;
 	}
 	cp->casreg_len = casreg_len;
@@ -5040,8 +5037,7 @@
 		pci_alloc_consistent(pdev, sizeof(struct cas_init_block),
 				     &cp->block_dvma);
 	if (!cp->init_block) {
-		printk(KERN_ERR PFX "Cannot allocate init block, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "Cannot allocate init block, aborting.\n");
 		goto err_out_iounmap;
 	}
 
@@ -5085,8 +5081,7 @@
 		dev->features |= NETIF_F_HIGHDMA;
 
 	if (register_netdev(dev)) {
-		printk(KERN_ERR PFX "Cannot register net device, "
-		       "aborting.\n");
+		dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
 		goto err_out_free_consistent;
 	}
 
diff --git a/drivers/net/declance.c b/drivers/net/declance.c
index 2038ca7..6ad5796 100644
--- a/drivers/net/declance.c
+++ b/drivers/net/declance.c
@@ -703,8 +703,8 @@
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t
-lance_interrupt(const int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t lance_interrupt(const int irq, void *dev_id,
+				   struct pt_regs *regs)
 {
 	struct net_device *dev = (struct net_device *) dev_id;
 	struct lance_private *lp = netdev_priv(dev);
@@ -1253,7 +1253,7 @@
 	return 0;
 
 err_out_free_dev:
-	kfree(dev);
+	free_netdev(dev);
 
 err_out:
 	return ret;
@@ -1299,6 +1299,7 @@
 	while (root_lance_dev) {
 		struct net_device *dev = root_lance_dev;
 		struct lance_private *lp = netdev_priv(dev);
+
 		unregister_netdev(dev);
 #ifdef CONFIG_TC
 		if (lp->slot >= 0)
diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c
index 4b6ddb7..402961e 100644
--- a/drivers/net/dl2k.c
+++ b/drivers/net/dl2k.c
@@ -9,49 +9,10 @@
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
 */
-/*
-    Rev		Date		Description
-    ==========================================================================
-    0.01	2001/05/03	Created DL2000-based linux driver
-    0.02	2001/05/21	Added VLAN and hardware checksum support.
-    1.00	2001/06/26	Added jumbo frame support.
-    1.01	2001/08/21	Added two parameters, rx_coalesce and rx_timeout.
-    1.02	2001/10/08	Supported fiber media.
-    				Added flow control parameters.
-    1.03	2001/10/12	Changed the default media to 1000mbps_fd for 
-    				the fiber devices.
-    1.04	2001/11/08	Fixed Tx stopped when tx very busy.
-    1.05	2001/11/22	Fixed Tx stopped when unidirectional tx busy.
-    1.06	2001/12/13	Fixed disconnect bug at 10Mbps mode.
-    				Fixed tx_full flag incorrect.
-				Added tx_coalesce paramter.
-    1.07	2002/01/03	Fixed miscount of RX frame error.
-    1.08	2002/01/17	Fixed the multicast bug.
-    1.09	2002/03/07	Move rx-poll-now to re-fill loop.	
-    				Added rio_timer() to watch rx buffers. 
-    1.10	2002/04/16	Fixed miscount of carrier error.
-    1.11	2002/05/23	Added ISR schedule scheme
-    				Fixed miscount of rx frame error for DGE-550SX.
-    				Fixed VLAN bug.
-    1.12	2002/06/13	Lock tx_coalesce=1 on 10/100Mbps mode.
-    1.13	2002/08/13	1. Fix disconnection (many tx:carrier/rx:frame
-    				   errs) with some mainboards.
-    				2. Use definition "DRV_NAME" "DRV_VERSION" 
-				   "DRV_RELDATE" for flexibility.	
-    1.14	2002/08/14	Support ethtool.	
-    1.15	2002/08/27	Changed the default media to Auto-Negotiation
-				for the fiber devices.    
-    1.16	2002/09/04      More power down time for fiber devices auto-
-    				negotiation.
-				Fix disconnect bug after ifup and ifdown.
-    1.17	2002/10/03	Fix RMON statistics overflow. 
-			     	Always use I/O mapping to access eeprom, 
-				avoid system freezing with some chipsets.
 
-*/
 #define DRV_NAME	"D-Link DL2000-based linux driver"
-#define DRV_VERSION	"v1.17b"
-#define DRV_RELDATE	"2006/03/10"
+#define DRV_VERSION	"v1.18"
+#define DRV_RELDATE	"2006/06/27"
 #include "dl2k.h"
 #include <linux/dma-mapping.h>
 
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
index 2ad3275..e445988 100644
--- a/drivers/net/eepro100.c
+++ b/drivers/net/eepro100.c
@@ -555,12 +555,12 @@
 
 	if (!request_region(pci_resource_start(pdev, 1),
 			pci_resource_len(pdev, 1), "eepro100")) {
-		printk (KERN_ERR "eepro100: cannot reserve I/O ports\n");
+		dev_err(&pdev->dev, "eepro100: cannot reserve I/O ports\n");
 		goto err_out_none;
 	}
 	if (!request_mem_region(pci_resource_start(pdev, 0),
 			pci_resource_len(pdev, 0), "eepro100")) {
-		printk (KERN_ERR "eepro100: cannot reserve MMIO region\n");
+		dev_err(&pdev->dev, "eepro100: cannot reserve MMIO region\n");
 		goto err_out_free_pio_region;
 	}
 
@@ -573,7 +573,7 @@
 
 	ioaddr = pci_iomap(pdev, pci_bar, 0);
 	if (!ioaddr) {
-		printk (KERN_ERR "eepro100: cannot remap IO\n");
+		dev_err(&pdev->dev, "eepro100: cannot remap IO\n");
 		goto err_out_free_mmio_region;
 	}
 
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 9f3e09a..a67650cc 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -19,62 +19,15 @@
 
 	Information and updates available at
 	http://www.scyld.com/network/epic100.html
+	[this link no longer provides anything useful -jgarzik]
 
 	---------------------------------------------------------------------
 
-	Linux kernel-specific changes:
-
-	LK1.1.2 (jgarzik):
-	* Merge becker version 1.09 (4/08/2000)
-
-	LK1.1.3:
-	* Major bugfix to 1.09 driver (Francis Romieu)
-
-	LK1.1.4 (jgarzik):
-	* Merge becker test version 1.09 (5/29/2000)
-
-	LK1.1.5:
-	* Fix locking (jgarzik)
-	* Limit 83c175 probe to ethernet-class PCI devices (rgooch)
-
-	LK1.1.6:
-	* Merge becker version 1.11
-	* Move pci_enable_device before any PCI BAR len checks
-
-	LK1.1.7:
-	* { fill me in }
-
-	LK1.1.8:
-	* ethtool driver info support (jgarzik)
-
-	LK1.1.9:
-	* ethtool media get/set support (jgarzik)
-
-	LK1.1.10:
-	* revert MII transceiver init change (jgarzik)
-
-	LK1.1.11:
-	* implement ETHTOOL_[GS]SET, _NWAY_RST, _[GS]MSGLVL, _GLINK (jgarzik)
-	* replace some MII-related magic numbers with constants
-
-	LK1.1.12:
-	* fix power-up sequence
-
-	LK1.1.13:
-	* revert version 1.1.12, power-up sequence "fix"
-
-	LK1.1.14 (Kryzsztof Halasa):
-	* fix spurious bad initializations
-	* pound phy a la SMSC's app note on the subject
-
-	AC1.1.14ac
-	* fix power up/down for ethtool that broke in 1.11
-
 */
 
 #define DRV_NAME        "epic100"
-#define DRV_VERSION     "1.11+LK1.1.14+AC1.1.14"
-#define DRV_RELDATE     "June 2, 2004"
+#define DRV_VERSION     "2.0"
+#define DRV_RELDATE     "June 27, 2006"
 
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
@@ -204,19 +157,15 @@
 
 struct epic_chip_info {
 	const char *name;
-        int io_size;                            /* Needed for I/O region check or ioremap(). */
         int drv_flags;                          /* Driver use, intended as capability flags. */
 };
 
 
 /* indexed by chip_t */
 static const struct epic_chip_info pci_id_tbl[] = {
-	{ "SMSC EPIC/100 83c170",
-	  EPIC_TOTAL_SIZE, TYPE2_INTR | NO_MII | MII_PWRDWN },
-	{ "SMSC EPIC/100 83c170",
-	  EPIC_TOTAL_SIZE, TYPE2_INTR },
-	{ "SMSC EPIC/C 83c175",
-	  EPIC_TOTAL_SIZE, TYPE2_INTR | MII_PWRDWN },
+	{ "SMSC EPIC/100 83c170",	TYPE2_INTR | NO_MII | MII_PWRDWN },
+	{ "SMSC EPIC/100 83c170",	TYPE2_INTR },
+	{ "SMSC EPIC/C 83c175",		TYPE2_INTR | MII_PWRDWN },
 };
 
 
@@ -385,8 +334,8 @@
 		goto out;
 	irq = pdev->irq;
 
-	if (pci_resource_len(pdev, 0) < pci_id_tbl[chip_idx].io_size) {
-		printk (KERN_ERR "card %d: no PCI region space\n", card_idx);
+	if (pci_resource_len(pdev, 0) < EPIC_TOTAL_SIZE) {
+		dev_err(&pdev->dev, "no PCI region space\n");
 		ret = -ENODEV;
 		goto err_out_disable;
 	}
@@ -401,7 +350,7 @@
 
 	dev = alloc_etherdev(sizeof (*ep));
 	if (!dev) {
-		printk (KERN_ERR "card %d: no memory for eth device\n", card_idx);
+		dev_err(&pdev->dev, "no memory for eth device\n");
 		goto err_out_free_res;
 	}
 	SET_MODULE_OWNER(dev);
@@ -413,7 +362,7 @@
 	ioaddr = pci_resource_start (pdev, 1);
 	ioaddr = (long) ioremap (ioaddr, pci_resource_len (pdev, 1));
 	if (!ioaddr) {
-		printk (KERN_ERR DRV_NAME " %d: ioremap failed\n", card_idx);
+		dev_err(&pdev->dev, "ioremap failed\n");
 		goto err_out_free_netdev;
 	}
 #endif
@@ -473,8 +422,7 @@
 		((u16 *)dev->dev_addr)[i] = le16_to_cpu(inw(ioaddr + LAN0 + i*4));
 
 	if (debug > 2) {
-		printk(KERN_DEBUG DRV_NAME "(%s): EEPROM contents\n",
-		       pci_name(pdev));
+		dev_printk(KERN_DEBUG, &pdev->dev, "EEPROM contents:\n");
 		for (i = 0; i < 64; i++)
 			printk(" %4.4x%s", read_eeprom(ioaddr, i),
 				   i % 16 == 15 ? "\n" : "");
@@ -496,21 +444,23 @@
 			int mii_status = mdio_read(dev, phy, MII_BMSR);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				ep->phys[phy_idx++] = phy;
-				printk(KERN_INFO DRV_NAME "(%s): MII transceiver #%d control "
-					   "%4.4x status %4.4x.\n",
-					   pci_name(pdev), phy, mdio_read(dev, phy, 0), mii_status);
+				dev_info(&pdev->dev,
+					"MII transceiver #%d control "
+					"%4.4x status %4.4x.\n",
+					phy, mdio_read(dev, phy, 0), mii_status);
 			}
 		}
 		ep->mii_phy_cnt = phy_idx;
 		if (phy_idx != 0) {
 			phy = ep->phys[0];
 			ep->mii.advertising = mdio_read(dev, phy, MII_ADVERTISE);
-			printk(KERN_INFO DRV_NAME "(%s): Autonegotiation advertising %4.4x link "
+			dev_info(&pdev->dev,
+				"Autonegotiation advertising %4.4x link "
 				   "partner %4.4x.\n",
-				   pci_name(pdev), ep->mii.advertising, mdio_read(dev, phy, 5));
+				   ep->mii.advertising, mdio_read(dev, phy, 5));
 		} else if ( ! (ep->chip_flags & NO_MII)) {
-			printk(KERN_WARNING DRV_NAME "(%s): ***WARNING***: No MII transceiver found!\n",
-			       pci_name(pdev));
+			dev_warn(&pdev->dev,
+				"***WARNING***: No MII transceiver found!\n");
 			/* Use the known PHY address of the EPII. */
 			ep->phys[0] = 3;
 		}
@@ -525,8 +475,7 @@
 	/* The lower four bits are the media type. */
 	if (duplex) {
 		ep->mii.force_media = ep->mii.full_duplex = 1;
-		printk(KERN_INFO DRV_NAME "(%s):  Forced full duplex operation requested.\n",
-		       pci_name(pdev));
+		dev_info(&pdev->dev, "Forced full duplex requested.\n");
 	}
 	dev->if_port = ep->default_port = option;
 
diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c
index c701951..97d34fe 100644
--- a/drivers/net/fealnx.c
+++ b/drivers/net/fealnx.c
@@ -124,7 +124,9 @@
 MODULE_PARM_DESC(options, "fealnx: Bits 0-3: media type, bit 17: full duplex");
 MODULE_PARM_DESC(full_duplex, "fealnx full duplex setting(s) (1)");
 
-#define MIN_REGION_SIZE 136
+enum {
+	MIN_REGION_SIZE		= 136,
+};
 
 /* A chip capabilities table, matching the entries in pci_tbl[] above. */
 enum chip_capability_flags {
@@ -146,14 +148,13 @@
 
 struct chip_info {
 	char *chip_name;
-	int io_size;
 	int flags;
 };
 
-static const struct chip_info skel_netdrv_tbl[] = {
-	{"100/10M Ethernet PCI Adapter", 136, HAS_MII_XCVR},
-	{"100/10M Ethernet PCI Adapter", 136, HAS_CHIP_XCVR},
-	{"1000/100/10M Ethernet PCI Adapter", 136, HAS_MII_XCVR},
+static const struct chip_info skel_netdrv_tbl[] __devinitdata = {
+ 	{ "100/10M Ethernet PCI Adapter",	HAS_MII_XCVR },
+	{ "100/10M Ethernet PCI Adapter",	HAS_CHIP_XCVR },
+	{ "1000/100/10M Ethernet PCI Adapter",	HAS_MII_XCVR },
 };
 
 /* Offsets to the Command and Status Registers. */
@@ -504,13 +505,14 @@
 	
 	len = pci_resource_len(pdev, bar);
 	if (len < MIN_REGION_SIZE) {
-		printk(KERN_ERR "%s: region size %ld too small, aborting\n",
-		       boardname, len);
+		dev_err(&pdev->dev,
+			   "region size %ld too small, aborting\n", len);
 		return -ENODEV;
 	}
 
 	i = pci_request_regions(pdev, boardname);
-	if (i) return i;
+	if (i)
+		return i;
 	
 	irq = pdev->irq;
 
@@ -576,9 +578,9 @@
 
 			if (mii_status != 0xffff && mii_status != 0x0000) {
 				np->phys[phy_idx++] = phy;
-				printk(KERN_INFO
-				       "%s: MII PHY found at address %d, status "
-				       "0x%4.4x.\n", dev->name, phy, mii_status);
+				dev_info(&pdev->dev,
+				       "MII PHY found at address %d, status "
+				       "0x%4.4x.\n", phy, mii_status);
 				/* get phy type */
 				{
 					unsigned int data;
@@ -601,10 +603,10 @@
 		}
 
 		np->mii_cnt = phy_idx;
-		if (phy_idx == 0) {
-			printk(KERN_WARNING "%s: MII PHY not found -- this device may "
-			       "not operate correctly.\n", dev->name);
-		}
+		if (phy_idx == 0)
+			dev_warn(&pdev->dev,
+				"MII PHY not found -- this device may "
+			       "not operate correctly.\n");
 	} else {
 		np->phys[0] = 32;
 /* 89/6/23 add, (begin) */
@@ -630,7 +632,7 @@
 		np->mii.full_duplex = full_duplex[card_idx];
 
 	if (np->mii.full_duplex) {
-		printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
+		dev_info(&pdev->dev, "Media type forced to Full Duplex.\n");
 /* 89/6/13 add, (begin) */
 //      if (np->PHYType==MarvellPHY)
 		if ((np->PHYType == MarvellPHY) || (np->PHYType == LevelOnePHY)) {
diff --git a/drivers/net/gt96100eth.c b/drivers/net/gt96100eth.c
index 49dacc6..2b4db74 100644
--- a/drivers/net/gt96100eth.c
+++ b/drivers/net/gt96100eth.c
@@ -699,7 +699,6 @@
 	memset(gp, 0, sizeof(*gp)); // clear it
 
 	gp->port_num = port_num;
-	gp->io_size = GT96100_ETH_IO_SIZE;
 	gp->port_offset = port_num * GT96100_ETH_IO_SIZE;
 	gp->phy_addr = phy_addr;
 	gp->chip_rev = chip_rev;
@@ -1531,7 +1530,7 @@
 				+ sizeof(gt96100_td_t) * TX_RING_SIZE,
 				gp->rx_ring);
 			free_netdev(gtif->dev);
-			release_region(gtif->iobase, gp->io_size);
+			release_region(gtif->iobase, GT96100_ETH_IO_SIZE);
 		}
 	}
 }
diff --git a/drivers/net/gt96100eth.h b/drivers/net/gt96100eth.h
index 2a833193..3b62a87 100644
--- a/drivers/net/gt96100eth.h
+++ b/drivers/net/gt96100eth.h
@@ -331,7 +331,6 @@
 	mib_counters_t mib;
 	struct net_device_stats stats;
 
-	int io_size;
 	int port_num;  // 0 or 1
 	int chip_rev;
 	u32 port_offset;
@@ -340,7 +339,6 @@
 	u32 last_psr; // last value of the port status register
 
 	int options;     /* User-settable misc. driver options. */
-	int drv_flags;
 	struct timer_list timer;
 	spinlock_t lock; /* Serialise access to device */
 };
diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c
index 7bcd939..409c6aa 100644
--- a/drivers/net/hamachi.c
+++ b/drivers/net/hamachi.c
@@ -20,22 +20,15 @@
 
 	Support and updates available at
 	http://www.scyld.com/network/hamachi.html
+	[link no longer provides useful info -jgarzik]
 	or
 	http://www.parl.clemson.edu/~keithu/hamachi.html
 
-
-
-	Linux kernel changelog:
-
-	LK1.0.1:
-	- fix lack of pci_dev<->dev association
-	- ethtool support (jgarzik)
-
 */
 
 #define DRV_NAME	"hamachi"
-#define DRV_VERSION	"1.01+LK1.0.1"
-#define DRV_RELDATE	"5/18/2001"
+#define DRV_VERSION	"2.0"
+#define DRV_RELDATE	"June 27, 2006"
 
 
 /* A few user-configurable values. */
@@ -608,7 +601,8 @@
 	pci_set_master(pdev);
 
 	i = pci_request_regions(pdev, DRV_NAME);
-	if (i) return i;
+	if (i)
+		return i;
 
 	irq = pdev->irq;
 	ioaddr = ioremap(base, 0x400);
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c
index 72aad42..f4c8fd3 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/myri10ge/myri10ge.c
@@ -188,7 +188,6 @@
 	int vendor_specific_offset;
 	u32 devctl;
 	u16 msi_flags;
-	u32 pm_state[16];
 	u32 read_dma;
 	u32 write_dma;
 	u32 read_write_dma;
@@ -1289,6 +1288,7 @@
 	"tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors",
 	"tx_heartbeat_errors", "tx_window_errors",
 	/* device-specific stats */
+	"tx_boundary", "WC", "irq", "MSI",
 	"read_dma_bw_MBs", "write_dma_bw_MBs", "read_write_dma_bw_MBs",
 	"serial_number", "tx_pkt_start", "tx_pkt_done",
 	"tx_req", "tx_done", "rx_small_cnt", "rx_big_cnt",
@@ -1327,6 +1327,10 @@
 	for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++)
 		data[i] = ((unsigned long *)&mgp->stats)[i];
 
+	data[i++] = (unsigned int)mgp->tx.boundary;
+	data[i++] = (unsigned int)(mgp->mtrr >= 0);
+	data[i++] = (unsigned int)mgp->pdev->irq;
+	data[i++] = (unsigned int)mgp->msi_enabled;
 	data[i++] = (unsigned int)mgp->read_dma;
 	data[i++] = (unsigned int)mgp->write_dma;
 	data[i++] = (unsigned int)mgp->read_write_dma;
@@ -2197,8 +2201,6 @@
  * any other device, except if forced with myri10ge_ecrc_enable > 1.
  */
 
-#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_PCIE	0x005d
-
 static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
 {
 	struct pci_dev *bridge = mgp->pdev->bus->self;
@@ -2737,11 +2739,10 @@
 		dev_err(&pdev->dev, "register_netdev failed: %d\n", status);
 		goto abort_with_irq;
 	}
-
-	printk(KERN_INFO "myri10ge: %s: %s IRQ %d, tx bndry %d, fw %s, WC %s\n",
-	       netdev->name, (mgp->msi_enabled ? "MSI" : "xPIC"),
-	       pdev->irq, mgp->tx.boundary, mgp->fw_name,
-	       (mgp->mtrr >= 0 ? "Enabled" : "Disabled"));
+	dev_info(dev, "%s IRQ %d, tx bndry %d, fw %s, WC %s\n",
+		 (mgp->msi_enabled ? "MSI" : "xPIC"),
+		 pdev->irq, mgp->tx.boundary, mgp->fw_name,
+		 (mgp->mtrr >= 0 ? "Enabled" : "Disabled"));
 
 	return 0;
 
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 9df2628..db0475a 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -20,120 +20,9 @@
 
 	Support information and updates available at
 	http://www.scyld.com/network/netsemi.html
+	[link no longer provides useful info -jgarzik]
 
 
-	Linux kernel modifications:
-
-	Version 1.0.1:
-		- Spinlock fixes
-		- Bug fixes and better intr performance (Tjeerd)
-	Version 1.0.2:
-		- Now reads correct MAC address from eeprom
-	Version 1.0.3:
-		- Eliminate redundant priv->tx_full flag
-		- Call netif_start_queue from dev->tx_timeout
-		- wmb() in start_tx() to flush data
-		- Update Tx locking
-		- Clean up PCI enable (davej)
-	Version 1.0.4:
-		- Merge Donald Becker's natsemi.c version 1.07
-	Version 1.0.5:
-		- { fill me in }
-	Version 1.0.6:
-		* ethtool support (jgarzik)
-		* Proper initialization of the card (which sometimes
-		fails to occur and leaves the card in a non-functional
-		state). (uzi)
-
-		* Some documented register settings to optimize some
-		of the 100Mbit autodetection circuitry in rev C cards. (uzi)
-
-		* Polling of the PHY intr for stuff like link state
-		change and auto- negotiation to finally work properly. (uzi)
-
-		* One-liner removal of a duplicate declaration of
-		netdev_error(). (uzi)
-
-	Version 1.0.7: (Manfred Spraul)
-		* pci dma
-		* SMP locking update
-		* full reset added into tx_timeout
-		* correct multicast hash generation (both big and little endian)
-			[copied from a natsemi driver version
-			 from Myrio Corporation, Greg Smith]
-		* suspend/resume
-
-	version 1.0.8 (Tim Hockin <thockin@sun.com>)
-		* ETHTOOL_* support
-		* Wake on lan support (Erik Gilling)
-		* MXDMA fixes for serverworks
-		* EEPROM reload
-
-	version 1.0.9 (Manfred Spraul)
-		* Main change: fix lack of synchronize
-		netif_close/netif_suspend against a last interrupt
-		or packet.
-		* do not enable superflous interrupts (e.g. the
-		drivers relies on TxDone - TxIntr not needed)
-		* wait that the hardware has really stopped in close
-		and suspend.
-		* workaround for the (at least) gcc-2.95.1 compiler
-		problem. Also simplifies the code a bit.
-		* disable_irq() in tx_timeout - needed to protect
-		against rx interrupts.
-		* stop the nic before switching into silent rx mode
-		for wol (required according to docu).
-
-	version 1.0.10:
-		* use long for ee_addr (various)
-		* print pointers properly (DaveM)
-		* include asm/irq.h (?)
-
-	version 1.0.11:
-		* check and reset if PHY errors appear (Adrian Sun)
-		* WoL cleanup (Tim Hockin)
-		* Magic number cleanup (Tim Hockin)
-		* Don't reload EEPROM on every reset (Tim Hockin)
-		* Save and restore EEPROM state across reset (Tim Hockin)
-		* MDIO Cleanup (Tim Hockin)
-		* Reformat register offsets/bits (jgarzik)
-
-	version 1.0.12:
-		* ETHTOOL_* further support (Tim Hockin)
-
-	version 1.0.13:
-		* ETHTOOL_[G]EEPROM support (Tim Hockin)
-
-	version 1.0.13:
-		* crc cleanup (Matt Domsch <Matt_Domsch@dell.com>)
-
-	version 1.0.14:
-		* Cleanup some messages and autoneg in ethtool (Tim Hockin)
-
-	version 1.0.15:
-		* Get rid of cable_magic flag
-		* use new (National provided) solution for cable magic issue
-
-	version 1.0.16:
-		* call netdev_rx() for RxErrors (Manfred Spraul)
-		* formatting and cleanups
-		* change options and full_duplex arrays to be zero
-		  initialized
-		* enable only the WoL and PHY interrupts in wol mode
-
-	version 1.0.17:
-		* only do cable_magic on 83815 and early 83816 (Tim Hockin)
-		* create a function for rx refill (Manfred Spraul)
-		* combine drain_ring and init_ring (Manfred Spraul)
-		* oom handling (Manfred Spraul)
-		* hands_off instead of playing with netif_device_{de,a}ttach
-		  (Manfred Spraul)
-		* be sure to write the MAC back to the chip (Manfred Spraul)
-		* lengthen EEPROM timeout, and always warn about timeouts
-		  (Manfred Spraul)
-		* comments update (Manfred)
-		* do the right thing on a phy-reset (Manfred and Tim)
-
 	TODO:
 	* big endian support with CFG:BEM instead of cpu_to_le32
 */
@@ -165,8 +54,8 @@
 #include <asm/uaccess.h>
 
 #define DRV_NAME	"natsemi"
-#define DRV_VERSION	"1.07+LK1.0.17"
-#define DRV_RELDATE	"Sep 27, 2002"
+#define DRV_VERSION	"2.0"
+#define DRV_RELDATE	"June 27, 2006"
 
 #define RX_OFFSET	2
 
diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c
index fa50eb8..34bdba9 100644
--- a/drivers/net/ne2k-pci.c
+++ b/drivers/net/ne2k-pci.c
@@ -231,12 +231,12 @@
 	irq = pdev->irq;
 
 	if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) {
-		printk (KERN_ERR PFX "no I/O resource at PCI BAR #0\n");
+		dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n");
 		return -ENODEV;
 	}
 
 	if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) {
-		printk (KERN_ERR PFX "I/O resource 0x%x @ 0x%lx busy\n",
+		dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n",
 			NE_IO_EXTENT, ioaddr);
 		return -EBUSY;
 	}
@@ -263,7 +263,7 @@
 	/* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */
 	dev = alloc_ei_netdev();
 	if (!dev) {
-		printk (KERN_ERR PFX "cannot allocate ethernet device\n");
+		dev_err(&pdev->dev, "cannot allocate ethernet device\n");
 		goto err_out_free_res;
 	}
 	SET_MODULE_OWNER(dev);
@@ -281,7 +281,8 @@
 		while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
 			/* Limit wait: '2' avoids jiffy roll-over. */
 			if (jiffies - reset_start_time > 2) {
-				printk(KERN_ERR PFX "Card failure (no reset ack).\n");
+				dev_err(&pdev->dev,
+					"Card failure (no reset ack).\n");
 				goto err_out_free_netdev;
 			}
 
diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c
index a68bf47..d4be207 100644
--- a/drivers/net/ni5010.c
+++ b/drivers/net/ni5010.c
@@ -1,17 +1,12 @@
 /*	ni5010.c: A network driver for the MiCom-Interlan NI5010 ethercard.
  *
- *	Copyright 1996,1997 Jan-Pascal van Best and Andreas Mohr.
+ *	Copyright 1996,1997,2006 Jan-Pascal van Best and Andreas Mohr.
  *
  *	This software may be used and distributed according to the terms
  *	of the GNU General Public License, incorporated herein by reference.
  *
  * 	The authors may be reached as:
- *		jvbest@wi.leidenuniv.nl		a.mohr@mailto.de
- * 	or by snail mail as
- * 		Jan-Pascal van Best		Andreas Mohr
- *		Klikspaanweg 58-4		Stauferstr. 6
- *		2324 LZ  Leiden			D-71272 Renningen
- *		The Netherlands			Germany
+ *		janpascal@vanbest.org		andi@lisas.de
  *
  *	Sources:
  * 	 	Donald Becker's "skeleton.c"
@@ -27,8 +22,9 @@
  *	970503	v0.93: Fixed auto-irq failure on warm reboot (JB)
  *	970623	v1.00: First kernel version (AM)
  *	970814	v1.01: Added detection of onboard receive buffer size (AM)
+ *	060611	v1.02: slight cleanup: email addresses, driver modernization.
  *	Bugs:
- *		- None known...
+ *		- not SMP-safe (no locking of I/O accesses)
  *		- Note that you have to patch ifconfig for the new /proc/net/dev
  *		format. It gives incorrect stats otherwise.
  *
@@ -39,7 +35,7 @@
  *		Complete merge with Andreas' driver
  *		Implement ring buffers (Is this useful? You can't squeeze
  *			too many packet in a 2k buffer!)
- *		Implement DMA (Again, is this useful? Some docs says DMA is
+ *		Implement DMA (Again, is this useful? Some docs say DMA is
  *			slower than programmed I/O)
  *
  *	Compile with:
@@ -47,7 +43,7 @@
  *			-DMODULE -c ni5010.c 
  *
  *	Insert with e.g.:
- *		insmod ni5010.o io=0x300 irq=5 	
+ *		insmod ni5010.ko io=0x300 irq=5
  */
 
 #include <linux/module.h>
@@ -69,15 +65,15 @@
 
 #include "ni5010.h"
 
-static const char *boardname = "NI5010";
-static char *version =
-	"ni5010.c: v1.00 06/23/97 Jan-Pascal van Best and Andreas Mohr\n";
+static const char boardname[] = "NI5010";
+static char version[] __initdata =
+	"ni5010.c: v1.02 20060611 Jan-Pascal van Best and Andreas Mohr\n";
 	
 /* bufsize_rcv == 0 means autoprobing */
 static unsigned int bufsize_rcv;
 
-#define jumpered_interrupts	/* IRQ line jumpered on board */
-#undef jumpered_dma		/* No DMA used */
+#define JUMPERED_INTERRUPTS	/* IRQ line jumpered on board */
+#undef JUMPERED_DMA		/* No DMA used */
 #undef FULL_IODETECT		/* Only detect in portlist */
 
 #ifndef FULL_IODETECT
@@ -281,7 +277,7 @@
 
 	PRINTK2((KERN_DEBUG "%s: I/O #4 passed!\n", dev->name));
 
-#ifdef jumpered_interrupts
+#ifdef JUMPERED_INTERRUPTS
 	if (dev->irq == 0xff)
 		;
 	else if (dev->irq < 2) {
@@ -305,7 +301,7 @@
 	} else if (dev->irq == 2) {
 		dev->irq = 9;
 	}
-#endif	/* jumpered_irq */
+#endif	/* JUMPERED_INTERRUPTS */
 	PRINTK2((KERN_DEBUG "%s: I/O #9 passed!\n", dev->name));
 
 	/* DMA is not supported (yet?), so no use detecting it */
@@ -334,7 +330,7 @@
         	outw(0, IE_GP);		/* Point GP at start of packet */
         	outb(0, IE_RBUF);	/* set buffer byte 0 to 0 again */
 	}
-        printk("// bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE);
+        printk("-> bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE);
 	memset(dev->priv, 0, sizeof(struct ni5010_local));
 	
 	dev->open		= ni5010_open;
@@ -354,11 +350,9 @@
 	outb(0xff, EDLC_XCLR); 	/* Kill all pending xmt interrupts */
 
 	printk(KERN_INFO "%s: NI5010 found at 0x%x, using IRQ %d", dev->name, ioaddr, dev->irq);
-	if (dev->dma) printk(" & DMA %d", dev->dma);
+	if (dev->dma)
+		printk(" & DMA %d", dev->dma);
 	printk(".\n");
-
-	printk(KERN_INFO "Join the NI5010 driver development team!\n");
-	printk(KERN_INFO "Mail to a.mohr@mailto.de or jvbest@wi.leidenuniv.nl\n");
 	return 0;
 out:
 	release_region(dev->base_addr, NI5010_IO_EXTENT);
@@ -371,7 +365,7 @@
  *
  * This routine should set everything up anew at each open, even
  * registers that "should" only need to be set once at boot, so that
- * there is non-reboot way to recover if something goes wrong.
+ * there is a non-reboot way to recover if something goes wrong.
  */
    
 static int ni5010_open(struct net_device *dev)
@@ -390,13 +384,13 @@
          * Always allocate the DMA channel after the IRQ,
          * and clean up on failure.
          */
-#ifdef jumpered_dma
+#ifdef JUMPERED_DMA
         if (request_dma(dev->dma, cardname)) {
 		printk(KERN_WARNING "%s: Cannot get dma %#2x\n", dev->name, dev->dma);
                 free_irq(dev->irq, NULL);
                 return -EAGAIN;
         }
-#endif	/* jumpered_dma */
+#endif	/* JUMPERED_DMA */
 
 	PRINTK3((KERN_DEBUG "%s: passed open() #2\n", dev->name));
 	/* Reset the hardware here.  Don't forget to set the station address. */
@@ -633,7 +627,7 @@
 	int ioaddr = dev->base_addr;
 
 	PRINTK2((KERN_DEBUG "%s: entering ni5010_close\n", dev->name));
-#ifdef jumpered_interrupts	
+#ifdef JUMPERED_INTERRUPTS
 	free_irq(dev->irq, NULL);
 #endif
 	/* Put card in held-RESET state */
@@ -771,7 +765,7 @@
 MODULE_PARM_DESC(io, "ni5010 I/O base address");
 MODULE_PARM_DESC(irq, "ni5010 IRQ number");
 
-int init_module(void)
+static int __init ni5010_init_module(void)
 {
 	PRINTK2((KERN_DEBUG "%s: entering init_module\n", boardname));
 	/*
@@ -792,13 +786,15 @@
         return 0;
 }
 
-void cleanup_module(void)
+static void __exit ni5010_cleanup_module(void)
 {
 	PRINTK2((KERN_DEBUG "%s: entering cleanup_module\n", boardname));
 	unregister_netdev(dev_ni5010);
 	release_region(dev_ni5010->base_addr, NI5010_IO_EXTENT);
 	free_netdev(dev_ni5010);
 }
+module_init(ni5010_init_module);
+module_exit(ni5010_cleanup_module);
 #endif /* MODULE */
 MODULE_LICENSE("GPL");
 
diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c
index 70429108..0e76859 100644
--- a/drivers/net/ns83820.c
+++ b/drivers/net/ns83820.c
@@ -803,7 +803,7 @@
 
 		writel(dev->IMR_cache, dev->base + IMR);
 		writel(1, dev->base + IER);
-		spin_unlock_irq(&dev->misc_lock);
+		spin_unlock(&dev->misc_lock);
 
 		kick_rx(ndev);
 
@@ -1012,8 +1012,6 @@
 	struct ns83820 *dev = PRIV(ndev);
 	u32 cmdsts, tx_done_idx, *desc;
 
-	spin_lock_irq(&dev->tx_lock);
-
 	dprintk("do_tx_done(%p)\n", ndev);
 	tx_done_idx = dev->tx_done_idx;
 	desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
@@ -1069,7 +1067,6 @@
 		netif_start_queue(ndev);
 		netif_wake_queue(ndev);
 	}
-	spin_unlock_irq(&dev->tx_lock);
 }
 
 static void ns83820_cleanup_tx(struct ns83820 *dev)
@@ -1281,11 +1278,13 @@
 	.get_link = ns83820_get_link
 };
 
+/* this function is called in irq context from the ISR */
 static void ns83820_mib_isr(struct ns83820 *dev)
 {
-	spin_lock(&dev->misc_lock);
+	unsigned long flags;
+	spin_lock_irqsave(&dev->misc_lock, flags);
 	ns83820_update_stats(dev);
-	spin_unlock(&dev->misc_lock);
+	spin_unlock_irqrestore(&dev->misc_lock, flags);
 }
 
 static void ns83820_do_isr(struct net_device *ndev, u32 isr);
@@ -1307,6 +1306,8 @@
 static void ns83820_do_isr(struct net_device *ndev, u32 isr)
 {
 	struct ns83820 *dev = PRIV(ndev);
+	unsigned long flags;
+
 #ifdef DEBUG
 	if (isr & ~(ISR_PHY | ISR_RXDESC | ISR_RXEARLY | ISR_RXOK | ISR_RXERR | ISR_TXIDLE | ISR_TXOK | ISR_TXDESC))
 		Dprintk("odd isr? 0x%08x\n", isr);
@@ -1321,10 +1322,10 @@
 	if ((ISR_RXDESC | ISR_RXOK) & isr) {
 		prefetch(dev->rx_info.next_rx_desc);
 
-		spin_lock_irq(&dev->misc_lock);
+		spin_lock_irqsave(&dev->misc_lock, flags);
 		dev->IMR_cache &= ~(ISR_RXDESC | ISR_RXOK);
 		writel(dev->IMR_cache, dev->base + IMR);
-		spin_unlock_irq(&dev->misc_lock);
+		spin_unlock_irqrestore(&dev->misc_lock, flags);
 
 		tasklet_schedule(&dev->rx_tasklet);
 		//rx_irq(ndev);
@@ -1370,16 +1371,18 @@
 	 * work has accumulated
 	 */
 	if ((ISR_TXDESC | ISR_TXIDLE | ISR_TXOK | ISR_TXERR) & isr) {
+		spin_lock_irqsave(&dev->tx_lock, flags);
 		do_tx_done(ndev);
+		spin_unlock_irqrestore(&dev->tx_lock, flags);
 
 		/* Disable TxOk if there are no outstanding tx packets.
 		 */
 		if ((dev->tx_done_idx == dev->tx_free_idx) &&
 		    (dev->IMR_cache & ISR_TXOK)) {
-			spin_lock_irq(&dev->misc_lock);
+			spin_lock_irqsave(&dev->misc_lock, flags);
 			dev->IMR_cache &= ~ISR_TXOK;
 			writel(dev->IMR_cache, dev->base + IMR);
-			spin_unlock_irq(&dev->misc_lock);
+			spin_unlock_irqrestore(&dev->misc_lock, flags);
 		}
 	}
 
@@ -1390,10 +1393,10 @@
 	 * nature are expected, we must enable TxOk.
 	 */
 	if ((ISR_TXIDLE & isr) && (dev->tx_done_idx != dev->tx_free_idx)) {
-		spin_lock_irq(&dev->misc_lock);
+		spin_lock_irqsave(&dev->misc_lock, flags);
 		dev->IMR_cache |= ISR_TXOK;
 		writel(dev->IMR_cache, dev->base + IMR);
-		spin_unlock_irq(&dev->misc_lock);
+		spin_unlock_irqrestore(&dev->misc_lock, flags);
 	}
 
 	/* MIB interrupt: one of the statistics counters is about to overflow */
@@ -1455,7 +1458,7 @@
         u32 tx_done_idx, *desc;
 	unsigned long flags;
 
-	local_irq_save(flags);
+	spin_lock_irqsave(&dev->tx_lock, flags);
 
 	tx_done_idx = dev->tx_done_idx;
 	desc = dev->tx_descs + (tx_done_idx * DESC_SIZE);
@@ -1482,7 +1485,7 @@
 		ndev->name,
 		tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS]));
 
-	local_irq_restore(flags);
+	spin_unlock_irqrestore(&dev->tx_lock, flags);
 }
 
 static void ns83820_tx_watch(unsigned long data)
@@ -1832,7 +1835,7 @@
 	} else if (!pci_set_dma_mask(pci_dev, DMA_32BIT_MASK)) {
 		using_dac = 0;
 	} else {
-		printk(KERN_WARNING "ns83820.c: pci_set_dma_mask failed!\n");
+		dev_warn(&pci_dev->dev, "pci_set_dma_mask failed!\n");
 		return -ENODEV;
 	}
 
@@ -1855,7 +1858,7 @@
 
 	err = pci_enable_device(pci_dev);
 	if (err) {
-		printk(KERN_INFO "ns83820: pci_enable_dev failed: %d\n", err);
+		dev_info(&pci_dev->dev, "pci_enable_dev failed: %d\n", err);
 		goto out_free;
 	}
 
@@ -1884,8 +1887,8 @@
 	err = request_irq(pci_dev->irq, ns83820_irq, IRQF_SHARED,
 			  DRV_NAME, ndev);
 	if (err) {
-		printk(KERN_INFO "ns83820: unable to register irq %d\n",
-			pci_dev->irq);
+		dev_info(&pci_dev->dev, "unable to register irq %d, err %d\n",
+			pci_dev->irq, err);
 		goto out_disable;
 	}
 
@@ -1899,7 +1902,7 @@
 	rtnl_lock();
 	err = dev_alloc_name(ndev, ndev->name);
 	if (err < 0) {
-		printk(KERN_INFO "ns83820: unable to get netdev name: %d\n", err);
+		dev_info(&pci_dev->dev, "unable to get netdev name: %d\n", err);
 		goto out_free_irq;
 	}
 
diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c
index 3388ee1..e0e2939 100644
--- a/drivers/net/pci-skeleton.c
+++ b/drivers/net/pci-skeleton.c
@@ -601,7 +601,7 @@
 	/* dev zeroed in alloc_etherdev */
 	dev = alloc_etherdev (sizeof (*tp));
 	if (dev == NULL) {
-		printk (KERN_ERR PFX "unable to alloc new ethernet\n");
+		dev_err(&pdev->dev, "unable to alloc new ethernet\n");
 		DPRINTK ("EXIT, returning -ENOMEM\n");
 		return -ENOMEM;
 	}
@@ -631,14 +631,14 @@
 
 	/* make sure PCI base addr 0 is PIO */
 	if (!(pio_flags & IORESOURCE_IO)) {
-		printk (KERN_ERR PFX "region #0 not a PIO resource, aborting\n");
+		dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n");
 		rc = -ENODEV;
 		goto err_out;
 	}
 
 	/* make sure PCI base addr 1 is MMIO */
 	if (!(mmio_flags & IORESOURCE_MEM)) {
-		printk (KERN_ERR PFX "region #1 not an MMIO resource, aborting\n");
+		dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n");
 		rc = -ENODEV;
 		goto err_out;
 	}
@@ -646,12 +646,12 @@
 	/* check for weird/broken PCI region reporting */
 	if ((pio_len < NETDRV_MIN_IO_SIZE) ||
 	    (mmio_len < NETDRV_MIN_IO_SIZE)) {
-		printk (KERN_ERR PFX "Invalid PCI region size(s), aborting\n");
+		dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n");
 		rc = -ENODEV;
 		goto err_out;
 	}
 
-	rc = pci_request_regions (pdev, "pci-skeleton");
+	rc = pci_request_regions (pdev, MODNAME);
 	if (rc)
 		goto err_out;
 
@@ -663,7 +663,7 @@
 	/* ioremap MMIO region */
 	ioaddr = ioremap (mmio_start, mmio_len);
 	if (ioaddr == NULL) {
-		printk (KERN_ERR PFX "cannot remap MMIO, aborting\n");
+		dev_err(&pdev->dev, "cannot remap MMIO, aborting\n");
 		rc = -EIO;
 		goto err_out_free_res;
 	}
@@ -699,9 +699,10 @@
 		}
 
 	/* if unknown chip, assume array element #0, original RTL-8139 in this case */
-	printk (KERN_DEBUG PFX "PCI device %s: unknown chip version, assuming RTL-8139\n",
-		pci_name(pdev));
-	printk (KERN_DEBUG PFX "PCI device %s: TxConfig = 0x%lx\n", pci_name(pdev), NETDRV_R32 (TxConfig));
+	dev_printk (KERN_DEBUG, &pdev->dev,
+		"unknown chip version, assuming RTL-8139\n");
+	dev_printk (KERN_DEBUG, &pdev->dev, "TxConfig = 0x%lx\n",
+		NETDRV_R32 (TxConfig));
 	tp->chipset = 0;
 
 match:
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index d768f3d..4daafe3 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -58,18 +58,15 @@
  * PCI device identifiers for "new style" Linux PCI Device Drivers
  */
 static struct pci_device_id pcnet32_pci_tbl[] = {
-	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE_HOME,
-	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE,
-	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE_HOME), },
+	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE), },
 
 	/*
 	 * Adapters that were sold with IBM's RS/6000 or pSeries hardware have
 	 * the incorrect vendor id.
 	 */
-	{ PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE,
-	  PCI_ANY_ID, PCI_ANY_ID,
-	  PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, 0},
+	{ PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE),
+	  .class = (PCI_CLASS_NETWORK_ETHERNET << 8), .class_mask = 0xffff00, },
 
 	{ }	/* terminate list */
 };
@@ -188,6 +185,23 @@
 
 #define PCNET32_TOTAL_SIZE	0x20
 
+#define CSR0		0
+#define CSR0_INIT	0x1
+#define CSR0_START	0x2
+#define CSR0_STOP	0x4
+#define CSR0_TXPOLL	0x8
+#define CSR0_INTEN	0x40
+#define CSR0_IDON	0x0100
+#define CSR0_NORMAL	(CSR0_START | CSR0_INTEN)
+#define PCNET32_INIT_LOW	1
+#define PCNET32_INIT_HIGH	2
+#define CSR3		3
+#define CSR4		4
+#define CSR5		5
+#define CSR5_SUSPEND	0x0001
+#define CSR15		15
+#define PCNET32_MC_FILTER	8
+
 /* The PCNET32 Rx and Tx ring descriptors. */
 struct pcnet32_rx_head {
 	u32	base;
@@ -277,7 +291,6 @@
 	u32			phymask;
 };
 
-static void pcnet32_probe_vlbus(void);
 static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *);
 static int pcnet32_probe1(unsigned long, int, struct pci_dev *);
 static int pcnet32_open(struct net_device *);
@@ -419,6 +432,238 @@
 	.reset = pcnet32_dwio_reset
 };
 
+static void pcnet32_netif_stop(struct net_device *dev)
+{
+	dev->trans_start = jiffies;
+	netif_poll_disable(dev);
+	netif_tx_disable(dev);
+}
+
+static void pcnet32_netif_start(struct net_device *dev)
+{
+	netif_wake_queue(dev);
+	netif_poll_enable(dev);
+}
+
+/*
+ * Allocate space for the new sized tx ring.
+ * Free old resources
+ * Save new resources.
+ * Any failure keeps old resources.
+ * Must be called with lp->lock held.
+ */
+static void pcnet32_realloc_tx_ring(struct net_device *dev,
+				    struct pcnet32_private *lp,
+				    unsigned int size)
+{
+	dma_addr_t new_ring_dma_addr;
+	dma_addr_t *new_dma_addr_list;
+	struct pcnet32_tx_head *new_tx_ring;
+	struct sk_buff **new_skb_list;
+
+	pcnet32_purge_tx_ring(dev);
+
+	new_tx_ring = pci_alloc_consistent(lp->pci_dev,
+					   sizeof(struct pcnet32_tx_head) *
+					   (1 << size),
+					   &new_ring_dma_addr);
+	if (new_tx_ring == NULL) {
+		if (netif_msg_drv(lp))
+			printk("\n" KERN_ERR
+			       "%s: Consistent memory allocation failed.\n",
+			       dev->name);
+		return;
+	}
+	memset(new_tx_ring, 0, sizeof(struct pcnet32_tx_head) * (1 << size));
+
+	new_dma_addr_list = kcalloc((1 << size), sizeof(dma_addr_t),
+				GFP_ATOMIC);
+	if (!new_dma_addr_list) {
+		if (netif_msg_drv(lp))
+			printk("\n" KERN_ERR
+			       "%s: Memory allocation failed.\n", dev->name);
+		goto free_new_tx_ring;
+	}
+
+	new_skb_list = kcalloc((1 << size), sizeof(struct sk_buff *),
+				GFP_ATOMIC);
+	if (!new_skb_list) {
+		if (netif_msg_drv(lp))
+			printk("\n" KERN_ERR
+			       "%s: Memory allocation failed.\n", dev->name);
+		goto free_new_lists;
+	}
+
+	kfree(lp->tx_skbuff);
+	kfree(lp->tx_dma_addr);
+	pci_free_consistent(lp->pci_dev,
+			    sizeof(struct pcnet32_tx_head) *
+			    lp->tx_ring_size, lp->tx_ring,
+			    lp->tx_ring_dma_addr);
+
+	lp->tx_ring_size = (1 << size);
+	lp->tx_mod_mask = lp->tx_ring_size - 1;
+	lp->tx_len_bits = (size << 12);
+	lp->tx_ring = new_tx_ring;
+	lp->tx_ring_dma_addr = new_ring_dma_addr;
+	lp->tx_dma_addr = new_dma_addr_list;
+	lp->tx_skbuff = new_skb_list;
+	return;
+
+    free_new_lists:
+	kfree(new_dma_addr_list);
+    free_new_tx_ring:
+	pci_free_consistent(lp->pci_dev,
+			    sizeof(struct pcnet32_tx_head) *
+			    (1 << size),
+			    new_tx_ring,
+			    new_ring_dma_addr);
+	return;
+}
+
+/*
+ * Allocate space for the new sized rx ring.
+ * Re-use old receive buffers.
+ *   alloc extra buffers
+ *   free unneeded buffers
+ *   free unneeded buffers
+ * Save new resources.
+ * Any failure keeps old resources.
+ * Must be called with lp->lock held.
+ */
+static void pcnet32_realloc_rx_ring(struct net_device *dev,
+				    struct pcnet32_private *lp,
+				    unsigned int size)
+{
+	dma_addr_t new_ring_dma_addr;
+	dma_addr_t *new_dma_addr_list;
+	struct pcnet32_rx_head *new_rx_ring;
+	struct sk_buff **new_skb_list;
+	int new, overlap;
+
+	new_rx_ring = pci_alloc_consistent(lp->pci_dev,
+					   sizeof(struct pcnet32_rx_head) *
+					   (1 << size),
+					   &new_ring_dma_addr);
+	if (new_rx_ring == NULL) {
+		if (netif_msg_drv(lp))
+			printk("\n" KERN_ERR
+			       "%s: Consistent memory allocation failed.\n",
+			       dev->name);
+		return;
+	}
+	memset(new_rx_ring, 0, sizeof(struct pcnet32_rx_head) * (1 << size));
+
+	new_dma_addr_list = kcalloc((1 << size), sizeof(dma_addr_t),
+				GFP_ATOMIC);
+	if (!new_dma_addr_list) {
+		if (netif_msg_drv(lp))
+			printk("\n" KERN_ERR
+			       "%s: Memory allocation failed.\n", dev->name);
+		goto free_new_rx_ring;
+	}
+
+	new_skb_list = kcalloc((1 << size), sizeof(struct sk_buff *),
+				GFP_ATOMIC);
+	if (!new_skb_list) {
+		if (netif_msg_drv(lp))
+			printk("\n" KERN_ERR
+			       "%s: Memory allocation failed.\n", dev->name);
+		goto free_new_lists;
+	}
+
+	/* first copy the current receive buffers */
+	overlap = min(size, lp->rx_ring_size);
+	for (new = 0; new < overlap; new++) {
+		new_rx_ring[new] = lp->rx_ring[new];
+		new_dma_addr_list[new] = lp->rx_dma_addr[new];
+		new_skb_list[new] = lp->rx_skbuff[new];
+	}
+	/* now allocate any new buffers needed */
+	for (; new < size; new++ ) {
+		struct sk_buff *rx_skbuff;
+		new_skb_list[new] = dev_alloc_skb(PKT_BUF_SZ);
+		if (!(rx_skbuff = new_skb_list[new])) {
+			/* keep the original lists and buffers */
+			if (netif_msg_drv(lp))
+				printk(KERN_ERR
+				       "%s: pcnet32_realloc_rx_ring dev_alloc_skb failed.\n",
+				       dev->name);
+			goto free_all_new;
+		}
+		skb_reserve(rx_skbuff, 2);
+
+		new_dma_addr_list[new] =
+			    pci_map_single(lp->pci_dev, rx_skbuff->data,
+					   PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
+		new_rx_ring[new].base = (u32) le32_to_cpu(new_dma_addr_list[new]);
+		new_rx_ring[new].buf_length = le16_to_cpu(2 - PKT_BUF_SZ);
+		new_rx_ring[new].status = le16_to_cpu(0x8000);
+	}
+	/* and free any unneeded buffers */
+	for (; new < lp->rx_ring_size; new++) {
+		if (lp->rx_skbuff[new]) {
+			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[new],
+					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(lp->rx_skbuff[new]);
+		}
+	}
+
+	kfree(lp->rx_skbuff);
+	kfree(lp->rx_dma_addr);
+	pci_free_consistent(lp->pci_dev,
+			    sizeof(struct pcnet32_rx_head) *
+			    lp->rx_ring_size, lp->rx_ring,
+			    lp->rx_ring_dma_addr);
+
+	lp->rx_ring_size = (1 << size);
+	lp->rx_mod_mask = lp->rx_ring_size - 1;
+	lp->rx_len_bits = (size << 4);
+	lp->rx_ring = new_rx_ring;
+	lp->rx_ring_dma_addr = new_ring_dma_addr;
+	lp->rx_dma_addr = new_dma_addr_list;
+	lp->rx_skbuff = new_skb_list;
+	return;
+
+    free_all_new:
+	for (; --new >= lp->rx_ring_size; ) {
+		if (new_skb_list[new]) {
+			pci_unmap_single(lp->pci_dev, new_dma_addr_list[new],
+					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(new_skb_list[new]);
+		}
+	}
+	kfree(new_skb_list);
+    free_new_lists:
+	kfree(new_dma_addr_list);
+    free_new_rx_ring:
+	pci_free_consistent(lp->pci_dev,
+			    sizeof(struct pcnet32_rx_head) *
+			    (1 << size),
+			    new_rx_ring,
+			    new_ring_dma_addr);
+	return;
+}
+
+static void pcnet32_purge_rx_ring(struct net_device *dev)
+{
+	struct pcnet32_private *lp = dev->priv;
+	int i;
+
+	/* free all allocated skbuffs */
+	for (i = 0; i < lp->rx_ring_size; i++) {
+		lp->rx_ring[i].status = 0;	/* CPU owns buffer */
+		wmb();		/* Make sure adapter sees owner change */
+		if (lp->rx_skbuff[i]) {
+			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],
+					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb_any(lp->rx_skbuff[i]);
+		}
+		lp->rx_skbuff[i] = NULL;
+		lp->rx_dma_addr[i] = 0;
+	}
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void pcnet32_poll_controller(struct net_device *dev)
 {
@@ -519,10 +764,10 @@
 {
 	struct pcnet32_private *lp = dev->priv;
 
-	ering->tx_max_pending = TX_MAX_RING_SIZE - 1;
-	ering->tx_pending = lp->tx_ring_size - 1;
-	ering->rx_max_pending = RX_MAX_RING_SIZE - 1;
-	ering->rx_pending = lp->rx_ring_size - 1;
+	ering->tx_max_pending = TX_MAX_RING_SIZE;
+	ering->tx_pending = lp->tx_ring_size;
+	ering->rx_max_pending = RX_MAX_RING_SIZE;
+	ering->rx_pending = lp->rx_ring_size;
 }
 
 static int pcnet32_set_ringparam(struct net_device *dev,
@@ -530,56 +775,53 @@
 {
 	struct pcnet32_private *lp = dev->priv;
 	unsigned long flags;
+	unsigned int size;
+	ulong ioaddr = dev->base_addr;
 	int i;
 
 	if (ering->rx_mini_pending || ering->rx_jumbo_pending)
 		return -EINVAL;
 
 	if (netif_running(dev))
-		pcnet32_close(dev);
+		pcnet32_netif_stop(dev);
 
 	spin_lock_irqsave(&lp->lock, flags);
-	pcnet32_free_ring(dev);
-	lp->tx_ring_size =
-	    min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE);
-	lp->rx_ring_size =
-	    min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE);
+	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */
+
+	size = min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE);
 
 	/* set the minimum ring size to 4, to allow the loopback test to work
 	 * unchanged.
 	 */
 	for (i = 2; i <= PCNET32_LOG_MAX_TX_BUFFERS; i++) {
-		if (lp->tx_ring_size <= (1 << i))
+		if (size <= (1 << i))
 			break;
 	}
-	lp->tx_ring_size = (1 << i);
-	lp->tx_mod_mask = lp->tx_ring_size - 1;
-	lp->tx_len_bits = (i << 12);
-
+	if ((1 << i) != lp->tx_ring_size)
+		pcnet32_realloc_tx_ring(dev, lp, i);
+	
+	size = min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE);
 	for (i = 2; i <= PCNET32_LOG_MAX_RX_BUFFERS; i++) {
-		if (lp->rx_ring_size <= (1 << i))
+		if (size <= (1 << i))
 			break;
 	}
-	lp->rx_ring_size = (1 << i);
-	lp->rx_mod_mask = lp->rx_ring_size - 1;
-	lp->rx_len_bits = (i << 4);
+	if ((1 << i) != lp->rx_ring_size)
+		pcnet32_realloc_rx_ring(dev, lp, i);
+	
+	dev->weight = lp->rx_ring_size / 2;
 
-	if (pcnet32_alloc_ring(dev, dev->name)) {
-		pcnet32_free_ring(dev);
-		spin_unlock_irqrestore(&lp->lock, flags);
-		return -ENOMEM;
+	if (netif_running(dev)) {
+		pcnet32_netif_start(dev);
+		pcnet32_restart(dev, CSR0_NORMAL);
 	}
 
 	spin_unlock_irqrestore(&lp->lock, flags);
 
-	if (pcnet32_debug & NETIF_MSG_DRV)
-		printk(KERN_INFO PFX
+	if (netif_msg_drv(lp))
+		printk(KERN_INFO
 		       "%s: Ring Param Settings: RX: %d, TX: %d\n", dev->name,
 		       lp->rx_ring_size, lp->tx_ring_size);
 
-	if (netif_running(dev))
-		pcnet32_open(dev);
-
 	return 0;
 }
 
@@ -633,29 +875,27 @@
 	unsigned long flags;
 	unsigned long ticks;
 
-	*data1 = 1;		/* status of test, default to fail */
 	rc = 1;			/* default to fail */
 
 	if (netif_running(dev))
 		pcnet32_close(dev);
 
 	spin_lock_irqsave(&lp->lock, flags);
+	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */
+
+	numbuffs = min(numbuffs, (int)min(lp->rx_ring_size, lp->tx_ring_size));
 
 	/* Reset the PCNET32 */
 	lp->a.reset(ioaddr);
+	lp->a.write_csr(ioaddr, CSR4, 0x0915);
 
 	/* switch pcnet32 to 32bit mode */
 	lp->a.write_bcr(ioaddr, 20, 2);
 
-	lp->init_block.mode =
-	    le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
-	lp->init_block.filter[0] = 0;
-	lp->init_block.filter[1] = 0;
-
 	/* purge & init rings but don't actually restart */
 	pcnet32_restart(dev, 0x0000);
 
-	lp->a.write_csr(ioaddr, 0, 0x0004);	/* Set STOP bit */
+	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* Set STOP bit */
 
 	/* Initialize Transmit buffers. */
 	size = data_len + 15;
@@ -697,14 +937,15 @@
 		}
 	}
 
-	x = a->read_bcr(ioaddr, 32);	/* set internal loopback in BSR32 */
-	x = x | 0x0002;
-	a->write_bcr(ioaddr, 32, x);
+	x = a->read_bcr(ioaddr, 32);	/* set internal loopback in BCR32 */
+	a->write_bcr(ioaddr, 32, x | 0x0002);
 
-	lp->a.write_csr(ioaddr, 15, 0x0044);	/* set int loopback in CSR15 */
+	/* set int loopback in CSR15 */
+	x = a->read_csr(ioaddr, CSR15) & 0xfffc;
+	lp->a.write_csr(ioaddr, CSR15, x | 0x0044);
 
 	teststatus = le16_to_cpu(0x8000);
-	lp->a.write_csr(ioaddr, 0, 0x0002);	/* Set STRT bit */
+	lp->a.write_csr(ioaddr, CSR0, CSR0_START);	/* Set STRT bit */
 
 	/* Check status of descriptors */
 	for (x = 0; x < numbuffs; x++) {
@@ -712,7 +953,7 @@
 		rmb();
 		while ((lp->rx_ring[x].status & teststatus) && (ticks < 200)) {
 			spin_unlock_irqrestore(&lp->lock, flags);
-			mdelay(1);
+			msleep(1);
 			spin_lock_irqsave(&lp->lock, flags);
 			rmb();
 			ticks++;
@@ -725,7 +966,7 @@
 		}
 	}
 
-	lp->a.write_csr(ioaddr, 0, 0x0004);	/* Set STOP bit */
+	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* Set STOP bit */
 	wmb();
 	if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) {
 		printk(KERN_DEBUG "%s: RX loopback packets:\n", dev->name);
@@ -758,25 +999,24 @@
 		}
 		x++;
 	}
-	if (!rc) {
-		*data1 = 0;
-	}
 
       clean_up:
+	*data1 = rc;
 	pcnet32_purge_tx_ring(dev);
-	x = a->read_csr(ioaddr, 15) & 0xFFFF;
-	a->write_csr(ioaddr, 15, (x & ~0x0044));	/* reset bits 6 and 2 */
+
+	x = a->read_csr(ioaddr, CSR15);
+	a->write_csr(ioaddr, CSR15, (x & ~0x0044));	/* reset bits 6 and 2 */
 
 	x = a->read_bcr(ioaddr, 32);	/* reset internal loopback */
-	x = x & ~0x0002;
-	a->write_bcr(ioaddr, 32, x);
-
-	spin_unlock_irqrestore(&lp->lock, flags);
+	a->write_bcr(ioaddr, 32, (x & ~0x0002));
 
 	if (netif_running(dev)) {
+		spin_unlock_irqrestore(&lp->lock, flags);
 		pcnet32_open(dev);
 	} else {
+		pcnet32_purge_rx_ring(dev);
 		lp->a.write_bcr(ioaddr, 20, 4);	/* return to 16bit mode */
+		spin_unlock_irqrestore(&lp->lock, flags);
 	}
 
 	return (rc);
@@ -839,6 +1079,43 @@
 	return 0;
 }
 
+/*
+ * lp->lock must be held.
+ */
+static int pcnet32_suspend(struct net_device *dev, unsigned long *flags,
+		int can_sleep)
+{
+	int csr5;
+	struct pcnet32_private *lp = dev->priv;
+	struct pcnet32_access *a = &lp->a;
+	ulong ioaddr = dev->base_addr;
+	int ticks;
+
+	/* set SUSPEND (SPND) - CSR5 bit 0 */
+	csr5 = a->read_csr(ioaddr, CSR5);
+	a->write_csr(ioaddr, CSR5, csr5 | CSR5_SUSPEND);
+
+	/* poll waiting for bit to be set */
+	ticks = 0;
+	while (!(a->read_csr(ioaddr, CSR5) & CSR5_SUSPEND)) {
+		spin_unlock_irqrestore(&lp->lock, *flags);
+		if (can_sleep)
+			msleep(1);
+		else
+			mdelay(1);
+		spin_lock_irqsave(&lp->lock, *flags);
+		ticks++;
+		if (ticks > 200) {
+			if (netif_msg_hw(lp))
+				printk(KERN_DEBUG
+				       "%s: Error getting into suspend!\n",
+				       dev->name);
+			return 0;
+		}
+	}
+	return 1;
+}
+
 #define PCNET32_REGS_PER_PHY	32
 #define PCNET32_MAX_PHYS	32
 static int pcnet32_get_regs_len(struct net_device *dev)
@@ -857,32 +1134,13 @@
 	struct pcnet32_private *lp = dev->priv;
 	struct pcnet32_access *a = &lp->a;
 	ulong ioaddr = dev->base_addr;
-	int ticks;
 	unsigned long flags;
 
 	spin_lock_irqsave(&lp->lock, flags);
 
-	csr0 = a->read_csr(ioaddr, 0);
-	if (!(csr0 & 0x0004)) {	/* If not stopped */
-		/* set SUSPEND (SPND) - CSR5 bit 0 */
-		a->write_csr(ioaddr, 5, 0x0001);
-
-		/* poll waiting for bit to be set */
-		ticks = 0;
-		while (!(a->read_csr(ioaddr, 5) & 0x0001)) {
-			spin_unlock_irqrestore(&lp->lock, flags);
-			mdelay(1);
-			spin_lock_irqsave(&lp->lock, flags);
-			ticks++;
-			if (ticks > 200) {
-				if (netif_msg_hw(lp))
-					printk(KERN_DEBUG
-					       "%s: Error getting into suspend!\n",
-					       dev->name);
-				break;
-			}
-		}
-	}
+	csr0 = a->read_csr(ioaddr, CSR0);
+	if (!(csr0 & CSR0_STOP))	/* If not stopped */
+		pcnet32_suspend(dev, &flags, 1);
 
 	/* read address PROM */
 	for (i = 0; i < 16; i += 2)
@@ -919,9 +1177,12 @@
 		}
 	}
 
-	if (!(csr0 & 0x0004)) {	/* If not stopped */
+	if (!(csr0 & CSR0_STOP)) {	/* If not stopped */
+		int csr5;
+
 		/* clear SUSPEND (SPND) - CSR5 bit 0 */
-		a->write_csr(ioaddr, 5, 0x0000);
+		csr5 = a->read_csr(ioaddr, CSR5);
+		a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));
 	}
 
 	spin_unlock_irqrestore(&lp->lock, flags);
@@ -952,7 +1213,7 @@
 /* only probes for non-PCI devices, the rest are handled by
  * pci_register_driver via pcnet32_probe_pci */
 
-static void __devinit pcnet32_probe_vlbus(void)
+static void __devinit pcnet32_probe_vlbus(unsigned int *pcnet32_portlist)
 {
 	unsigned int *port, ioaddr;
 
@@ -1436,7 +1697,7 @@
 					   lp->tx_ring_size,
 					   &lp->tx_ring_dma_addr);
 	if (lp->tx_ring == NULL) {
-		if (pcnet32_debug & NETIF_MSG_DRV)
+		if (netif_msg_drv(lp))
 			printk("\n" KERN_ERR PFX
 			       "%s: Consistent memory allocation failed.\n",
 			       name);
@@ -1448,52 +1709,48 @@
 					   lp->rx_ring_size,
 					   &lp->rx_ring_dma_addr);
 	if (lp->rx_ring == NULL) {
-		if (pcnet32_debug & NETIF_MSG_DRV)
+		if (netif_msg_drv(lp))
 			printk("\n" KERN_ERR PFX
 			       "%s: Consistent memory allocation failed.\n",
 			       name);
 		return -ENOMEM;
 	}
 
-	lp->tx_dma_addr = kmalloc(sizeof(dma_addr_t) * lp->tx_ring_size,
+	lp->tx_dma_addr = kcalloc(lp->tx_ring_size, sizeof(dma_addr_t),
 				  GFP_ATOMIC);
 	if (!lp->tx_dma_addr) {
-		if (pcnet32_debug & NETIF_MSG_DRV)
+		if (netif_msg_drv(lp))
 			printk("\n" KERN_ERR PFX
 			       "%s: Memory allocation failed.\n", name);
 		return -ENOMEM;
 	}
-	memset(lp->tx_dma_addr, 0, sizeof(dma_addr_t) * lp->tx_ring_size);
 
-	lp->rx_dma_addr = kmalloc(sizeof(dma_addr_t) * lp->rx_ring_size,
+	lp->rx_dma_addr = kcalloc(lp->rx_ring_size, sizeof(dma_addr_t),
 				  GFP_ATOMIC);
 	if (!lp->rx_dma_addr) {
-		if (pcnet32_debug & NETIF_MSG_DRV)
+		if (netif_msg_drv(lp))
 			printk("\n" KERN_ERR PFX
 			       "%s: Memory allocation failed.\n", name);
 		return -ENOMEM;
 	}
-	memset(lp->rx_dma_addr, 0, sizeof(dma_addr_t) * lp->rx_ring_size);
 
-	lp->tx_skbuff = kmalloc(sizeof(struct sk_buff *) * lp->tx_ring_size,
+	lp->tx_skbuff = kcalloc(lp->tx_ring_size, sizeof(struct sk_buff *),
 				GFP_ATOMIC);
 	if (!lp->tx_skbuff) {
-		if (pcnet32_debug & NETIF_MSG_DRV)
+		if (netif_msg_drv(lp))
 			printk("\n" KERN_ERR PFX
 			       "%s: Memory allocation failed.\n", name);
 		return -ENOMEM;
 	}
-	memset(lp->tx_skbuff, 0, sizeof(struct sk_buff *) * lp->tx_ring_size);
 
-	lp->rx_skbuff = kmalloc(sizeof(struct sk_buff *) * lp->rx_ring_size,
+	lp->rx_skbuff = kcalloc(lp->rx_ring_size, sizeof(struct sk_buff *),
 				GFP_ATOMIC);
 	if (!lp->rx_skbuff) {
-		if (pcnet32_debug & NETIF_MSG_DRV)
+		if (netif_msg_drv(lp))
 			printk("\n" KERN_ERR PFX
 			       "%s: Memory allocation failed.\n", name);
 		return -ENOMEM;
 	}
-	memset(lp->rx_skbuff, 0, sizeof(struct sk_buff *) * lp->rx_ring_size);
 
 	return 0;
 }
@@ -1757,16 +2014,7 @@
 
       err_free_ring:
 	/* free any allocated skbuffs */
-	for (i = 0; i < lp->rx_ring_size; i++) {
-		lp->rx_ring[i].status = 0;
-		if (lp->rx_skbuff[i]) {
-			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],
-					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
-			dev_kfree_skb(lp->rx_skbuff[i]);
-		}
-		lp->rx_skbuff[i] = NULL;
-		lp->rx_dma_addr[i] = 0;
-	}
+	pcnet32_purge_rx_ring(dev);
 
 	/*
 	 * Switch back to 16bit mode to avoid problems with dumb
@@ -2348,7 +2596,6 @@
 {
 	unsigned long ioaddr = dev->base_addr;
 	struct pcnet32_private *lp = dev->priv;
-	int i;
 	unsigned long flags;
 
 	del_timer_sync(&lp->watchdog_timer);
@@ -2379,31 +2626,8 @@
 
 	spin_lock_irqsave(&lp->lock, flags);
 
-	/* free all allocated skbuffs */
-	for (i = 0; i < lp->rx_ring_size; i++) {
-		lp->rx_ring[i].status = 0;
-		wmb();		/* Make sure adapter sees owner change */
-		if (lp->rx_skbuff[i]) {
-			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],
-					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);
-			dev_kfree_skb(lp->rx_skbuff[i]);
-		}
-		lp->rx_skbuff[i] = NULL;
-		lp->rx_dma_addr[i] = 0;
-	}
-
-	for (i = 0; i < lp->tx_ring_size; i++) {
-		lp->tx_ring[i].status = 0;	/* CPU owns buffer */
-		wmb();		/* Make sure adapter sees owner change */
-		if (lp->tx_skbuff[i]) {
-			pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i],
-					 lp->tx_skbuff[i]->len,
-					 PCI_DMA_TODEVICE);
-			dev_kfree_skb(lp->tx_skbuff[i]);
-		}
-		lp->tx_skbuff[i] = NULL;
-		lp->tx_dma_addr[i] = 0;
-	}
+	pcnet32_purge_rx_ring(dev);
+	pcnet32_purge_tx_ring(dev);
 
 	spin_unlock_irqrestore(&lp->lock, flags);
 
@@ -2433,6 +2657,7 @@
 	volatile struct pcnet32_init_block *ib = &lp->init_block;
 	volatile u16 *mcast_table = (u16 *) & ib->filter;
 	struct dev_mc_list *dmi = dev->mc_list;
+	unsigned long ioaddr = dev->base_addr;
 	char *addrs;
 	int i;
 	u32 crc;
@@ -2441,6 +2666,10 @@
 	if (dev->flags & IFF_ALLMULTI) {
 		ib->filter[0] = 0xffffffff;
 		ib->filter[1] = 0xffffffff;
+		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER, 0xffff);
+		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+1, 0xffff);
+		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+2, 0xffff);
+		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+3, 0xffff);
 		return;
 	}
 	/* clear the multicast filter */
@@ -2462,6 +2691,9 @@
 		    le16_to_cpu(le16_to_cpu(mcast_table[crc >> 4]) |
 				(1 << (crc & 0xf)));
 	}
+	for (i = 0; i < 4; i++)
+		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER + i,
+				le16_to_cpu(mcast_table[i]));
 	return;
 }
 
@@ -2472,8 +2704,11 @@
 {
 	unsigned long ioaddr = dev->base_addr, flags;
 	struct pcnet32_private *lp = dev->priv;
+	int csr15, suspended;
 
 	spin_lock_irqsave(&lp->lock, flags);
+	suspended = pcnet32_suspend(dev, &flags, 0);
+	csr15 = lp->a.read_csr(ioaddr, CSR15);
 	if (dev->flags & IFF_PROMISC) {
 		/* Log any net taps. */
 		if (netif_msg_hw(lp))
@@ -2482,15 +2717,24 @@
 		lp->init_block.mode =
 		    le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) <<
 				7);
+		lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000);
 	} else {
 		lp->init_block.mode =
 		    le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
+		lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff);
 		pcnet32_load_multicast(dev);
 	}
 
-	lp->a.write_csr(ioaddr, 0, 0x0004);	/* Temporarily stop the lance. */
-	pcnet32_restart(dev, 0x0042);	/*  Resume normal operation */
-	netif_wake_queue(dev);
+	if (suspended) {
+		int csr5;
+		/* clear SUSPEND (SPND) - CSR5 bit 0 */
+		csr5 = lp->a.read_csr(ioaddr, CSR5);
+		lp->a.write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));
+	} else { 
+		lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);
+		pcnet32_restart(dev, CSR0_NORMAL);
+		netif_wake_queue(dev);
+	}
 
 	spin_unlock_irqrestore(&lp->lock, flags);
 }
@@ -2730,7 +2974,7 @@
 
 	/* should we find any remaining VLbus devices ? */
 	if (pcnet32vlb)
-		pcnet32_probe_vlbus();
+		pcnet32_probe_vlbus(pcnet32_portlist);
 
 	if (cards_found && (pcnet32_debug & NETIF_MSG_PROBE))
 		printk(KERN_INFO PFX "%d cards_found.\n", cards_found);
diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c
index 3efb715..ae60e6e 100644
--- a/drivers/net/phy/cicada.c
+++ b/drivers/net/phy/cicada.c
@@ -103,7 +103,22 @@
 	return err;
 }
 
-/* Cicada 820x */
+/* Cicada 8201, a.k.a Vitesse VSC8201 */
+static struct phy_driver cis8201_driver = {
+	.phy_id		= 0x000fc410,
+	.name		= "Cicada Cis8201",
+	.phy_id_mask	= 0x000ffff0,
+	.features	= PHY_GBIT_FEATURES,
+	.flags		= PHY_HAS_INTERRUPT,
+	.config_init	= &cis820x_config_init,
+	.config_aneg	= &genphy_config_aneg,
+	.read_status	= &genphy_read_status,
+	.ack_interrupt	= &cis820x_ack_interrupt,
+	.config_intr	= &cis820x_config_intr,
+	.driver 	= { .owner = THIS_MODULE,},
+};
+
+/* Cicada 8204 */
 static struct phy_driver cis8204_driver = {
 	.phy_id		= 0x000fc440,
 	.name		= "Cicada Cis8204",
@@ -118,15 +133,30 @@
 	.driver 	= { .owner = THIS_MODULE,},
 };
 
-static int __init cis8204_init(void)
+static int __init cicada_init(void)
 {
-	return phy_driver_register(&cis8204_driver);
+	int ret;
+
+	ret = phy_driver_register(&cis8204_driver);
+	if (ret)
+		goto err1;
+
+	ret = phy_driver_register(&cis8201_driver);
+	if (ret)
+		goto err2;
+	return 0;
+
+err2:
+	phy_driver_unregister(&cis8204_driver);
+err1:
+	return ret;
 }
 
-static void __exit cis8204_exit(void)
+static void __exit cicada_exit(void)
 {
 	phy_driver_unregister(&cis8204_driver);
+	phy_driver_unregister(&cis8201_driver);
 }
 
-module_init(cis8204_init);
-module_exit(cis8204_exit);
+module_init(cicada_init);
+module_exit(cicada_exit);
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 16a0ef1..4c2f575 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -1406,7 +1406,7 @@
 	dev = alloc_etherdev(sizeof (*tp));
 	if (dev == NULL) {
 		if (netif_msg_drv(&debug))
-			printk(KERN_ERR PFX "unable to alloc new ethernet\n");
+			dev_err(&pdev->dev, "unable to alloc new ethernet\n");
 		goto err_out;
 	}
 
@@ -1418,10 +1418,8 @@
 	/* enable device (incl. PCI PM wakeup and hotplug setup) */
 	rc = pci_enable_device(pdev);
 	if (rc < 0) {
-		if (netif_msg_probe(tp)) {
-			printk(KERN_ERR PFX "%s: enable failure\n",
-			       pci_name(pdev));
-		}
+		if (netif_msg_probe(tp))
+			dev_err(&pdev->dev, "enable failure\n");
 		goto err_out_free_dev;
 	}
 
@@ -1437,37 +1435,32 @@
 		pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command);
 		acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
 	} else {
-		if (netif_msg_probe(tp)) {
-			printk(KERN_ERR PFX
+		if (netif_msg_probe(tp))
+			dev_err(&pdev->dev,
 			       "PowerManagement capability not found.\n");
-		}
 	}
 
 	/* make sure PCI base addr 1 is MMIO */
 	if (!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
-		if (netif_msg_probe(tp)) {
-			printk(KERN_ERR PFX
+		if (netif_msg_probe(tp))
+			dev_err(&pdev->dev,
 			       "region #1 not an MMIO resource, aborting\n");
-		}
 		rc = -ENODEV;
 		goto err_out_mwi;
 	}
 	/* check for weird/broken PCI region reporting */
 	if (pci_resource_len(pdev, 1) < R8169_REGS_SIZE) {
-		if (netif_msg_probe(tp)) {
-			printk(KERN_ERR PFX
+		if (netif_msg_probe(tp))
+			dev_err(&pdev->dev,
 			       "Invalid PCI region size(s), aborting\n");
-		}
 		rc = -ENODEV;
 		goto err_out_mwi;
 	}
 
 	rc = pci_request_regions(pdev, MODULENAME);
 	if (rc < 0) {
-		if (netif_msg_probe(tp)) {
-			printk(KERN_ERR PFX "%s: could not request regions.\n",
-			       pci_name(pdev));
-		}
+		if (netif_msg_probe(tp))
+			dev_err(&pdev->dev, "could not request regions.\n");
 		goto err_out_mwi;
 	}
 
@@ -1480,10 +1473,9 @@
 	} else {
 		rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
 		if (rc < 0) {
-			if (netif_msg_probe(tp)) {
-				printk(KERN_ERR PFX
+			if (netif_msg_probe(tp))
+				dev_err(&pdev->dev,
 				       "DMA configuration failed.\n");
-			}
 			goto err_out_free_res;
 		}
 	}
@@ -1494,7 +1486,7 @@
 	ioaddr = ioremap(pci_resource_start(pdev, 1), R8169_REGS_SIZE);
 	if (ioaddr == NULL) {
 		if (netif_msg_probe(tp))
-			printk(KERN_ERR PFX "cannot remap MMIO, aborting\n");
+			dev_err(&pdev->dev, "cannot remap MMIO, aborting\n");
 		rc = -EIO;
 		goto err_out_free_res;
 	}
@@ -1526,9 +1518,9 @@
 	if (i < 0) {
 		/* Unknown chip: assume array element #0, original RTL-8169 */
 		if (netif_msg_probe(tp)) {
-			printk(KERN_DEBUG PFX "PCI device %s: "
+			dev_printk(KERN_DEBUG, &pdev->dev,
 			       "unknown chip version, assuming %s\n",
-			       pci_name(pdev), rtl_chip_info[0].name);
+			       rtl_chip_info[0].name);
 		}
 		i++;
 	}
diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c
index ed1f599..c0a62b0 100644
--- a/drivers/net/starfire.c
+++ b/drivers/net/starfire.c
@@ -22,129 +22,13 @@
 
 	Support and updates available at
 	http://www.scyld.com/network/starfire.html
+	[link no longer provides useful info -jgarzik]
 
-	-----------------------------------------------------------
-
-	Linux kernel-specific changes:
-
-	LK1.1.1 (jgarzik):
-	- Use PCI driver interface
-	- Fix MOD_xxx races
-	- softnet fixups
-
-	LK1.1.2 (jgarzik):
-	- Merge Becker version 0.15
-
-	LK1.1.3 (Andrew Morton)
-	- Timer cleanups
-
-	LK1.1.4 (jgarzik):
-	- Merge Becker version 1.03
-
-	LK1.2.1 (Ion Badulescu <ionut@cs.columbia.edu>)
-	- Support hardware Rx/Tx checksumming
-	- Use the GFP firmware taken from Adaptec's Netware driver
-
-	LK1.2.2 (Ion Badulescu)
-	- Backported to 2.2.x
-
-	LK1.2.3 (Ion Badulescu)
-	- Fix the flaky mdio interface
-	- More compat clean-ups
-
-	LK1.2.4 (Ion Badulescu)
-	- More 2.2.x initialization fixes
-
-	LK1.2.5 (Ion Badulescu)
-	- Several fixes from Manfred Spraul
-
-	LK1.2.6 (Ion Badulescu)
-	- Fixed ifup/ifdown/ifup problem in 2.4.x
-
-	LK1.2.7 (Ion Badulescu)
-	- Removed unused code
-	- Made more functions static and __init
-
-	LK1.2.8 (Ion Badulescu)
-	- Quell bogus error messages, inform about the Tx threshold
-	- Removed #ifdef CONFIG_PCI, this driver is PCI only
-
-	LK1.2.9 (Ion Badulescu)
-	- Merged Jeff Garzik's changes from 2.4.4-pre5
-	- Added 2.2.x compatibility stuff required by the above changes
-
-	LK1.2.9a (Ion Badulescu)
-	- More updates from Jeff Garzik
-
-	LK1.3.0 (Ion Badulescu)
-	- Merged zerocopy support
-
-	LK1.3.1 (Ion Badulescu)
-	- Added ethtool support
-	- Added GPIO (media change) interrupt support
-
-	LK1.3.2 (Ion Badulescu)
-	- Fixed 2.2.x compatibility issues introduced in 1.3.1
-	- Fixed ethtool ioctl returning uninitialized memory
-
-	LK1.3.3 (Ion Badulescu)
-	- Initialize the TxMode register properly
-	- Don't dereference dev->priv after freeing it
-
-	LK1.3.4 (Ion Badulescu)
-	- Fixed initialization timing problems
-	- Fixed interrupt mask definitions
-
-	LK1.3.5 (jgarzik)
-	- ethtool NWAY_RST, GLINK, [GS]MSGLVL support
-
-	LK1.3.6:
-	- Sparc64 support and fixes (Ion Badulescu)
-	- Better stats and error handling (Ion Badulescu)
-	- Use new pci_set_mwi() PCI API function (jgarzik)
-
-	LK1.3.7 (Ion Badulescu)
-	- minimal implementation of tx_timeout()
-	- correctly shutdown the Rx/Tx engines in netdev_close()
-	- added calls to netif_carrier_on/off
-	(patch from Stefan Rompf <srompf@isg.de>)
-	- VLAN support
-
-	LK1.3.8 (Ion Badulescu)
-	- adjust DMA burst size on sparc64
-	- 64-bit support
-	- reworked zerocopy support for 64-bit buffers
-	- working and usable interrupt mitigation/latency
-	- reduced Tx interrupt frequency for lower interrupt overhead
-
-	LK1.3.9 (Ion Badulescu)
-	- bugfix for mcast filter
-	- enable the right kind of Tx interrupts (TxDMADone, not TxDone)
-
-	LK1.4.0 (Ion Badulescu)
-	- NAPI support
-
-	LK1.4.1 (Ion Badulescu)
-	- flush PCI posting buffers after disabling Rx interrupts
-	- put the chip to a D3 slumber on driver unload
-	- added config option to enable/disable NAPI
-
-	LK1.4.2 (Ion Badulescu)
-	- finally added firmware (GPL'ed by Adaptec)
-	- removed compatibility code for 2.2.x
-
-	LK1.4.2.1 (Ion Badulescu)
-	- fixed 32/64 bit issues on i386 + CONFIG_HIGHMEM
-	- added 32-bit padding to outgoing skb's, removed previous workaround
-
-TODO:	- fix forced speed/duplexing code (broken a long time ago, when
-	somebody converted the driver to use the generic MII code)
-	- fix VLAN support
 */
 
 #define DRV_NAME	"starfire"
-#define DRV_VERSION	"1.03+LK1.4.2.1"
-#define DRV_RELDATE	"October 3, 2005"
+#define DRV_VERSION	"2.0"
+#define DRV_RELDATE	"June 27, 2006"
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -846,7 +730,6 @@
 		goto err_out_free_netdev;
 	}
 
-	/* ioremap is borken in Linux-2.2.x/sparc64 */
 	base = ioremap(ioaddr, io_size);
 	if (!base) {
 		printk(KERN_ERR DRV_NAME " %d: cannot remap %#x @ %#lx, aborting\n",
diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c
index 643fcea..ac17377 100644
--- a/drivers/net/sundance.c
+++ b/drivers/net/sundance.c
@@ -16,91 +16,13 @@
 
 	Support and updates available at
 	http://www.scyld.com/network/sundance.html
+	[link no longer provides useful info -jgarzik]
 
-
-	Version LK1.01a (jgarzik):
-	- Replace some MII-related magic numbers with constants
-
-	Version LK1.02 (D-Link):
-	- Add new board to PCI ID list
-	- Fix multicast bug
-
-	Version LK1.03 (D-Link):
-	- New Rx scheme, reduce Rx congestion
-	- Option to disable flow control
-
-	Version LK1.04 (D-Link):
-	- Tx timeout recovery
-	- More support for ethtool.
-
-	Version LK1.04a:
-	- Remove unused/constant members from struct pci_id_info
-	(which then allows removal of 'drv_flags' from private struct)
-	(jgarzik)
-	- If no phy is found, fail to load that board (jgarzik)
-	- Always start phy id scan at id 1 to avoid problems (Donald Becker)
-	- Autodetect where mii_preable_required is needed,
-	default to not needed.  (Donald Becker)
-
-	Version LK1.04b:
-	- Remove mii_preamble_required module parameter (Donald Becker)
-	- Add per-interface mii_preamble_required (setting is autodetected)
-	  (Donald Becker)
-	- Remove unnecessary cast from void pointer (jgarzik)
-	- Re-align comments in private struct (jgarzik)
-
-	Version LK1.04c (jgarzik):
-	- Support bitmapped message levels (NETIF_MSG_xxx), and the
-	  two ethtool ioctls that get/set them
-	- Don't hand-code MII ethtool support, use standard API/lib
-
-	Version LK1.04d:
-	- Merge from Donald Becker's sundance.c: (Jason Lunz)
-		* proper support for variably-sized MTUs
-		* default to PIO, to fix chip bugs
-	- Add missing unregister_netdev (Jason Lunz)
-	- Add CONFIG_SUNDANCE_MMIO config option (jgarzik)
-	- Better rx buf size calculation (Donald Becker)
-
-	Version LK1.05 (D-Link):
-	- Fix DFE-580TX packet drop issue (for DL10050C)
-	- Fix reset_tx logic
-
-	Version LK1.06 (D-Link):
-	- Fix crash while unloading driver
-
-	Versin LK1.06b (D-Link):
-	- New tx scheme, adaptive tx_coalesce
-	
-	Version LK1.07 (D-Link):
-	- Fix tx bugs in big-endian machines
-	- Remove unused max_interrupt_work module parameter, the new 
-	  NAPI-like rx scheme doesn't need it.
-	- Remove redundancy get_stats() in intr_handler(), those 
-	  I/O access could affect performance in ARM-based system
-	- Add Linux software VLAN support
-	
-	Version LK1.08 (Philippe De Muyter phdm@macqel.be):
-	- Fix bug of custom mac address 
-	(StationAddr register only accept word write) 
-
-	Version LK1.09 (D-Link):
-	- Fix the flowctrl bug.	
-	- Set Pause bit in MII ANAR if flow control enabled.	
-
-	Version LK1.09a (ICPlus):
-	- Add the delay time in reading the contents of EEPROM
-
-	Version LK1.10 (Philippe De Muyter phdm@macqel.be):
-	- Make 'unblock interface after Tx underrun' work
-
-	Version LK1.11 (Pedro Alejandro Lopez-Valencia palopezv at gmail.com):
-	- Add support for IC Plus Corporation IP100A chipset
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.01+LK1.11"
-#define DRV_RELDATE	"14-Jun-2006"
+#define DRV_VERSION	"1.1"
+#define DRV_RELDATE	"27-Jun-2006"
 
 
 /* The user-configurable values.
@@ -282,15 +204,15 @@
 #define USE_IO_OPS 1
 #endif
 
-static struct pci_device_id sundance_pci_tbl[] = {
-	{0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0},
-	{0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1},
-	{0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2},
-	{0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3},
-	{0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
-	{0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
-	{0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6},
-	{0,}
+static const struct pci_device_id sundance_pci_tbl[] = {
+	{ 0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0 },
+	{ 0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1 },
+	{ 0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2 },
+	{ 0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3 },
+	{ 0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 },
+	{ 0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 },
+	{ 0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 },
+	{ }
 };
 MODULE_DEVICE_TABLE(pci, sundance_pci_tbl);
 
@@ -301,7 +223,7 @@
 struct pci_id_info {
         const char *name;
 };
-static const struct pci_id_info pci_id_tbl[] = {
+static const struct pci_id_info pci_id_tbl[] __devinitdata = {
 	{"D-Link DFE-550TX FAST Ethernet Adapter"},
 	{"D-Link DFE-550FX 100Mbps Fiber-optics Adapter"},
 	{"D-Link DFE-580TX 4 port Server Adapter"},
@@ -309,7 +231,7 @@
 	{"D-Link DL10050-based FAST Ethernet Adapter"},
 	{"Sundance Technology Alta"},
 	{"IC Plus Corporation IP100A FAST Ethernet Adapter"},
-	{NULL,},			/* 0 terminated list. */
+	{ }	/* terminate list. */
 };
 
 /* This driver was written to use PCI memory space, however x86-oriented
diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c
index b4c0d10..7f41481 100644
--- a/drivers/net/tulip/winbond-840.c
+++ b/drivers/net/tulip/winbond-840.c
@@ -224,24 +224,21 @@
 };
 MODULE_DEVICE_TABLE(pci, w840_pci_tbl);
 
+enum {
+	netdev_res_size		= 128,	/* size of PCI BAR resource */
+};
+
 struct pci_id_info {
         const char *name;
-        struct match_info {
-                int     pci, pci_mask, subsystem, subsystem_mask;
-                int revision, revision_mask;                            /* Only 8 bits. */
-        } id;
-        int io_size;                            /* Needed for I/O region check or ioremap(). */
-        int drv_flags;                          /* Driver use, intended as capability flags. */
+        int drv_flags;		/* Driver use, intended as capability flags. */
 };
-static struct pci_id_info pci_id_tbl[] = {
-	{"Winbond W89c840",			/* Sometime a Level-One switch card. */
-	 { 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 },
-	   128, CanHaveMII | HasBrokenTx | FDXOnNoMII},
-	{"Winbond W89c840", { 0x08401050, 0xffffffff, },
-	   128, CanHaveMII | HasBrokenTx},
-	{"Compex RL100-ATX", { 0x201111F6, 0xffffffff,},
-	   128, CanHaveMII | HasBrokenTx},
-	{NULL,},					/* 0 terminated list. */
+
+static const struct pci_id_info pci_id_tbl[] __devinitdata = {
+	{ 				/* Sometime a Level-One switch card. */
+	  "Winbond W89c840",	CanHaveMII | HasBrokenTx | FDXOnNoMII},
+	{ "Winbond W89c840",	CanHaveMII | HasBrokenTx},
+	{ "Compex RL100-ATX",	CanHaveMII | HasBrokenTx},
+	{ }	/* terminate list. */
 };
 
 /* This driver was written to use PCI memory space, however some x86 systems
@@ -399,7 +396,7 @@
 #ifdef USE_IO_OPS
 	bar = 0;
 #endif
-	ioaddr = pci_iomap(pdev, bar, pci_id_tbl[chip_idx].io_size);
+	ioaddr = pci_iomap(pdev, bar, netdev_res_size);
 	if (!ioaddr)
 		goto err_out_free_res;
 
diff --git a/drivers/net/tulip/xircom_tulip_cb.c b/drivers/net/tulip/xircom_tulip_cb.c
index 091ebb7..17ca7dc 100644
--- a/drivers/net/tulip/xircom_tulip_cb.c
+++ b/drivers/net/tulip/xircom_tulip_cb.c
@@ -10,26 +10,11 @@
 	410 Severn Ave., Suite 210
 	Annapolis MD 21403
 
-	-----------------------------------------------------------
-
-	Linux kernel-specific changes:
-
-	LK1.0 (Ion Badulescu)
-	- Major cleanup
-	- Use 2.4 PCI API
-	- Support ethtool
-	- Rewrite perfect filter/hash code
-	- Use interrupts for media changes
-
-	LK1.1 (Ion Badulescu)
-	- Disallow negotiation of unsupported full-duplex modes
 */
 
 #define DRV_NAME	"xircom_tulip_cb"
-#define DRV_VERSION	"0.91+LK1.1"
-#define DRV_RELDATE	"October 11, 2001"
-
-#define CARDBUS 1
+#define DRV_VERSION	"0.92"
+#define DRV_RELDATE	"June 27, 2006"
 
 /* A few user-configurable values. */
 
@@ -306,10 +291,10 @@
 	struct xircom_tx_desc tx_ring[TX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-#ifdef CARDBUS
+
 	/* The X3201-3 requires 4-byte aligned tx bufs */
 	struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE];
-#endif
+
 	/* The addresses of receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
 	u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)];	/* Pseudo-Tx frame to init address table. */
@@ -908,10 +893,8 @@
 		tp->tx_skbuff[i] = NULL;
 		tp->tx_ring[i].status = 0;
 		tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
-#ifdef CARDBUS
 		if (tp->chip_id == X3201_3)
 			tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ);
-#endif /* CARDBUS */
 	}
 	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
 }
@@ -931,12 +914,10 @@
 	entry = tp->cur_tx % TX_RING_SIZE;
 
 	tp->tx_skbuff[entry] = skb;
-#ifdef CARDBUS
 	if (tp->chip_id == X3201_3) {
 		memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len);
 		tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data);
 	} else
-#endif
 		tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
 
 	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index 98b6f32..d3d0ec9 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -25,117 +25,13 @@
 	version. He may or may not be interested in bug reports on this
 	code. You can find his versions at:
 	http://www.scyld.com/network/via-rhine.html
-
-
-	Linux kernel version history:
-
-	LK1.1.0:
-	- Jeff Garzik: softnet 'n stuff
-
-	LK1.1.1:
-	- Justin Guyett: softnet and locking fixes
-	- Jeff Garzik: use PCI interface
-
-	LK1.1.2:
-	- Urban Widmark: minor cleanups, merges from Becker 1.03a/1.04 versions
-
-	LK1.1.3:
-	- Urban Widmark: use PCI DMA interface (with thanks to the eepro100.c
-			 code) update "Theory of Operation" with
-			 softnet/locking changes
-	- Dave Miller: PCI DMA and endian fixups
-	- Jeff Garzik: MOD_xxx race fixes, updated PCI resource allocation
-
-	LK1.1.4:
-	- Urban Widmark: fix gcc 2.95.2 problem and
-	                 remove writel's to fixed address 0x7c
-
-	LK1.1.5:
-	- Urban Widmark: mdio locking, bounce buffer changes
-	                 merges from Beckers 1.05 version
-	                 added netif_running_on/off support
-
-	LK1.1.6:
-	- Urban Widmark: merges from Beckers 1.08b version (VT6102 + mdio)
-	                 set netif_running_on/off on startup, del_timer_sync
-
-	LK1.1.7:
-	- Manfred Spraul: added reset into tx_timeout
-
-	LK1.1.9:
-	- Urban Widmark: merges from Beckers 1.10 version
-	                 (media selection + eeprom reload)
-	- David Vrabel:  merges from D-Link "1.11" version
-	                 (disable WOL and PME on startup)
-
-	LK1.1.10:
-	- Manfred Spraul: use "singlecopy" for unaligned buffers
-	                  don't allocate bounce buffers for !ReqTxAlign cards
-
-	LK1.1.11:
-	- David Woodhouse: Set dev->base_addr before the first time we call
-			   wait_for_reset(). It's a lot happier that way.
-			   Free np->tx_bufs only if we actually allocated it.
-
-	LK1.1.12:
-	- Martin Eriksson: Allow Memory-Mapped IO to be enabled.
-
-	LK1.1.13 (jgarzik):
-	- Add ethtool support
-	- Replace some MII-related magic numbers with constants
-
-	LK1.1.14 (Ivan G.):
-	- fixes comments for Rhine-III
-	- removes W_MAX_TIMEOUT (unused)
-	- adds HasDavicomPhy for Rhine-I (basis: linuxfet driver; my card
-	  is R-I and has Davicom chip, flag is referenced in kernel driver)
-	- sends chip_id as a parameter to wait_for_reset since np is not
-	  initialized on first call
-	- changes mmio "else if (chip_id==VT6102)" to "else" so it will work
-	  for Rhine-III's (documentation says same bit is correct)
-	- transmit frame queue message is off by one - fixed
-	- adds IntrNormalSummary to "Something Wicked" exclusion list
-	  so normal interrupts will not trigger the message (src: Donald Becker)
-	(Roger Luethi)
-	- show confused chip where to continue after Tx error
-	- location of collision counter is chip specific
-	- allow selecting backoff algorithm (module parameter)
-
-	LK1.1.15 (jgarzik):
-	- Use new MII lib helper generic_mii_ioctl
-
-	LK1.1.16 (Roger Luethi)
-	- Etherleak fix
-	- Handle Tx buffer underrun
-	- Fix bugs in full duplex handling
-	- New reset code uses "force reset" cmd on Rhine-II
-	- Various clean ups
-
-	LK1.1.17 (Roger Luethi)
-	- Fix race in via_rhine_start_tx()
-	- On errors, wait for Tx engine to turn off before scavenging
-	- Handle Tx descriptor write-back race on Rhine-II
-	- Force flushing for PCI posted writes
-	- More reset code changes
-
-	LK1.1.18 (Roger Luethi)
-	- No filtering multicast in promisc mode (Edward Peng)
-	- Fix for Rhine-I Tx timeouts
-
-	LK1.1.19 (Roger Luethi)
-	- Increase Tx threshold for unspecified errors
-
-	LK1.2.0-2.6 (Roger Luethi)
-	- Massive clean-up
-	- Rewrite PHY, media handling (remove options, full_duplex, backoff)
-	- Fix Tx engine race for good
-	- Craig Brind: Zero padded aligned buffers for short packets.
+	[link no longer provides useful info -jgarzik]
 
 */
 
 #define DRV_NAME	"via-rhine"
-#define DRV_VERSION	"1.2.0-2.6"
-#define DRV_RELDATE	"June-10-2004"
+#define DRV_VERSION	"1.4.0"
+#define DRV_RELDATE	"June-27-2006"
 
 
 /* A few user-configurable values.
@@ -356,12 +252,11 @@
 /* Beware of PCI posted writes */
 #define IOSYNC	do { ioread8(ioaddr + StationAddr); } while (0)
 
-static struct pci_device_id rhine_pci_tbl[] =
-{
-	{0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, /* VT86C100A */
-	{0x1106, 0x3065, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, /* VT6102 */
-	{0x1106, 0x3106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, /* 6105{,L,LOM} */
-	{0x1106, 0x3053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, /* VT6105M */
+static const struct pci_device_id rhine_pci_tbl[] = {
+	{ 0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, },	/* VT86C100A */
+	{ 0x1106, 0x3065, PCI_ANY_ID, PCI_ANY_ID, },	/* VT6102 */
+	{ 0x1106, 0x3106, PCI_ANY_ID, PCI_ANY_ID, },	/* 6105{,L,LOM} */
+	{ 0x1106, 0x3053, PCI_ANY_ID, PCI_ANY_ID, },	/* VT6105M */
 	{ }	/* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, rhine_pci_tbl);
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index ba2972b..f5b0078 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -229,7 +229,8 @@
 module_param(rx_copybreak, int, 0644);
 MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");
 
-static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, struct velocity_info_tbl *info);
+static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr,
+			       const struct velocity_info_tbl *info);
 static int velocity_get_pci_info(struct velocity_info *, struct pci_dev *pdev);
 static void velocity_print_info(struct velocity_info *vptr);
 static int velocity_open(struct net_device *dev);
@@ -294,9 +295,9 @@
  *	Internal board variants. At the moment we have only one
  */
 
-static struct velocity_info_tbl chip_info_table[] = {
-	{CHIP_TYPE_VT6110, "VIA Networking Velocity Family Gigabit Ethernet Adapter", 256, 1, 0x00FFFFFFUL},
-	{0, NULL}
+static const struct velocity_info_tbl chip_info_table[] __devinitdata = {
+	{CHIP_TYPE_VT6110, "VIA Networking Velocity Family Gigabit Ethernet Adapter", 1, 0x00FFFFFFUL},
+	{ }
 };
 
 /*
@@ -304,10 +305,9 @@
  *	device driver. Used for hotplug autoloading.
  */
 
-static struct pci_device_id velocity_id_table[] __devinitdata = {
-	{PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) chip_info_table},
-	{0, }
+static const struct pci_device_id velocity_id_table[] __devinitdata = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) },
+	{ }
 };
 
 MODULE_DEVICE_TABLE(pci, velocity_id_table);
@@ -341,7 +341,7 @@
 static void __devexit velocity_remove1(struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 
 #ifdef CONFIG_PM
 	unsigned long flags;
@@ -686,21 +686,23 @@
 	static int first = 1;
 	struct net_device *dev;
 	int i;
-	struct velocity_info_tbl *info = (struct velocity_info_tbl *) ent->driver_data;
+	const struct velocity_info_tbl *info = &chip_info_table[ent->driver_data];
 	struct velocity_info *vptr;
 	struct mac_regs __iomem * regs;
 	int ret = -ENOMEM;
 
+	/* FIXME: this driver, like almost all other ethernet drivers,
+	 * can support more than MAX_UNITS.
+	 */
 	if (velocity_nics >= MAX_UNITS) {
-		printk(KERN_NOTICE VELOCITY_NAME ": already found %d NICs.\n", 
-				velocity_nics);
+		dev_notice(&pdev->dev, "already found %d NICs.\n", 
+			   velocity_nics);
 		return -ENODEV;
 	}
 
 	dev = alloc_etherdev(sizeof(struct velocity_info));
-
-	if (dev == NULL) {
-		printk(KERN_ERR VELOCITY_NAME ": allocate net device failed.\n");
+	if (!dev) {
+		dev_err(&pdev->dev, "allocate net device failed.\n");
 		goto out;
 	}
 	
@@ -708,7 +710,7 @@
 	
 	SET_MODULE_OWNER(dev);
 	SET_NETDEV_DEV(dev, &pdev->dev);
-	vptr = dev->priv;
+	vptr = netdev_priv(dev);
 
 
 	if (first) {
@@ -731,17 +733,17 @@
 
 	ret = velocity_get_pci_info(vptr, pdev);
 	if (ret < 0) {
-		printk(KERN_ERR VELOCITY_NAME ": Failed to find PCI device.\n");
+		/* error message already printed */
 		goto err_disable;
 	}
 
 	ret = pci_request_regions(pdev, VELOCITY_NAME);
 	if (ret < 0) {
-		printk(KERN_ERR VELOCITY_NAME ": Failed to find PCI device.\n");
+		dev_err(&pdev->dev, "No PCI resources.\n");
 		goto err_disable;
 	}
 
-	regs = ioremap(vptr->memaddr, vptr->io_size);
+	regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE);
 	if (regs == NULL) {
 		ret = -EIO;
 		goto err_release_res;
@@ -859,13 +861,14 @@
  *	discovered.
  */
 
-static void __devinit velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, struct velocity_info_tbl *info)
+static void __devinit velocity_init_info(struct pci_dev *pdev,
+					 struct velocity_info *vptr,
+					 const struct velocity_info_tbl *info)
 {
 	memset(vptr, 0, sizeof(struct velocity_info));
 
 	vptr->pdev = pdev;
 	vptr->chip_id = info->chip_id;
-	vptr->io_size = info->io_size;
 	vptr->num_txq = info->txqueue;
 	vptr->multicast_limit = MCAM_SIZE;
 	spin_lock_init(&vptr->lock);
@@ -883,8 +886,7 @@
 
 static int __devinit velocity_get_pci_info(struct velocity_info *vptr, struct pci_dev *pdev)
 {
-
-	if(pci_read_config_byte(pdev, PCI_REVISION_ID, &vptr->rev_id) < 0)
+	if (pci_read_config_byte(pdev, PCI_REVISION_ID, &vptr->rev_id) < 0)
 		return -EIO;
 		
 	pci_set_master(pdev);
@@ -892,24 +894,20 @@
 	vptr->ioaddr = pci_resource_start(pdev, 0);
 	vptr->memaddr = pci_resource_start(pdev, 1);
 	
-	if(!(pci_resource_flags(pdev, 0) & IORESOURCE_IO))
-	{
-		printk(KERN_ERR "%s: region #0 is not an I/O resource, aborting.\n",
-				pci_name(pdev));
+	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) {
+		dev_err(&pdev->dev,
+			   "region #0 is not an I/O resource, aborting.\n");
 		return -EINVAL;
 	}
 
-	if((pci_resource_flags(pdev, 1) & IORESOURCE_IO))
-	{
-		printk(KERN_ERR "%s: region #1 is an I/O resource, aborting.\n",
-				pci_name(pdev));
+	if ((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) {
+		dev_err(&pdev->dev,
+			   "region #1 is an I/O resource, aborting.\n");
 		return -EINVAL;
 	}
 
-	if(pci_resource_len(pdev, 1) < 256)
-	{
-		printk(KERN_ERR "%s: region #1 is too small.\n", 
-				pci_name(pdev));
+	if (pci_resource_len(pdev, 1) < VELOCITY_IO_SIZE) {
+		dev_err(&pdev->dev, "region #1 is too small.\n");
 		return -EINVAL;
 	}
 	vptr->pdev = pdev;
@@ -1728,7 +1726,7 @@
  
 static int velocity_open(struct net_device *dev)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	int ret;
 
 	vptr->rx_buf_sz = (dev->mtu <= 1504 ? PKT_BUF_SZ : dev->mtu + 32);
@@ -1785,7 +1783,7 @@
  
 static int velocity_change_mtu(struct net_device *dev, int new_mtu)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	unsigned long flags;
 	int oldmtu = dev->mtu;
 	int ret = 0;
@@ -1861,7 +1859,7 @@
 
 static int velocity_close(struct net_device *dev)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 
 	netif_stop_queue(dev);
 	velocity_shutdown(vptr);
@@ -1894,7 +1892,7 @@
  
 static int velocity_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	int qnum = 0;
 	struct tx_desc *td_ptr;
 	struct velocity_td_info *tdinfo;
@@ -2049,7 +2047,7 @@
 static int velocity_intr(int irq, void *dev_instance, struct pt_regs *regs)
 {
 	struct net_device *dev = dev_instance;
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	u32 isr_status;
 	int max_count = 0;
 
@@ -2104,7 +2102,7 @@
  
 static void velocity_set_multi(struct net_device *dev)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	struct mac_regs __iomem * regs = vptr->mac_regs;
 	u8 rx_mode;
 	int i;
@@ -2153,7 +2151,7 @@
  
 static struct net_device_stats *velocity_get_stats(struct net_device *dev)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	
 	/* If the hardware is down, don't touch MII */
 	if(!netif_running(dev))
@@ -2196,7 +2194,7 @@
  
 static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	int ret;
 
 	/* If we are asked for information and the device is power
@@ -2825,7 +2823,7 @@
  
 static int velocity_ethtool_up(struct net_device *dev)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	if (!netif_running(dev))
 		pci_set_power_state(vptr->pdev, PCI_D0);
 	return 0;
@@ -2841,14 +2839,14 @@
  
 static void velocity_ethtool_down(struct net_device *dev)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	if (!netif_running(dev))
 		pci_set_power_state(vptr->pdev, PCI_D3hot);
 }
 
 static int velocity_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	struct mac_regs __iomem * regs = vptr->mac_regs;
 	u32 status;
 	status = check_connection_type(vptr->mac_regs);
@@ -2873,7 +2871,7 @@
 
 static int velocity_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	u32 curr_status;
 	u32 new_status = 0;
 	int ret = 0;
@@ -2896,14 +2894,14 @@
 
 static u32 velocity_get_link(struct net_device *dev)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	struct mac_regs __iomem * regs = vptr->mac_regs;
 	return BYTE_REG_BITS_IS_ON(PHYSR0_LINKGD, &regs->PHYSR0)  ? 0 : 1;
 }
 
 static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	strcpy(info->driver, VELOCITY_NAME);
 	strcpy(info->version, VELOCITY_VERSION);
 	strcpy(info->bus_info, pci_name(vptr->pdev));
@@ -2911,7 +2909,7 @@
 
 static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP;
 	wol->wolopts |= WAKE_MAGIC;
 	/*
@@ -2927,7 +2925,7 @@
 
 static int velocity_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 
 	if (!(wol->wolopts & (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP)))
 		return -EFAULT;
@@ -2992,7 +2990,7 @@
  
 static int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-	struct velocity_info *vptr = dev->priv;
+	struct velocity_info *vptr = netdev_priv(dev);
 	struct mac_regs __iomem * regs = vptr->mac_regs;
 	unsigned long flags;
 	struct mii_ioctl_data *miidata = if_mii(ifr);
diff --git a/drivers/net/via-velocity.h b/drivers/net/via-velocity.h
index f1b2640..496c3d5 100644
--- a/drivers/net/via-velocity.h
+++ b/drivers/net/via-velocity.h
@@ -31,6 +31,8 @@
 #define VELOCITY_FULL_DRV_NAM  "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver"
 #define VELOCITY_VERSION       "1.13"
 
+#define VELOCITY_IO_SIZE	256
+
 #define PKT_BUF_SZ          1540
 
 #define MAX_UNITS           8
@@ -1191,7 +1193,6 @@
 struct velocity_info_tbl {
 	enum chip_type chip_id;
 	char *name;
-	int io_size;
 	int txqueue;
 	u32 flags;
 };
@@ -1751,7 +1752,6 @@
 	struct mac_regs __iomem * mac_regs;
 	unsigned long memaddr;
 	unsigned long ioaddr;
-	u32 io_size;
 
 	u8 rev_id;
 
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index b5328b0..54b8e49 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -134,18 +134,6 @@
 	  The driver will be compiled as a module: the
 	  module will be called sealevel.
 
-config SYNCLINK_SYNCPPP
-	tristate "SyncLink HDLC/SYNCPPP support"
-	depends on WAN
-	help
-	  Enables HDLC/SYNCPPP support for the SyncLink WAN driver.
-
-	  Normally the SyncLink WAN driver works with the main PPP driver
-	  <file:drivers/net/ppp_generic.c> and pppd program.
-	  HDLC/SYNCPPP support allows use of the Cisco HDLC/PPP driver
-	  <file:drivers/net/wan/syncppp.c>. The SyncLink WAN driver (in
-	  character devices) must also be enabled.
-
 # Generic HDLC
 config HDLC
 	tristate "Generic HDLC layer"
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 823c6d5..316ca68 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -28,7 +28,6 @@
 obj-$(CONFIG_FARSYNC)		+=		syncppp.o	farsync.o
 obj-$(CONFIG_DSCC4)             +=				dscc4.o
 obj-$(CONFIG_LANMEDIA)		+=		syncppp.o
-obj-$(CONFIG_SYNCLINK_SYNCPPP)	+=		syncppp.o
 obj-$(CONFIG_X25_ASY)		+= x25_asy.o
 
 obj-$(CONFIG_LANMEDIA)		+= lmc/
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 30ec235..fa9d2c4 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -550,6 +550,7 @@
 
 source "drivers/net/wireless/hostap/Kconfig"
 source "drivers/net/wireless/bcm43xx/Kconfig"
+source "drivers/net/wireless/zd1211rw/Kconfig"
 
 # yes, this works even when no drivers are selected
 config NET_WIRELESS
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 512603d..c613af1 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -36,6 +36,7 @@
 
 obj-$(CONFIG_HOSTAP)		+= hostap/
 obj-$(CONFIG_BCM43XX)		+= bcm43xx/
+obj-$(CONFIG_ZD1211RW)		+= zd1211rw/
 
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index d8f5600..e1c5a93 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -1885,6 +1885,15 @@
 
 	spin_lock(&bcm->irq_lock);
 
+	/* Only accept IRQs, if we are initialized properly.
+	 * This avoids an RX race while initializing.
+	 * We should probably not enable IRQs before we are initialized
+	 * completely, but some careful work is needed to fix this. I think it
+	 * is best to stay with this cheap workaround for now... .
+	 */
+	if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED))
+		goto out;
+
 	reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
 	if (reason == 0xffffffff) {
 		/* irq not for us (shared irq) */
@@ -1906,19 +1915,11 @@
 
 	bcm43xx_interrupt_ack(bcm, reason);
 
-	/* Only accept IRQs, if we are initialized properly.
-	 * This avoids an RX race while initializing.
-	 * We should probably not enable IRQs before we are initialized
-	 * completely, but some careful work is needed to fix this. I think it
-	 * is best to stay with this cheap workaround for now... .
-	 */
-	if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
-		/* disable all IRQs. They are enabled again in the bottom half. */
-		bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
-		/* save the reason code and call our bottom half. */
-		bcm->irq_reason = reason;
-		tasklet_schedule(&bcm->isr_tasklet);
-	}
+	/* disable all IRQs. They are enabled again in the bottom half. */
+	bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+	/* save the reason code and call our bottom half. */
+	bcm->irq_reason = reason;
+	tasklet_schedule(&bcm->isr_tasklet);
 
 out:
 	mmiowb();
@@ -3698,6 +3699,10 @@
 		secinfo->encrypt = sec->encrypt;
 		dprintk(", .encrypt = %d", sec->encrypt);
 	}
+	if (sec->flags & SEC_AUTH_MODE) {
+		secinfo->auth_mode = sec->auth_mode;
+		dprintk(", .auth_mode = %d\n", sec->auth_mode);
+	}
 	dprintk("\n");
 	if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED &&
 	    !bcm->ieee->host_encrypt) {
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h
index 30a202b..1164936 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h
@@ -112,30 +112,6 @@
 	return bcm43xx_channel_to_freq_bg(channel);
 }
 
-/* Lightweight function to check if a channel number is valid.
- * Note that this does _NOT_ check for geographical restrictions!
- */
-static inline
-int bcm43xx_is_valid_channel_a(u8 channel)
-{
-	return (channel >= IEEE80211_52GHZ_MIN_CHANNEL
-	       && channel <= IEEE80211_52GHZ_MAX_CHANNEL);
-}
-static inline
-int bcm43xx_is_valid_channel_bg(u8 channel)
-{
-	return (channel >= IEEE80211_24GHZ_MIN_CHANNEL
-	       && channel <= IEEE80211_24GHZ_MAX_CHANNEL);
-}
-static inline
-int bcm43xx_is_valid_channel(struct bcm43xx_private *bcm,
-			     u8 channel)
-{
-	if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)
-		return bcm43xx_is_valid_channel_a(channel);
-	return bcm43xx_is_valid_channel_bg(channel);
-}
-
 void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf);
 void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf);
 
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c
index af5c0bf..bb9c484 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c
@@ -1594,11 +1594,11 @@
 	u16 r8, tmp;
 	u16 freq;
 
+	if (!ieee80211_is_valid_channel(bcm->ieee, channel))
+		return -EINVAL;
 	if ((radio->manufact == 0x17F) &&
 	    (radio->version == 0x2060) &&
 	    (radio->revision == 1)) {
-		if (channel > 200)
-			return -EINVAL;
 		freq = channel2freq_a(channel);
 
 		r8 = bcm43xx_radio_read16(bcm, 0x0008);
@@ -1651,9 +1651,6 @@
 		TODO();	//TODO:	TSSI2dbm workaround
 		bcm43xx_phy_xmitpower(bcm);//FIXME correct?
 	} else {
-		if ((channel < 1) || (channel > 14))
-			return -EINVAL;
-
 		if (synthetic_pu_workaround)
 			bcm43xx_synth_pu_workaround(bcm, channel);
 
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
index c35cb3a..5c36e29ef 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
@@ -119,7 +119,7 @@
 		channel = bcm43xx_freq_to_channel(bcm, data->freq.m);
 		freq = data->freq.m;
 	}
-	if (!bcm43xx_is_valid_channel(bcm, channel))
+	if (!ieee80211_is_valid_channel(bcm->ieee, channel))
 		goto out_unlock;
 	if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
 		//ieee80211softmac_disassoc(softmac, $REASON);
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
index d8ece28..6dbd855 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
@@ -296,11 +296,14 @@
 	u16 control = 0;
 	u16 wsec_rate = 0;
 	u16 encrypt_frame;
+	const u16 ftype = WLAN_FC_GET_TYPE(le16_to_cpu(wireless_header->frame_ctl));
+	const int is_mgt = (ftype == IEEE80211_FTYPE_MGMT);
 
 	/* Now construct the TX header. */
 	memset(txhdr, 0, sizeof(*txhdr));
 
-	bitrate = bcm->softmac->txrates.default_rate;
+	bitrate = ieee80211softmac_suggest_txrate(bcm->softmac,
+		is_multicast_ether_addr(wireless_header->addr1), is_mgt);
 	ofdm_modulation = !(ieee80211_is_cck_rate(bitrate));
 	fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate);
 	fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate));
diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c
index 49860fa..6dfa041 100644
--- a/drivers/net/wireless/hostap/hostap_plx.c
+++ b/drivers/net/wireless/hostap/hostap_plx.c
@@ -66,10 +66,12 @@
 	PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
 	PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
 	PLXDEV(0x126c, 0x8030, "Nortel emobility"),
+	PLXDEV(0x1562, 0x0001, "Symbol LA-4123"),
 	PLXDEV(0x1385, 0x4100, "Netgear MA301"),
 	PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
 	PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
 	PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
+	PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"),
 	PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
 	PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
 	PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig
new file mode 100644
index 0000000..66ed55b
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/Kconfig
@@ -0,0 +1,19 @@
+config ZD1211RW
+	tristate "ZyDAS ZD1211/ZD1211B USB-wireless support"
+	depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL
+	select FW_LOADER
+	---help---
+	  This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless
+	  chip, present in many USB-wireless adapters.
+
+	  Device firmware is required alongside this driver. You can download the
+	  firmware distribution from http://zd1211.ath.cx/get-firmware
+
+config ZD1211RW_DEBUG
+	bool "ZyDAS ZD1211 debugging"
+	depends on ZD1211RW
+	---help---
+	  ZD1211 debugging messages. Choosing Y will result in additional debug
+	  messages being saved to your kernel logs, which may help debug any
+	  problems.
+
diff --git a/drivers/net/wireless/zd1211rw/Makefile b/drivers/net/wireless/zd1211rw/Makefile
new file mode 100644
index 0000000..500314f
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_ZD1211RW) += zd1211rw.o
+
+zd1211rw-objs := zd_chip.o zd_ieee80211.o \
+		zd_mac.o zd_netdev.o \
+		zd_rf_al2230.o zd_rf_rf2959.o \
+		zd_rf.o zd_usb.o zd_util.o
+
+ifeq ($(CONFIG_ZD1211RW_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
new file mode 100644
index 0000000..efc9c4b
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -0,0 +1,1615 @@
+/* zd_chip.c
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* This file implements all the hardware specific functions for the ZD1211
+ * and ZD1211B chips. Support for the ZD1211B was possible after Timothy
+ * Legge sent me a ZD1211B device. Thank you Tim. -- Uli
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include "zd_def.h"
+#include "zd_chip.h"
+#include "zd_ieee80211.h"
+#include "zd_mac.h"
+#include "zd_rf.h"
+#include "zd_util.h"
+
+void zd_chip_init(struct zd_chip *chip,
+	         struct net_device *netdev,
+		 struct usb_interface *intf)
+{
+	memset(chip, 0, sizeof(*chip));
+	mutex_init(&chip->mutex);
+	zd_usb_init(&chip->usb, netdev, intf);
+	zd_rf_init(&chip->rf);
+}
+
+void zd_chip_clear(struct zd_chip *chip)
+{
+	mutex_lock(&chip->mutex);
+	zd_usb_clear(&chip->usb);
+	zd_rf_clear(&chip->rf);
+	mutex_unlock(&chip->mutex);
+	mutex_destroy(&chip->mutex);
+	memset(chip, 0, sizeof(*chip));
+}
+
+static int scnprint_mac_oui(const u8 *addr, char *buffer, size_t size)
+{
+	return scnprintf(buffer, size, "%02x-%02x-%02x",
+		         addr[0], addr[1], addr[2]);
+}
+
+/* Prints an identifier line, which will support debugging. */
+static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size)
+{
+	int i = 0;
+
+	i = scnprintf(buffer, size, "zd1211%s chip ",
+		      chip->is_zd1211b ? "b" : "");
+	i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i);
+	i += scnprintf(buffer+i, size-i, " ");
+	i += scnprint_mac_oui(chip->e2p_mac, buffer+i, size-i);
+	i += scnprintf(buffer+i, size-i, " ");
+	i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i);
+	i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c", chip->pa_type,
+		chip->patch_cck_gain ? 'g' : '-',
+		chip->patch_cr157 ? '7' : '-',
+		chip->patch_6m_band_edge ? '6' : '-');
+	return i;
+}
+
+static void print_id(struct zd_chip *chip)
+{
+	char buffer[80];
+
+	scnprint_id(chip, buffer, sizeof(buffer));
+	buffer[sizeof(buffer)-1] = 0;
+	dev_info(zd_chip_dev(chip), "%s\n", buffer);
+}
+
+/* Read a variable number of 32-bit values. Parameter count is not allowed to
+ * exceed USB_MAX_IOREAD32_COUNT.
+ */
+int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr,
+		 unsigned int count)
+{
+	int r;
+	int i;
+	zd_addr_t *a16 = (zd_addr_t *)NULL;
+	u16 *v16;
+	unsigned int count16;
+
+	if (count > USB_MAX_IOREAD32_COUNT)
+		return -EINVAL;
+
+	/* Allocate a single memory block for values and addresses. */
+	count16 = 2*count;
+	a16 = (zd_addr_t *)kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)),
+		                   GFP_NOFS);
+	if (!a16) {
+		dev_dbg_f(zd_chip_dev(chip),
+			  "error ENOMEM in allocation of a16\n");
+		r = -ENOMEM;
+		goto out;
+	}
+	v16 = (u16 *)(a16 + count16);
+
+	for (i = 0; i < count; i++) {
+		int j = 2*i;
+		/* We read the high word always first. */
+		a16[j] = zd_inc_word(addr[i]);
+		a16[j+1] = addr[i];
+	}
+
+	r = zd_ioread16v_locked(chip, v16, a16, count16);
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip),
+			  "error: zd_ioread16v_locked. Error number %d\n", r);
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		int j = 2*i;
+		values[i] = (v16[j] << 16) | v16[j+1];
+	}
+
+out:
+	kfree((void *)a16);
+	return r;
+}
+
+int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+	           unsigned int count)
+{
+	int i, j, r;
+	struct zd_ioreq16 *ioreqs16;
+	unsigned int count16;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+
+	if (count == 0)
+		return 0;
+	if (count > USB_MAX_IOWRITE32_COUNT)
+		return -EINVAL;
+
+	/* Allocate a single memory block for values and addresses. */
+	count16 = 2*count;
+	ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_NOFS);
+	if (!ioreqs16) {
+		r = -ENOMEM;
+		dev_dbg_f(zd_chip_dev(chip),
+			  "error %d in ioreqs16 allocation\n", r);
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		j = 2*i;
+		/* We write the high word always first. */
+		ioreqs16[j].value   = ioreqs[i].value >> 16;
+		ioreqs16[j].addr    = zd_inc_word(ioreqs[i].addr);
+		ioreqs16[j+1].value = ioreqs[i].value;
+		ioreqs16[j+1].addr  = ioreqs[i].addr;
+	}
+
+	r = zd_usb_iowrite16v(&chip->usb, ioreqs16, count16);
+#ifdef DEBUG
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip),
+			  "error %d in zd_usb_write16v\n", r);
+	}
+#endif /* DEBUG */
+out:
+	kfree(ioreqs16);
+	return r;
+}
+
+int zd_iowrite16a_locked(struct zd_chip *chip,
+                  const struct zd_ioreq16 *ioreqs, unsigned int count)
+{
+	int r;
+	unsigned int i, j, t, max;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	for (i = 0; i < count; i += j + t) {
+		t = 0;
+		max = count-i;
+		if (max > USB_MAX_IOWRITE16_COUNT)
+			max = USB_MAX_IOWRITE16_COUNT;
+		for (j = 0; j < max; j++) {
+			if (!ioreqs[i+j].addr) {
+				t = 1;
+				break;
+			}
+		}
+
+		r = zd_usb_iowrite16v(&chip->usb, &ioreqs[i], j);
+		if (r) {
+			dev_dbg_f(zd_chip_dev(chip),
+				  "error zd_usb_iowrite16v. Error number %d\n",
+				  r);
+			return r;
+		}
+	}
+
+	return 0;
+}
+
+/* Writes a variable number of 32 bit registers. The functions will split
+ * that in several USB requests. A split can be forced by inserting an IO
+ * request with an zero address field.
+ */
+int zd_iowrite32a_locked(struct zd_chip *chip,
+	          const struct zd_ioreq32 *ioreqs, unsigned int count)
+{
+	int r;
+	unsigned int i, j, t, max;
+
+	for (i = 0; i < count; i += j + t) {
+		t = 0;
+		max = count-i;
+		if (max > USB_MAX_IOWRITE32_COUNT)
+			max = USB_MAX_IOWRITE32_COUNT;
+		for (j = 0; j < max; j++) {
+			if (!ioreqs[i+j].addr) {
+				t = 1;
+				break;
+			}
+		}
+
+		r = _zd_iowrite32v_locked(chip, &ioreqs[i], j);
+		if (r) {
+			dev_dbg_f(zd_chip_dev(chip),
+				"error _zd_iowrite32v_locked."
+				" Error number %d\n", r);
+			return r;
+		}
+	}
+
+	return 0;
+}
+
+int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value)
+{
+	int r;
+
+	ZD_ASSERT(!mutex_is_locked(&chip->mutex));
+	mutex_lock(&chip->mutex);
+	r = zd_ioread16_locked(chip, value, addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value)
+{
+	int r;
+
+	ZD_ASSERT(!mutex_is_locked(&chip->mutex));
+	mutex_lock(&chip->mutex);
+	r = zd_ioread32_locked(chip, value, addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value)
+{
+	int r;
+
+	ZD_ASSERT(!mutex_is_locked(&chip->mutex));
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite16_locked(chip, value, addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value)
+{
+	int r;
+
+	ZD_ASSERT(!mutex_is_locked(&chip->mutex));
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32_locked(chip, value, addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses,
+	          u32 *values, unsigned int count)
+{
+	int r;
+
+	ZD_ASSERT(!mutex_is_locked(&chip->mutex));
+	mutex_lock(&chip->mutex);
+	r = zd_ioread32v_locked(chip, values, addresses, count);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+	          unsigned int count)
+{
+	int r;
+
+	ZD_ASSERT(!mutex_is_locked(&chip->mutex));
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32a_locked(chip, ioreqs, count);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int read_pod(struct zd_chip *chip, u8 *rf_type)
+{
+	int r;
+	u32 value;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread32_locked(chip, &value, E2P_POD);
+	if (r)
+		goto error;
+	dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value);
+
+	/* FIXME: AL2230 handling (Bit 7 in POD) */
+	*rf_type = value & 0x0f;
+	chip->pa_type = (value >> 16) & 0x0f;
+	chip->patch_cck_gain = (value >> 8) & 0x1;
+	chip->patch_cr157 = (value >> 13) & 0x1;
+	chip->patch_6m_band_edge = (value >> 21) & 0x1;
+
+	dev_dbg_f(zd_chip_dev(chip),
+		"RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d "
+		"patch 6M %d\n",
+		zd_rf_name(*rf_type), *rf_type,
+		chip->pa_type, chip->patch_cck_gain,
+		chip->patch_cr157, chip->patch_6m_band_edge);
+	return 0;
+error:
+	*rf_type = 0;
+	chip->pa_type = 0;
+	chip->patch_cck_gain = 0;
+	chip->patch_cr157 = 0;
+	chip->patch_6m_band_edge = 0;
+	return r;
+}
+
+static int _read_mac_addr(struct zd_chip *chip, u8 *mac_addr,
+	                  const zd_addr_t *addr)
+{
+	int r;
+	u32 parts[2];
+
+	r = zd_ioread32v_locked(chip, parts, (const zd_addr_t *)addr, 2);
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip),
+			"error: couldn't read e2p macs. Error number %d\n", r);
+		return r;
+	}
+
+	mac_addr[0] = parts[0];
+	mac_addr[1] = parts[0] >>  8;
+	mac_addr[2] = parts[0] >> 16;
+	mac_addr[3] = parts[0] >> 24;
+	mac_addr[4] = parts[1];
+	mac_addr[5] = parts[1] >>  8;
+
+	return 0;
+}
+
+static int read_e2p_mac_addr(struct zd_chip *chip)
+{
+	static const zd_addr_t addr[2] = { E2P_MAC_ADDR_P1, E2P_MAC_ADDR_P2 };
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return _read_mac_addr(chip, chip->e2p_mac, (const zd_addr_t *)addr);
+}
+
+/* MAC address: if custom mac addresses are to to be used CR_MAC_ADDR_P1 and
+ *              CR_MAC_ADDR_P2 must be overwritten
+ */
+void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr)
+{
+	mutex_lock(&chip->mutex);
+	memcpy(mac_addr, chip->e2p_mac, ETH_ALEN);
+	mutex_unlock(&chip->mutex);
+}
+
+static int read_mac_addr(struct zd_chip *chip, u8 *mac_addr)
+{
+	static const zd_addr_t addr[2] = { CR_MAC_ADDR_P1, CR_MAC_ADDR_P2 };
+	return _read_mac_addr(chip, mac_addr, (const zd_addr_t *)addr);
+}
+
+int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr)
+{
+	int r;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+	mutex_lock(&chip->mutex);
+	r = read_mac_addr(chip, mac_addr);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
+{
+	int r;
+	struct zd_ioreq32 reqs[2] = {
+		[0] = { .addr = CR_MAC_ADDR_P1 },
+		[1] = { .addr = CR_MAC_ADDR_P2 },
+	};
+
+	reqs[0].value = (mac_addr[3] << 24)
+		      | (mac_addr[2] << 16)
+		      | (mac_addr[1] <<  8)
+		      |  mac_addr[0];
+	reqs[1].value = (mac_addr[5] <<  8)
+		      |  mac_addr[4];
+
+	dev_dbg_f(zd_chip_dev(chip),
+		"mac addr " MAC_FMT "\n", MAC_ARG(mac_addr));
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
+#ifdef DEBUG
+	{
+		u8 tmp[ETH_ALEN];
+		read_mac_addr(chip, tmp);
+	}
+#endif /* DEBUG */
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain)
+{
+	int r;
+	u32 value;
+
+	mutex_lock(&chip->mutex);
+	r = zd_ioread32_locked(chip, &value, E2P_SUBID);
+	mutex_unlock(&chip->mutex);
+	if (r)
+		return r;
+
+	*regdomain = value >> 16;
+	dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain);
+
+	return 0;
+}
+
+static int read_values(struct zd_chip *chip, u8 *values, size_t count,
+	               zd_addr_t e2p_addr, u32 guard)
+{
+	int r;
+	int i;
+	u32 v;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	for (i = 0;;) {
+		r = zd_ioread32_locked(chip, &v, e2p_addr+i/2);
+		if (r)
+			return r;
+		v -= guard;
+		if (i+4 < count) {
+			values[i++] = v;
+			values[i++] = v >>  8;
+			values[i++] = v >> 16;
+			values[i++] = v >> 24;
+			continue;
+		}
+		for (;i < count; i++)
+			values[i] = v >> (8*(i%3));
+		return 0;
+	}
+}
+
+static int read_pwr_cal_values(struct zd_chip *chip)
+{
+	return read_values(chip, chip->pwr_cal_values,
+		        E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1,
+			0);
+}
+
+static int read_pwr_int_values(struct zd_chip *chip)
+{
+	return read_values(chip, chip->pwr_int_values,
+		        E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1,
+			E2P_PWR_INT_GUARD);
+}
+
+static int read_ofdm_cal_values(struct zd_chip *chip)
+{
+	int r;
+	int i;
+	static const zd_addr_t addresses[] = {
+		E2P_36M_CAL_VALUE1,
+		E2P_48M_CAL_VALUE1,
+		E2P_54M_CAL_VALUE1,
+	};
+
+	for (i = 0; i < 3; i++) {
+		r = read_values(chip, chip->ofdm_cal_values[i],
+				E2P_CHANNEL_COUNT, addresses[i], 0);
+		if (r)
+			return r;
+	}
+	return 0;
+}
+
+static int read_cal_int_tables(struct zd_chip *chip)
+{
+	int r;
+
+	r = read_pwr_cal_values(chip);
+	if (r)
+		return r;
+	r = read_pwr_int_values(chip);
+	if (r)
+		return r;
+	r = read_ofdm_cal_values(chip);
+	if (r)
+		return r;
+	return 0;
+}
+
+/* phy means physical registers */
+int zd_chip_lock_phy_regs(struct zd_chip *chip)
+{
+	int r;
+	u32 tmp;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread32_locked(chip, &tmp, CR_REG1);
+	if (r) {
+		dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r);
+		return r;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip),
+		"CR_REG1: 0x%02x -> 0x%02x\n", tmp, tmp & ~UNLOCK_PHY_REGS);
+	tmp &= ~UNLOCK_PHY_REGS;
+
+	r = zd_iowrite32_locked(chip, tmp, CR_REG1);
+	if (r)
+		dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r);
+	return r;
+}
+
+int zd_chip_unlock_phy_regs(struct zd_chip *chip)
+{
+	int r;
+	u32 tmp;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread32_locked(chip, &tmp, CR_REG1);
+	if (r) {
+		dev_err(zd_chip_dev(chip),
+			"error ioread32(CR_REG1): %d\n", r);
+		return r;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip),
+		"CR_REG1: 0x%02x -> 0x%02x\n", tmp, tmp | UNLOCK_PHY_REGS);
+	tmp |= UNLOCK_PHY_REGS;
+
+	r = zd_iowrite32_locked(chip, tmp, CR_REG1);
+	if (r)
+		dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r);
+	return r;
+}
+
+/* CR157 can be optionally patched by the EEPROM */
+static int patch_cr157(struct zd_chip *chip)
+{
+	int r;
+	u32 value;
+
+	if (!chip->patch_cr157)
+		return 0;
+
+	r = zd_ioread32_locked(chip, &value, E2P_PHY_REG);
+	if (r)
+		return r;
+
+	dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8);
+	return zd_iowrite32_locked(chip, value >> 8, CR157);
+}
+
+/*
+ * 6M band edge can be optionally overwritten for certain RF's
+ * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge
+ * bit (for AL2230, AL2230S)
+ */
+static int patch_6m_band_edge(struct zd_chip *chip, int channel)
+{
+	struct zd_ioreq16 ioreqs[] = {
+		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+		{ CR47,  0x1e },
+	};
+
+	if (!chip->patch_6m_band_edge || !chip->rf.patch_6m_band_edge)
+		return 0;
+
+	/* FIXME: Channel 11 is not the edge for all regulatory domains. */
+	if (channel == 1 || channel == 11)
+		ioreqs[0].value = 0x12;
+
+	dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel);
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211_hw_reset_phy(struct zd_chip *chip)
+{
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR0,   0x0a }, { CR1,   0x06 }, { CR2,   0x26 },
+		{ CR3,   0x38 }, { CR4,   0x80 }, { CR9,   0xa0 },
+		{ CR10,  0x81 }, { CR11,  0x00 }, { CR12,  0x7f },
+		{ CR13,  0x8c }, { CR14,  0x80 }, { CR15,  0x3d },
+		{ CR16,  0x20 }, { CR17,  0x1e }, { CR18,  0x0a },
+		{ CR19,  0x48 }, { CR20,  0x0c }, { CR21,  0x0c },
+		{ CR22,  0x23 }, { CR23,  0x90 }, { CR24,  0x14 },
+		{ CR25,  0x40 }, { CR26,  0x10 }, { CR27,  0x19 },
+		{ CR28,  0x7f }, { CR29,  0x80 }, { CR30,  0x4b },
+		{ CR31,  0x60 }, { CR32,  0x43 }, { CR33,  0x08 },
+		{ CR34,  0x06 }, { CR35,  0x0a }, { CR36,  0x00 },
+		{ CR37,  0x00 }, { CR38,  0x38 }, { CR39,  0x0c },
+		{ CR40,  0x84 }, { CR41,  0x2a }, { CR42,  0x80 },
+		{ CR43,  0x10 }, { CR44,  0x12 }, { CR46,  0xff },
+		{ CR47,  0x1E }, { CR48,  0x26 }, { CR49,  0x5b },
+		{ CR64,  0xd0 }, { CR65,  0x04 }, { CR66,  0x58 },
+		{ CR67,  0xc9 }, { CR68,  0x88 }, { CR69,  0x41 },
+		{ CR70,  0x23 }, { CR71,  0x10 }, { CR72,  0xff },
+		{ CR73,  0x32 }, { CR74,  0x30 }, { CR75,  0x65 },
+		{ CR76,  0x41 }, { CR77,  0x1b }, { CR78,  0x30 },
+		{ CR79,  0x68 }, { CR80,  0x64 }, { CR81,  0x64 },
+		{ CR82,  0x00 }, { CR83,  0x00 }, { CR84,  0x00 },
+		{ CR85,  0x02 }, { CR86,  0x00 }, { CR87,  0x00 },
+		{ CR88,  0xff }, { CR89,  0xfc }, { CR90,  0x00 },
+		{ CR91,  0x00 }, { CR92,  0x00 }, { CR93,  0x08 },
+		{ CR94,  0x00 }, { CR95,  0x00 }, { CR96,  0xff },
+		{ CR97,  0xe7 }, { CR98,  0x00 }, { CR99,  0x00 },
+		{ CR100, 0x00 }, { CR101, 0xae }, { CR102, 0x02 },
+		{ CR103, 0x00 }, { CR104, 0x03 }, { CR105, 0x65 },
+		{ CR106, 0x04 }, { CR107, 0x00 }, { CR108, 0x0a },
+		{ CR109, 0xaa }, { CR110, 0xaa }, { CR111, 0x25 },
+		{ CR112, 0x25 }, { CR113, 0x00 }, { CR119, 0x1e },
+		{ CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 },
+		{ },
+		{ CR5,   0x00 }, { CR6,   0x00 }, { CR7,   0x00 },
+		{ CR8,   0x00 }, { CR9,   0x20 }, { CR12,  0xf0 },
+		{ CR20,  0x0e }, { CR21,  0x0e }, { CR27,  0x10 },
+		{ CR44,  0x33 }, { CR47,  0x1E }, { CR83,  0x24 },
+		{ CR84,  0x04 }, { CR85,  0x00 }, { CR86,  0x0C },
+		{ CR87,  0x12 }, { CR88,  0x0C }, { CR89,  0x00 },
+		{ CR90,  0x10 }, { CR91,  0x08 }, { CR93,  0x00 },
+		{ CR94,  0x01 }, { CR95,  0x00 }, { CR96,  0x50 },
+		{ CR97,  0x37 }, { CR98,  0x35 }, { CR101, 0x13 },
+		{ CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 },
+		{ CR105, 0x12 }, { CR109, 0x27 }, { CR110, 0x27 },
+		{ CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 },
+		{ CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 },
+		{ CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f },
+		{ CR123, 0x27 }, { CR125, 0xaa }, { CR127, 0x03 },
+		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+		{ CR131, 0x0C }, { CR136, 0xdf }, { CR137, 0x40 },
+		{ CR138, 0xa0 }, { CR139, 0xb0 }, { CR140, 0x99 },
+		{ CR141, 0x82 }, { CR142, 0x54 }, { CR143, 0x1c },
+		{ CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x4c },
+		{ CR149, 0x50 }, { CR150, 0x0e }, { CR151, 0x18 },
+		{ CR160, 0xfe }, { CR161, 0xee }, { CR162, 0xaa },
+		{ CR163, 0xfa }, { CR164, 0xfa }, { CR165, 0xea },
+		{ CR166, 0xbe }, { CR167, 0xbe }, { CR168, 0x6a },
+		{ CR169, 0xba }, { CR170, 0xba }, { CR171, 0xba },
+		/* Note: CR204 must lead the CR203 */
+		{ CR204, 0x7d },
+		{ },
+		{ CR203, 0x30 },
+	};
+
+	int r, t;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		goto out;
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		goto unlock;
+
+	r = patch_cr157(chip);
+unlock:
+	t = zd_chip_unlock_phy_regs(chip);
+	if (t && !r)
+		r = t;
+out:
+	return r;
+}
+
+static int zd1211b_hw_reset_phy(struct zd_chip *chip)
+{
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR0,   0x14 }, { CR1,   0x06 }, { CR2,   0x26 },
+		{ CR3,   0x38 }, { CR4,   0x80 }, { CR9,   0xe0 },
+		{ CR10,  0x81 },
+		/* power control { { CR11,  1 << 6 }, */
+		{ CR11,  0x00 },
+		{ CR12,  0xf0 }, { CR13,  0x8c }, { CR14,  0x80 },
+		{ CR15,  0x3d }, { CR16,  0x20 }, { CR17,  0x1e },
+		{ CR18,  0x0a }, { CR19,  0x48 },
+		{ CR20,  0x10 }, /* Org:0x0E, ComTrend:RalLink AP */
+		{ CR21,  0x0e }, { CR22,  0x23 }, { CR23,  0x90 },
+		{ CR24,  0x14 }, { CR25,  0x40 }, { CR26,  0x10 },
+		{ CR27,  0x10 }, { CR28,  0x7f }, { CR29,  0x80 },
+		{ CR30,  0x49 }, /* jointly decoder, no ASIC */
+		{ CR31,  0x60 }, { CR32,  0x43 }, { CR33,  0x08 },
+		{ CR34,  0x06 }, { CR35,  0x0a }, { CR36,  0x00 },
+		{ CR37,  0x00 }, { CR38,  0x38 }, { CR39,  0x0c },
+		{ CR40,  0x84 }, { CR41,  0x2a }, { CR42,  0x80 },
+		{ CR43,  0x10 }, { CR44,  0x33 }, { CR46,  0xff },
+		{ CR47,  0x1E }, { CR48,  0x26 }, { CR49,  0x5b },
+		{ CR64,  0xd0 }, { CR65,  0x04 }, { CR66,  0x58 },
+		{ CR67,  0xc9 }, { CR68,  0x88 }, { CR69,  0x41 },
+		{ CR70,  0x23 }, { CR71,  0x10 }, { CR72,  0xff },
+		{ CR73,  0x32 }, { CR74,  0x30 }, { CR75,  0x65 },
+		{ CR76,  0x41 }, { CR77,  0x1b }, { CR78,  0x30 },
+		{ CR79,  0xf0 }, { CR80,  0x64 }, { CR81,  0x64 },
+		{ CR82,  0x00 }, { CR83,  0x24 }, { CR84,  0x04 },
+		{ CR85,  0x00 }, { CR86,  0x0c }, { CR87,  0x12 },
+		{ CR88,  0x0c }, { CR89,  0x00 }, { CR90,  0x58 },
+		{ CR91,  0x04 }, { CR92,  0x00 }, { CR93,  0x00 },
+		{ CR94,  0x01 },
+		{ CR95,  0x20 }, /* ZD1211B */
+		{ CR96,  0x50 }, { CR97,  0x37 }, { CR98,  0x35 },
+		{ CR99,  0x00 }, { CR100, 0x01 }, { CR101, 0x13 },
+		{ CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 },
+		{ CR105, 0x12 }, { CR106, 0x04 }, { CR107, 0x00 },
+		{ CR108, 0x0a }, { CR109, 0x27 }, { CR110, 0x27 },
+		{ CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 },
+		{ CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 },
+		{ CR117, 0xfc }, { CR118, 0xfa }, { CR119, 0x1e },
+		{ CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 },
+		{ CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 },
+		{ CR131, 0x0c }, { CR136, 0xdf }, { CR137, 0xa0 },
+		{ CR138, 0xa8 }, { CR139, 0xb4 }, { CR140, 0x98 },
+		{ CR141, 0x82 }, { CR142, 0x53 }, { CR143, 0x1c },
+		{ CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x40 },
+		{ CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */
+		{ CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */
+		{ CR151, 0x18 }, { CR159, 0x70 }, { CR160, 0xfe },
+		{ CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa },
+		{ CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe },
+		{ CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba },
+		{ CR170, 0xba }, { CR171, 0xba },
+		/* Note: CR204 must lead the CR203 */
+		{ CR204, 0x7d },
+		{},
+		{ CR203, 0x30 },
+	};
+
+	int r, t;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		goto out;
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		goto unlock;
+
+	r = patch_cr157(chip);
+unlock:
+	t = zd_chip_unlock_phy_regs(chip);
+	if (t && !r)
+		r = t;
+out:
+	return r;
+}
+
+static int hw_reset_phy(struct zd_chip *chip)
+{
+	return chip->is_zd1211b ? zd1211b_hw_reset_phy(chip) :
+		                  zd1211_hw_reset_phy(chip);
+}
+
+static int zd1211_hw_init_hmac(struct zd_chip *chip)
+{
+	static const struct zd_ioreq32 ioreqs[] = {
+		{ CR_ACK_TIMEOUT_EXT,		0x20 },
+		{ CR_ADDA_MBIAS_WARMTIME,	0x30000808 },
+		{ CR_ZD1211_RETRY_MAX,		0x2 },
+		{ CR_SNIFFER_ON,		0 },
+		{ CR_RX_FILTER,			AP_RX_FILTER },
+		{ CR_GROUP_HASH_P1,		0x00 },
+		{ CR_GROUP_HASH_P2,		0x80000000 },
+		{ CR_REG1,			0xa4 },
+		{ CR_ADDA_PWR_DWN,		0x7f },
+		{ CR_BCN_PLCP_CFG,		0x00f00401 },
+		{ CR_PHY_DELAY,			0x00 },
+		{ CR_ACK_TIMEOUT_EXT,		0x80 },
+		{ CR_ADDA_PWR_DWN,		0x00 },
+		{ CR_ACK_TIME_80211,		0x100 },
+		{ CR_IFS_VALUE,			0x547c032 },
+		{ CR_RX_PE_DELAY,		0x70 },
+		{ CR_PS_CTRL,			0x10000000 },
+		{ CR_RTS_CTS_RATE,		0x02030203 },
+		{ CR_RX_THRESHOLD,		0x000c0640 },
+		{ CR_AFTER_PNP,			0x1 },
+		{ CR_WEP_PROTECT,		0x114 },
+	};
+
+	int r;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+#ifdef DEBUG
+	if (r) {
+		dev_err(zd_chip_dev(chip),
+			"error in zd_iowrite32a_locked. Error number %d\n", r);
+	}
+#endif /* DEBUG */
+	return r;
+}
+
+static int zd1211b_hw_init_hmac(struct zd_chip *chip)
+{
+	static const struct zd_ioreq32 ioreqs[] = {
+		{ CR_ACK_TIMEOUT_EXT,		0x20 },
+		{ CR_ADDA_MBIAS_WARMTIME,	0x30000808 },
+		{ CR_ZD1211B_RETRY_MAX,		0x02020202 },
+		{ CR_ZD1211B_TX_PWR_CTL4,	0x007f003f },
+		{ CR_ZD1211B_TX_PWR_CTL3,	0x007f003f },
+		{ CR_ZD1211B_TX_PWR_CTL2,       0x003f001f },
+		{ CR_ZD1211B_TX_PWR_CTL1,       0x001f000f },
+		{ CR_ZD1211B_AIFS_CTL1,		0x00280028 },
+		{ CR_ZD1211B_AIFS_CTL2,		0x008C003C },
+		{ CR_ZD1211B_TXOP,		0x01800824 },
+		{ CR_SNIFFER_ON,		0 },
+		{ CR_RX_FILTER,			AP_RX_FILTER },
+		{ CR_GROUP_HASH_P1,		0x00 },
+		{ CR_GROUP_HASH_P2,		0x80000000 },
+		{ CR_REG1,			0xa4 },
+		{ CR_ADDA_PWR_DWN,		0x7f },
+		{ CR_BCN_PLCP_CFG,		0x00f00401 },
+		{ CR_PHY_DELAY,			0x00 },
+		{ CR_ACK_TIMEOUT_EXT,		0x80 },
+		{ CR_ADDA_PWR_DWN,		0x00 },
+		{ CR_ACK_TIME_80211,		0x100 },
+		{ CR_IFS_VALUE,			0x547c032 },
+		{ CR_RX_PE_DELAY,		0x70 },
+		{ CR_PS_CTRL,			0x10000000 },
+		{ CR_RTS_CTS_RATE,		0x02030203 },
+		{ CR_RX_THRESHOLD,		0x000c0640 },
+		{ CR_AFTER_PNP,			0x1 },
+		{ CR_WEP_PROTECT,		0x114 },
+	};
+
+	int r;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip),
+			"error in zd_iowrite32a_locked. Error number %d\n", r);
+	}
+	return r;
+}
+
+static int hw_init_hmac(struct zd_chip *chip)
+{
+	return chip->is_zd1211b ?
+		zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip);
+}
+
+struct aw_pt_bi {
+	u32 atim_wnd_period;
+	u32 pre_tbtt;
+	u32 beacon_interval;
+};
+
+static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
+{
+	int r;
+	static const zd_addr_t aw_pt_bi_addr[] =
+		{ CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL };
+	u32 values[3];
+
+	r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr,
+		         ARRAY_SIZE(aw_pt_bi_addr));
+	if (r) {
+		memset(s, 0, sizeof(*s));
+		return r;
+	}
+
+	s->atim_wnd_period = values[0];
+	s->pre_tbtt = values[1];
+	s->beacon_interval = values[2];
+	dev_dbg_f(zd_chip_dev(chip), "aw %u pt %u bi %u\n",
+		s->atim_wnd_period, s->pre_tbtt, s->beacon_interval);
+	return 0;
+}
+
+static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
+{
+	struct zd_ioreq32 reqs[3];
+
+	if (s->beacon_interval <= 5)
+		s->beacon_interval = 5;
+	if (s->pre_tbtt < 4 || s->pre_tbtt >= s->beacon_interval)
+		s->pre_tbtt = s->beacon_interval - 1;
+	if (s->atim_wnd_period >= s->pre_tbtt)
+		s->atim_wnd_period = s->pre_tbtt - 1;
+
+	reqs[0].addr = CR_ATIM_WND_PERIOD;
+	reqs[0].value = s->atim_wnd_period;
+	reqs[1].addr = CR_PRE_TBTT;
+	reqs[1].value = s->pre_tbtt;
+	reqs[2].addr = CR_BCN_INTERVAL;
+	reqs[2].value = s->beacon_interval;
+
+	dev_dbg_f(zd_chip_dev(chip),
+		"aw %u pt %u bi %u\n", s->atim_wnd_period, s->pre_tbtt,
+		                       s->beacon_interval);
+	return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
+}
+
+
+static int set_beacon_interval(struct zd_chip *chip, u32 interval)
+{
+	int r;
+	struct aw_pt_bi s;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = get_aw_pt_bi(chip, &s);
+	if (r)
+		return r;
+	s.beacon_interval = interval;
+	return set_aw_pt_bi(chip, &s);
+}
+
+int zd_set_beacon_interval(struct zd_chip *chip, u32 interval)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = set_beacon_interval(chip, interval);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int hw_init(struct zd_chip *chip)
+{
+	int r;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = hw_reset_phy(chip);
+	if (r)
+		return r;
+
+	r = hw_init_hmac(chip);
+	if (r)
+		return r;
+	r = set_beacon_interval(chip, 100);
+	if (r)
+		return r;
+	return 0;
+}
+
+#ifdef DEBUG
+static int dump_cr(struct zd_chip *chip, const zd_addr_t addr,
+	           const char *addr_string)
+{
+	int r;
+	u32 value;
+
+	r = zd_ioread32_locked(chip, &value, addr);
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip),
+			"error reading %s. Error number %d\n", addr_string, r);
+		return r;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n",
+		addr_string, (unsigned int)value);
+	return 0;
+}
+
+static int test_init(struct zd_chip *chip)
+{
+	int r;
+
+	r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP");
+	if (r)
+		return r;
+	r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN");
+	if (r)
+		return r;
+	return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT");
+}
+
+static void dump_fw_registers(struct zd_chip *chip)
+{
+	static const zd_addr_t addr[4] = {
+		FW_FIRMWARE_VER, FW_USB_SPEED, FW_FIX_TX_RATE,
+		FW_LINK_STATUS
+	};
+
+	int r;
+	u16 values[4];
+
+	r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr,
+		         ARRAY_SIZE(addr));
+	if (r) {
+		dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n",
+			 r);
+		return;
+	}
+
+	dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]);
+	dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]);
+	dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]);
+	dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]);
+}
+#endif /* DEBUG */
+
+static int print_fw_version(struct zd_chip *chip)
+{
+	int r;
+	u16 version;
+
+	r = zd_ioread16_locked(chip, &version, FW_FIRMWARE_VER);
+	if (r)
+		return r;
+
+	dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version);
+	return 0;
+}
+
+static int set_mandatory_rates(struct zd_chip *chip, enum ieee80211_std std)
+{
+	u32 rates;
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	/* This sets the mandatory rates, which only depend from the standard
+	 * that the device is supporting. Until further notice we should try
+	 * to support 802.11g also for full speed USB.
+	 */
+	switch (std) {
+	case IEEE80211B:
+		rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M;
+		break;
+	case IEEE80211G:
+		rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M|
+			CR_RATE_6M|CR_RATE_12M|CR_RATE_24M;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL);
+}
+
+int zd_chip_enable_hwint(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int disable_hwint(struct zd_chip *chip)
+{
+	return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT);
+}
+
+int zd_chip_disable_hwint(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = disable_hwint(chip);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_chip_init_hw(struct zd_chip *chip, u8 device_type)
+{
+	int r;
+	u8 rf_type;
+
+	dev_dbg_f(zd_chip_dev(chip), "\n");
+
+	mutex_lock(&chip->mutex);
+	chip->is_zd1211b = (device_type == DEVICE_ZD1211B) != 0;
+
+#ifdef DEBUG
+	r = test_init(chip);
+	if (r)
+		goto out;
+#endif
+	r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP);
+	if (r)
+		goto out;
+
+	r = zd_usb_init_hw(&chip->usb);
+	if (r)
+		goto out;
+
+	/* GPI is always disabled, also in the other driver.
+	 */
+	r = zd_iowrite32_locked(chip, 0, CR_GPI_EN);
+	if (r)
+		goto out;
+	r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX);
+	if (r)
+		goto out;
+	/* Currently we support IEEE 802.11g for full and high speed USB.
+	 * It might be discussed, whether we should suppport pure b mode for
+	 * full speed USB.
+	 */
+	r = set_mandatory_rates(chip, IEEE80211G);
+	if (r)
+		goto out;
+	/* Disabling interrupts is certainly a smart thing here.
+	 */
+	r = disable_hwint(chip);
+	if (r)
+		goto out;
+	r = read_pod(chip, &rf_type);
+	if (r)
+		goto out;
+	r = hw_init(chip);
+	if (r)
+		goto out;
+	r = zd_rf_init_hw(&chip->rf, rf_type);
+	if (r)
+		goto out;
+
+	r = print_fw_version(chip);
+	if (r)
+		goto out;
+
+#ifdef DEBUG
+	dump_fw_registers(chip);
+	r = test_init(chip);
+	if (r)
+		goto out;
+#endif /* DEBUG */
+
+	r = read_e2p_mac_addr(chip);
+	if (r)
+		goto out;
+
+	r = read_cal_int_tables(chip);
+	if (r)
+		goto out;
+
+	print_id(chip);
+out:
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int update_pwr_int(struct zd_chip *chip, u8 channel)
+{
+	u8 value = chip->pwr_int_values[channel - 1];
+	dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_int %#04x\n",
+		 channel, value);
+	return zd_iowrite32_locked(chip, value, CR31);
+}
+
+static int update_pwr_cal(struct zd_chip *chip, u8 channel)
+{
+	u8 value = chip->pwr_cal_values[channel-1];
+	dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_cal %#04x\n",
+		 channel, value);
+	return zd_iowrite32_locked(chip, value, CR68);
+}
+
+static int update_ofdm_cal(struct zd_chip *chip, u8 channel)
+{
+	struct zd_ioreq32 ioreqs[3];
+
+	ioreqs[0].addr = CR67;
+	ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1];
+	ioreqs[1].addr = CR66;
+	ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1];
+	ioreqs[2].addr = CR65;
+	ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1];
+
+	dev_dbg_f(zd_chip_dev(chip),
+		"channel %d ofdm_cal 36M %#04x 48M %#04x 54M %#04x\n",
+		channel, ioreqs[0].value, ioreqs[1].value, ioreqs[2].value);
+	return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int update_channel_integration_and_calibration(struct zd_chip *chip,
+	                                              u8 channel)
+{
+	int r;
+
+	r = update_pwr_int(chip, channel);
+	if (r)
+		return r;
+	if (chip->is_zd1211b) {
+		static const struct zd_ioreq32 ioreqs[] = {
+			{ CR69, 0x28 },
+			{},
+			{ CR69, 0x2a },
+		};
+
+		r = update_ofdm_cal(chip, channel);
+		if (r)
+			return r;
+		r = update_pwr_cal(chip, channel);
+		if (r)
+			return r;
+		r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+/* The CCK baseband gain can be optionally patched by the EEPROM */
+static int patch_cck_gain(struct zd_chip *chip)
+{
+	int r;
+	u32 value;
+
+	if (!chip->patch_cck_gain)
+		return 0;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread32_locked(chip, &value, E2P_PHY_REG);
+	if (r)
+		return r;
+	dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff);
+	return zd_iowrite32_locked(chip, value & 0xff, CR47);
+}
+
+int zd_chip_set_channel(struct zd_chip *chip, u8 channel)
+{
+	int r, t;
+
+	mutex_lock(&chip->mutex);
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		goto out;
+	r = zd_rf_set_channel(&chip->rf, channel);
+	if (r)
+		goto unlock;
+	r = update_channel_integration_and_calibration(chip, channel);
+	if (r)
+		goto unlock;
+	r = patch_cck_gain(chip);
+	if (r)
+		goto unlock;
+	r = patch_6m_band_edge(chip, channel);
+	if (r)
+		goto unlock;
+	r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS);
+unlock:
+	t = zd_chip_unlock_phy_regs(chip);
+	if (t && !r)
+		r = t;
+out:
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+u8 zd_chip_get_channel(struct zd_chip *chip)
+{
+	u8 channel;
+
+	mutex_lock(&chip->mutex);
+	channel = chip->rf.channel;
+	mutex_unlock(&chip->mutex);
+	return channel;
+}
+
+static u16 led_mask(int led)
+{
+	switch (led) {
+	case 1:
+		return LED1;
+	case 2:
+		return LED2;
+	default:
+		return 0;
+	}
+}
+
+static int read_led_reg(struct zd_chip *chip, u16 *status)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_ioread16_locked(chip, status, CR_LED);
+}
+
+static int write_led_reg(struct zd_chip *chip, u16 status)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_iowrite16_locked(chip, status, CR_LED);
+}
+
+int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status)
+{
+	int r, ret;
+	u16 mask = led_mask(led);
+	u16 reg;
+
+	if (!mask)
+		return -EINVAL;
+	mutex_lock(&chip->mutex);
+	r = read_led_reg(chip, &reg);
+	if (r)
+		return r;
+	switch (status) {
+	case LED_STATUS:
+		return (reg & mask) ? LED_ON : LED_OFF;
+	case LED_OFF:
+		reg &= ~mask;
+		ret = LED_OFF;
+		break;
+	case LED_FLIP:
+		reg ^= mask;
+		ret = (reg&mask) ? LED_ON : LED_OFF;
+		break;
+	case LED_ON:
+		reg |= mask;
+		ret = LED_ON;
+		break;
+	default:
+		return -EINVAL;
+	}
+	r = write_led_reg(chip, reg);
+	if (r) {
+		ret = r;
+		goto out;
+	}
+out:
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_chip_led_flip(struct zd_chip *chip, int led,
+	const unsigned int *phases_msecs, unsigned int count)
+{
+	int i, r;
+	enum led_status status;
+
+	r = zd_chip_led_status(chip, led, LED_STATUS);
+	if (r)
+		return r;
+	status = r;
+	for (i = 0; i < count; i++) {
+		r = zd_chip_led_status(chip, led, LED_FLIP);
+		if (r < 0)
+			goto out;
+		msleep(phases_msecs[i]);
+	}
+
+out:
+	zd_chip_led_status(chip, led, status);
+	return r;
+}
+
+int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates)
+{
+	int r;
+
+	if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G))
+		return -EINVAL;
+
+	mutex_lock(&chip->mutex);
+	r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+static int ofdm_qual_db(u8 status_quality, u8 rate, unsigned int size)
+{
+	static const u16 constants[] = {
+		715, 655, 585, 540, 470, 410, 360, 315,
+		270, 235, 205, 175, 150, 125, 105,  85,
+		 65,  50,  40,  25,  15
+	};
+
+	int i;
+	u32 x;
+
+	/* It seems that their quality parameter is somehow per signal
+	 * and is now transferred per bit.
+	 */
+	switch (rate) {
+	case ZD_OFDM_RATE_6M:
+	case ZD_OFDM_RATE_12M:
+	case ZD_OFDM_RATE_24M:
+		size *= 2;
+		break;
+	case ZD_OFDM_RATE_9M:
+	case ZD_OFDM_RATE_18M:
+	case ZD_OFDM_RATE_36M:
+	case ZD_OFDM_RATE_54M:
+		size *= 4;
+		size /= 3;
+		break;
+	case ZD_OFDM_RATE_48M:
+		size *= 3;
+		size /= 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	x = (10000 * status_quality)/size;
+	for (i = 0; i < ARRAY_SIZE(constants); i++) {
+		if (x > constants[i])
+			break;
+	}
+
+	return i;
+}
+
+static unsigned int log10times100(unsigned int x)
+{
+	static const u8 log10[] = {
+		  0,
+		  0,   30,   47,   60,   69,   77,   84,   90,   95,  100,
+		104,  107,  111,  114,  117,  120,  123,  125,  127,  130,
+		132,  134,  136,  138,  139,  141,  143,  144,  146,  147,
+		149,  150,  151,  153,  154,  155,  156,  157,  159,  160,
+		161,  162,  163,  164,  165,  166,  167,  168,  169,  169,
+		170,  171,  172,  173,  174,  174,  175,  176,  177,  177,
+		178,  179,  179,  180,  181,  181,  182,  183,  183,  184,
+		185,  185,  186,  186,  187,  188,  188,  189,  189,  190,
+		190,  191,  191,  192,  192,  193,  193,  194,  194,  195,
+		195,  196,  196,  197,  197,  198,  198,  199,  199,  200,
+		200,  200,  201,  201,  202,  202,  202,  203,  203,  204,
+		204,  204,  205,  205,  206,  206,  206,  207,  207,  207,
+		208,  208,  208,  209,  209,  210,  210,  210,  211,  211,
+		211,  212,  212,  212,  213,  213,  213,  213,  214,  214,
+		214,  215,  215,  215,  216,  216,  216,  217,  217,  217,
+		217,  218,  218,  218,  219,  219,  219,  219,  220,  220,
+		220,  220,  221,  221,  221,  222,  222,  222,  222,  223,
+		223,  223,  223,  224,  224,  224,  224,
+	};
+
+	return x < ARRAY_SIZE(log10) ? log10[x] : 225;
+}
+
+enum {
+	MAX_CCK_EVM_DB = 45,
+};
+
+static int cck_evm_db(u8 status_quality)
+{
+	return (20 * log10times100(status_quality)) / 100;
+}
+
+static int cck_snr_db(u8 status_quality)
+{
+	int r = MAX_CCK_EVM_DB - cck_evm_db(status_quality);
+	ZD_ASSERT(r >= 0);
+	return r;
+}
+
+static int rx_qual_db(const void *rx_frame, unsigned int size,
+	              const struct rx_status *status)
+{
+	return (status->frame_status&ZD_RX_OFDM) ?
+		ofdm_qual_db(status->signal_quality_ofdm,
+			     zd_ofdm_plcp_header_rate(rx_frame),
+			     size) :
+		cck_snr_db(status->signal_quality_cck);
+}
+
+u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size,
+	              const struct rx_status *status)
+{
+	int r = rx_qual_db(rx_frame, size, status);
+	if (r < 0)
+		r = 0;
+	r = (r * 100) / 14;
+	if (r > 100)
+		r = 100;
+	return r;
+}
+
+u8 zd_rx_strength_percent(u8 rssi)
+{
+	int r = (rssi*100) / 30;
+	if (r > 100)
+		r = 100;
+	return (u8) r;
+}
+
+u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status)
+{
+	static const u16 ofdm_rates[] = {
+		[ZD_OFDM_RATE_6M]  = 60,
+		[ZD_OFDM_RATE_9M]  = 90,
+		[ZD_OFDM_RATE_12M] = 120,
+		[ZD_OFDM_RATE_18M] = 180,
+		[ZD_OFDM_RATE_24M] = 240,
+		[ZD_OFDM_RATE_36M] = 360,
+		[ZD_OFDM_RATE_48M] = 480,
+		[ZD_OFDM_RATE_54M] = 540,
+	};
+	u16 rate;
+	if (status->frame_status & ZD_RX_OFDM) {
+		u8 ofdm_rate = zd_ofdm_plcp_header_rate(rx_frame);
+		rate = ofdm_rates[ofdm_rate & 0xf];
+	} else {
+		u8 cck_rate = zd_cck_plcp_header_rate(rx_frame);
+		switch (cck_rate) {
+		case ZD_CCK_SIGNAL_1M:
+			rate = 10;
+			break;
+		case ZD_CCK_SIGNAL_2M:
+			rate = 20;
+			break;
+		case ZD_CCK_SIGNAL_5M5:
+			rate = 55;
+			break;
+		case ZD_CCK_SIGNAL_11M:
+			rate = 110;
+			break;
+		default:
+			rate = 0;
+		}
+	}
+
+	return rate;
+}
+
+int zd_chip_switch_radio_on(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_switch_radio_on(&chip->rf);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_chip_switch_radio_off(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_switch_radio_off(&chip->rf);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+int zd_chip_enable_int(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_usb_enable_int(&chip->usb);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+void zd_chip_disable_int(struct zd_chip *chip)
+{
+	mutex_lock(&chip->mutex);
+	zd_usb_disable_int(&chip->usb);
+	mutex_unlock(&chip->mutex);
+}
+
+int zd_chip_enable_rx(struct zd_chip *chip)
+{
+	int r;
+
+	mutex_lock(&chip->mutex);
+	r = zd_usb_enable_rx(&chip->usb);
+	mutex_unlock(&chip->mutex);
+	return r;
+}
+
+void zd_chip_disable_rx(struct zd_chip *chip)
+{
+	mutex_lock(&chip->mutex);
+	zd_usb_disable_rx(&chip->usb);
+	mutex_unlock(&chip->mutex);
+}
+
+int zd_rfwritev_locked(struct zd_chip *chip,
+	               const u32* values, unsigned int count, u8 bits)
+{
+	int r;
+	unsigned int i;
+
+	for (i = 0; i < count; i++) {
+		r = zd_rfwrite_locked(chip, values[i], bits);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
new file mode 100644
index 0000000..8051210
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -0,0 +1,825 @@
+/* zd_chip.h
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_CHIP_H
+#define _ZD_CHIP_H
+
+#include "zd_types.h"
+#include "zd_rf.h"
+#include "zd_usb.h"
+
+/* Header for the Media Access Controller (MAC) and the Baseband Processor
+ * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and
+ * adds a processor for handling the USB protocol.
+ */
+
+/* 8-bit hardware registers */
+#define CR0   CTL_REG(0x0000)
+#define CR1   CTL_REG(0x0004)
+#define CR2   CTL_REG(0x0008)
+#define CR3   CTL_REG(0x000C)
+
+#define CR5   CTL_REG(0x0010)
+/*	bit 5: if set short preamble used
+ *	bit 6: filter band - Japan channel 14 on, else off
+ */
+#define CR6   CTL_REG(0x0014)
+#define CR7   CTL_REG(0x0018)
+#define CR8   CTL_REG(0x001C)
+
+#define CR4   CTL_REG(0x0020)
+
+#define CR9   CTL_REG(0x0024)
+/*	bit 2: antenna switch (together with CR10) */
+#define CR10  CTL_REG(0x0028)
+/*	bit 1: antenna switch (together with CR9)
+ *	RF2959 controls with CR11 radion on and off
+ */
+#define CR11  CTL_REG(0x002C)
+/*	bit 6:  TX power control for OFDM
+ *	RF2959 controls with CR10 radio on and off
+ */
+#define CR12  CTL_REG(0x0030)
+#define CR13  CTL_REG(0x0034)
+#define CR14  CTL_REG(0x0038)
+#define CR15  CTL_REG(0x003C)
+#define CR16  CTL_REG(0x0040)
+#define CR17  CTL_REG(0x0044)
+#define CR18  CTL_REG(0x0048)
+#define CR19  CTL_REG(0x004C)
+#define CR20  CTL_REG(0x0050)
+#define CR21  CTL_REG(0x0054)
+#define CR22  CTL_REG(0x0058)
+#define CR23  CTL_REG(0x005C)
+#define CR24  CTL_REG(0x0060)	/* CCA threshold */
+#define CR25  CTL_REG(0x0064)
+#define CR26  CTL_REG(0x0068)
+#define CR27  CTL_REG(0x006C)
+#define CR28  CTL_REG(0x0070)
+#define CR29  CTL_REG(0x0074)
+#define CR30  CTL_REG(0x0078)
+#define CR31  CTL_REG(0x007C)	/* TX power control for RF in CCK mode */
+#define CR32  CTL_REG(0x0080)
+#define CR33  CTL_REG(0x0084)
+#define CR34  CTL_REG(0x0088)
+#define CR35  CTL_REG(0x008C)
+#define CR36  CTL_REG(0x0090)
+#define CR37  CTL_REG(0x0094)
+#define CR38  CTL_REG(0x0098)
+#define CR39  CTL_REG(0x009C)
+#define CR40  CTL_REG(0x00A0)
+#define CR41  CTL_REG(0x00A4)
+#define CR42  CTL_REG(0x00A8)
+#define CR43  CTL_REG(0x00AC)
+#define CR44  CTL_REG(0x00B0)
+#define CR45  CTL_REG(0x00B4)
+#define CR46  CTL_REG(0x00B8)
+#define CR47  CTL_REG(0x00BC)	/* CCK baseband gain
+	                         * (patch value might be in EEPROM)
+				 */
+#define CR48  CTL_REG(0x00C0)
+#define CR49  CTL_REG(0x00C4)
+#define CR50  CTL_REG(0x00C8)
+#define CR51  CTL_REG(0x00CC)	/* TX power control for RF in 6-36M modes */
+#define CR52  CTL_REG(0x00D0)	/* TX power control for RF in 48M mode */
+#define CR53  CTL_REG(0x00D4)	/* TX power control for RF in 54M mode */
+#define CR54  CTL_REG(0x00D8)
+#define CR55  CTL_REG(0x00DC)
+#define CR56  CTL_REG(0x00E0)
+#define CR57  CTL_REG(0x00E4)
+#define CR58  CTL_REG(0x00E8)
+#define CR59  CTL_REG(0x00EC)
+#define CR60  CTL_REG(0x00F0)
+#define CR61  CTL_REG(0x00F4)
+#define CR62  CTL_REG(0x00F8)
+#define CR63  CTL_REG(0x00FC)
+#define CR64  CTL_REG(0x0100)
+#define CR65  CTL_REG(0x0104) /* OFDM 54M calibration */
+#define CR66  CTL_REG(0x0108) /* OFDM 48M calibration */
+#define CR67  CTL_REG(0x010C) /* OFDM 36M calibration */
+#define CR68  CTL_REG(0x0110) /* CCK calibration */
+#define CR69  CTL_REG(0x0114)
+#define CR70  CTL_REG(0x0118)
+#define CR71  CTL_REG(0x011C)
+#define CR72  CTL_REG(0x0120)
+#define CR73  CTL_REG(0x0124)
+#define CR74  CTL_REG(0x0128)
+#define CR75  CTL_REG(0x012C)
+#define CR76  CTL_REG(0x0130)
+#define CR77  CTL_REG(0x0134)
+#define CR78  CTL_REG(0x0138)
+#define CR79  CTL_REG(0x013C)
+#define CR80  CTL_REG(0x0140)
+#define CR81  CTL_REG(0x0144)
+#define CR82  CTL_REG(0x0148)
+#define CR83  CTL_REG(0x014C)
+#define CR84  CTL_REG(0x0150)
+#define CR85  CTL_REG(0x0154)
+#define CR86  CTL_REG(0x0158)
+#define CR87  CTL_REG(0x015C)
+#define CR88  CTL_REG(0x0160)
+#define CR89  CTL_REG(0x0164)
+#define CR90  CTL_REG(0x0168)
+#define CR91  CTL_REG(0x016C)
+#define CR92  CTL_REG(0x0170)
+#define CR93  CTL_REG(0x0174)
+#define CR94  CTL_REG(0x0178)
+#define CR95  CTL_REG(0x017C)
+#define CR96  CTL_REG(0x0180)
+#define CR97  CTL_REG(0x0184)
+#define CR98  CTL_REG(0x0188)
+#define CR99  CTL_REG(0x018C)
+#define CR100 CTL_REG(0x0190)
+#define CR101 CTL_REG(0x0194)
+#define CR102 CTL_REG(0x0198)
+#define CR103 CTL_REG(0x019C)
+#define CR104 CTL_REG(0x01A0)
+#define CR105 CTL_REG(0x01A4)
+#define CR106 CTL_REG(0x01A8)
+#define CR107 CTL_REG(0x01AC)
+#define CR108 CTL_REG(0x01B0)
+#define CR109 CTL_REG(0x01B4)
+#define CR110 CTL_REG(0x01B8)
+#define CR111 CTL_REG(0x01BC)
+#define CR112 CTL_REG(0x01C0)
+#define CR113 CTL_REG(0x01C4)
+#define CR114 CTL_REG(0x01C8)
+#define CR115 CTL_REG(0x01CC)
+#define CR116 CTL_REG(0x01D0)
+#define CR117 CTL_REG(0x01D4)
+#define CR118 CTL_REG(0x01D8)
+#define CR119 CTL_REG(0x01DC)
+#define CR120 CTL_REG(0x01E0)
+#define CR121 CTL_REG(0x01E4)
+#define CR122 CTL_REG(0x01E8)
+#define CR123 CTL_REG(0x01EC)
+#define CR124 CTL_REG(0x01F0)
+#define CR125 CTL_REG(0x01F4)
+#define CR126 CTL_REG(0x01F8)
+#define CR127 CTL_REG(0x01FC)
+#define CR128 CTL_REG(0x0200)
+#define CR129 CTL_REG(0x0204)
+#define CR130 CTL_REG(0x0208)
+#define CR131 CTL_REG(0x020C)
+#define CR132 CTL_REG(0x0210)
+#define CR133 CTL_REG(0x0214)
+#define CR134 CTL_REG(0x0218)
+#define CR135 CTL_REG(0x021C)
+#define CR136 CTL_REG(0x0220)
+#define CR137 CTL_REG(0x0224)
+#define CR138 CTL_REG(0x0228)
+#define CR139 CTL_REG(0x022C)
+#define CR140 CTL_REG(0x0230)
+#define CR141 CTL_REG(0x0234)
+#define CR142 CTL_REG(0x0238)
+#define CR143 CTL_REG(0x023C)
+#define CR144 CTL_REG(0x0240)
+#define CR145 CTL_REG(0x0244)
+#define CR146 CTL_REG(0x0248)
+#define CR147 CTL_REG(0x024C)
+#define CR148 CTL_REG(0x0250)
+#define CR149 CTL_REG(0x0254)
+#define CR150 CTL_REG(0x0258)
+#define CR151 CTL_REG(0x025C)
+#define CR152 CTL_REG(0x0260)
+#define CR153 CTL_REG(0x0264)
+#define CR154 CTL_REG(0x0268)
+#define CR155 CTL_REG(0x026C)
+#define CR156 CTL_REG(0x0270)
+#define CR157 CTL_REG(0x0274)
+#define CR158 CTL_REG(0x0278)
+#define CR159 CTL_REG(0x027C)
+#define CR160 CTL_REG(0x0280)
+#define CR161 CTL_REG(0x0284)
+#define CR162 CTL_REG(0x0288)
+#define CR163 CTL_REG(0x028C)
+#define CR164 CTL_REG(0x0290)
+#define CR165 CTL_REG(0x0294)
+#define CR166 CTL_REG(0x0298)
+#define CR167 CTL_REG(0x029C)
+#define CR168 CTL_REG(0x02A0)
+#define CR169 CTL_REG(0x02A4)
+#define CR170 CTL_REG(0x02A8)
+#define CR171 CTL_REG(0x02AC)
+#define CR172 CTL_REG(0x02B0)
+#define CR173 CTL_REG(0x02B4)
+#define CR174 CTL_REG(0x02B8)
+#define CR175 CTL_REG(0x02BC)
+#define CR176 CTL_REG(0x02C0)
+#define CR177 CTL_REG(0x02C4)
+#define CR178 CTL_REG(0x02C8)
+#define CR179 CTL_REG(0x02CC)
+#define CR180 CTL_REG(0x02D0)
+#define CR181 CTL_REG(0x02D4)
+#define CR182 CTL_REG(0x02D8)
+#define CR183 CTL_REG(0x02DC)
+#define CR184 CTL_REG(0x02E0)
+#define CR185 CTL_REG(0x02E4)
+#define CR186 CTL_REG(0x02E8)
+#define CR187 CTL_REG(0x02EC)
+#define CR188 CTL_REG(0x02F0)
+#define CR189 CTL_REG(0x02F4)
+#define CR190 CTL_REG(0x02F8)
+#define CR191 CTL_REG(0x02FC)
+#define CR192 CTL_REG(0x0300)
+#define CR193 CTL_REG(0x0304)
+#define CR194 CTL_REG(0x0308)
+#define CR195 CTL_REG(0x030C)
+#define CR196 CTL_REG(0x0310)
+#define CR197 CTL_REG(0x0314)
+#define CR198 CTL_REG(0x0318)
+#define CR199 CTL_REG(0x031C)
+#define CR200 CTL_REG(0x0320)
+#define CR201 CTL_REG(0x0324)
+#define CR202 CTL_REG(0x0328)
+#define CR203 CTL_REG(0x032C)	/* I2C bus template value & flash control */
+#define CR204 CTL_REG(0x0330)
+#define CR205 CTL_REG(0x0334)
+#define CR206 CTL_REG(0x0338)
+#define CR207 CTL_REG(0x033C)
+#define CR208 CTL_REG(0x0340)
+#define CR209 CTL_REG(0x0344)
+#define CR210 CTL_REG(0x0348)
+#define CR211 CTL_REG(0x034C)
+#define CR212 CTL_REG(0x0350)
+#define CR213 CTL_REG(0x0354)
+#define CR214 CTL_REG(0x0358)
+#define CR215 CTL_REG(0x035C)
+#define CR216 CTL_REG(0x0360)
+#define CR217 CTL_REG(0x0364)
+#define CR218 CTL_REG(0x0368)
+#define CR219 CTL_REG(0x036C)
+#define CR220 CTL_REG(0x0370)
+#define CR221 CTL_REG(0x0374)
+#define CR222 CTL_REG(0x0378)
+#define CR223 CTL_REG(0x037C)
+#define CR224 CTL_REG(0x0380)
+#define CR225 CTL_REG(0x0384)
+#define CR226 CTL_REG(0x0388)
+#define CR227 CTL_REG(0x038C)
+#define CR228 CTL_REG(0x0390)
+#define CR229 CTL_REG(0x0394)
+#define CR230 CTL_REG(0x0398)
+#define CR231 CTL_REG(0x039C)
+#define CR232 CTL_REG(0x03A0)
+#define CR233 CTL_REG(0x03A4)
+#define CR234 CTL_REG(0x03A8)
+#define CR235 CTL_REG(0x03AC)
+#define CR236 CTL_REG(0x03B0)
+
+#define CR240 CTL_REG(0x03C0)
+/*	bit 7:  host-controlled RF register writes
+ * CR241-CR245: for hardware controlled writing of RF bits, not needed for
+ *              USB
+ */
+#define CR241 CTL_REG(0x03C4)
+#define CR242 CTL_REG(0x03C8)
+#define CR243 CTL_REG(0x03CC)
+#define CR244 CTL_REG(0x03D0)
+#define CR245 CTL_REG(0x03D4)
+
+#define CR251 CTL_REG(0x03EC)	/* only used for activation and deactivation of
+				 * Airoha RFs AL2230 and AL7230B
+				 */
+#define CR252 CTL_REG(0x03F0)
+#define CR253 CTL_REG(0x03F4)
+#define CR254 CTL_REG(0x03F8)
+#define CR255 CTL_REG(0x03FC)
+
+#define CR_MAX_PHY_REG 255
+
+/* Taken from the ZYDAS driver, not all of them are relevant for the ZSD1211
+ * driver.
+ */
+
+#define CR_RF_IF_CLK			CTL_REG(0x0400)
+#define CR_RF_IF_DATA			CTL_REG(0x0404)
+#define CR_PE1_PE2			CTL_REG(0x0408)
+#define CR_PE2_DLY			CTL_REG(0x040C)
+#define CR_LE1				CTL_REG(0x0410)
+#define CR_LE2				CTL_REG(0x0414)
+/* Seems to enable/disable GPI (General Purpose IO?) */
+#define CR_GPI_EN			CTL_REG(0x0418)
+#define CR_RADIO_PD			CTL_REG(0x042C)
+#define CR_RF2948_PD			CTL_REG(0x042C)
+#define CR_ENABLE_PS_MANUAL_AGC		CTL_REG(0x043C)
+#define CR_CONFIG_PHILIPS		CTL_REG(0x0440)
+#define CR_SA2400_SER_AP		CTL_REG(0x0444)
+#define CR_I2C_WRITE			CTL_REG(0x0444)
+#define CR_SA2400_SER_RP		CTL_REG(0x0448)
+#define CR_RADIO_PE			CTL_REG(0x0458)
+#define CR_RST_BUS_MASTER		CTL_REG(0x045C)
+#define CR_RFCFG			CTL_REG(0x0464)
+#define CR_HSTSCHG			CTL_REG(0x046C)
+#define CR_PHY_ON			CTL_REG(0x0474)
+#define CR_RX_DELAY			CTL_REG(0x0478)
+#define CR_RX_PE_DELAY			CTL_REG(0x047C)
+#define CR_GPIO_1			CTL_REG(0x0490)
+#define CR_GPIO_2			CTL_REG(0x0494)
+#define CR_EncryBufMux			CTL_REG(0x04A8)
+#define CR_PS_CTRL			CTL_REG(0x0500)
+#define CR_ADDA_PWR_DWN			CTL_REG(0x0504)
+#define CR_ADDA_MBIAS_WARMTIME		CTL_REG(0x0508)
+#define CR_MAC_PS_STATE			CTL_REG(0x050C)
+
+#define CR_INTERRUPT			CTL_REG(0x0510)
+#define INT_TX_COMPLETE			0x00000001
+#define INT_RX_COMPLETE			0x00000002
+#define INT_RETRY_FAIL			0x00000004
+#define INT_WAKEUP			0x00000008
+#define INT_DTIM_NOTIFY			0x00000020
+#define INT_CFG_NEXT_BCN		0x00000040
+#define INT_BUS_ABORT			0x00000080
+#define INT_TX_FIFO_READY		0x00000100
+#define INT_UART			0x00000200
+#define INT_TX_COMPLETE_EN		0x00010000
+#define INT_RX_COMPLETE_EN		0x00020000
+#define INT_RETRY_FAIL_EN		0x00040000
+#define INT_WAKEUP_EN			0x00080000
+#define INT_DTIM_NOTIFY_EN		0x00200000
+#define INT_CFG_NEXT_BCN_EN		0x00400000
+#define INT_BUS_ABORT_EN		0x00800000
+#define INT_TX_FIFO_READY_EN		0x01000000
+#define INT_UART_EN			0x02000000
+
+#define CR_TSF_LOW_PART			CTL_REG(0x0514)
+#define CR_TSF_HIGH_PART		CTL_REG(0x0518)
+
+/* Following three values are in time units (1024us)
+ * Following condition must be met:
+ * atim < tbtt < bcn
+ */
+#define CR_ATIM_WND_PERIOD		CTL_REG(0x051C)
+#define CR_BCN_INTERVAL			CTL_REG(0x0520)
+#define CR_PRE_TBTT			CTL_REG(0x0524)
+/* in units of TU(1024us) */
+
+/* for UART support */
+#define CR_UART_RBR_THR_DLL		CTL_REG(0x0540)
+#define CR_UART_DLM_IER			CTL_REG(0x0544)
+#define CR_UART_IIR_FCR			CTL_REG(0x0548)
+#define CR_UART_LCR			CTL_REG(0x054c)
+#define CR_UART_MCR			CTL_REG(0x0550)
+#define CR_UART_LSR			CTL_REG(0x0554)
+#define CR_UART_MSR			CTL_REG(0x0558)
+#define CR_UART_ECR			CTL_REG(0x055c)
+#define CR_UART_STATUS			CTL_REG(0x0560)
+
+#define CR_PCI_TX_ADDR_P1		CTL_REG(0x0600)
+#define CR_PCI_TX_AddR_P2		CTL_REG(0x0604)
+#define CR_PCI_RX_AddR_P1		CTL_REG(0x0608)
+#define CR_PCI_RX_AddR_P2		CTL_REG(0x060C)
+
+/* must be overwritten if custom MAC address will be used */
+#define CR_MAC_ADDR_P1			CTL_REG(0x0610)
+#define CR_MAC_ADDR_P2			CTL_REG(0x0614)
+#define CR_BSSID_P1			CTL_REG(0x0618)
+#define CR_BSSID_P2			CTL_REG(0x061C)
+#define CR_BCN_PLCP_CFG			CTL_REG(0x0620)
+#define CR_GROUP_HASH_P1		CTL_REG(0x0624)
+#define CR_GROUP_HASH_P2		CTL_REG(0x0628)
+#define CR_RX_TIMEOUT			CTL_REG(0x062C)
+
+/* Basic rates supported by the BSS. When producing ACK or CTS messages, the
+ * device will use a rate in this table that is less than or equal to the rate
+ * of the incoming frame which prompted the response */
+#define CR_BASIC_RATE_TBL		CTL_REG(0x0630)
+#define CR_RATE_1M	0x0001	/* 802.11b */
+#define CR_RATE_2M	0x0002	/* 802.11b */
+#define CR_RATE_5_5M	0x0004	/* 802.11b */
+#define CR_RATE_11M	0x0008	/* 802.11b */
+#define CR_RATE_6M      0x0100	/* 802.11g */
+#define CR_RATE_9M      0x0200	/* 802.11g */
+#define CR_RATE_12M	0x0400	/* 802.11g */
+#define CR_RATE_18M	0x0800	/* 802.11g */
+#define CR_RATE_24M     0x1000	/* 802.11g */
+#define CR_RATE_36M     0x2000	/* 802.11g */
+#define CR_RATE_48M     0x4000	/* 802.11g */
+#define CR_RATE_54M     0x8000	/* 802.11g */
+#define CR_RATES_80211G	0xff00
+#define CR_RATES_80211B	0x000f
+
+/* Mandatory rates required in the BSS. When producing ACK or CTS messages, if
+ * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will
+ * look for a rate in this table that is less than or equal to the rate of
+ * the incoming frame. */
+#define CR_MANDATORY_RATE_TBL		CTL_REG(0x0634)
+#define CR_RTS_CTS_RATE			CTL_REG(0x0638)
+
+#define CR_WEP_PROTECT			CTL_REG(0x063C)
+#define CR_RX_THRESHOLD			CTL_REG(0x0640)
+
+/* register for controlling the LEDS */
+#define CR_LED				CTL_REG(0x0644)
+/* masks for controlling LEDs */
+#define LED1				0x0100
+#define LED2				0x0200
+
+/* Seems to indicate that the configuration is over.
+ */
+#define CR_AFTER_PNP			CTL_REG(0x0648)
+#define CR_ACK_TIME_80211		CTL_REG(0x0658)
+
+#define CR_RX_OFFSET			CTL_REG(0x065c)
+
+#define CR_PHY_DELAY			CTL_REG(0x066C)
+#define CR_BCN_FIFO			CTL_REG(0x0670)
+#define CR_SNIFFER_ON			CTL_REG(0x0674)
+
+#define CR_ENCRYPTION_TYPE		CTL_REG(0x0678)
+#define NO_WEP				0
+#define WEP64				1
+#define WEP128				5
+#define WEP256				6
+#define ENC_SNIFFER			8
+
+#define CR_ZD1211_RETRY_MAX		CTL_REG(0x067C)
+
+#define CR_REG1				CTL_REG(0x0680)
+/* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical
+ * registers, so one could argue it is a LOCK bit. But calling it
+ * LOCK_PHY_REGS makes it confusing.
+ */
+#define UNLOCK_PHY_REGS			0x0080
+
+#define CR_DEVICE_STATE			CTL_REG(0x0684)
+#define CR_UNDERRUN_CNT			CTL_REG(0x0688)
+
+#define CR_RX_FILTER			CTL_REG(0x068c)
+#define RX_FILTER_ASSOC_RESPONSE	0x0002
+#define RX_FILTER_PROBE_RESPONSE	0x0020
+#define RX_FILTER_BEACON		0x0100
+#define RX_FILTER_AUTH			0x0800
+/* Sniff modus sets filter to 0xfffff */
+
+#define CR_ACK_TIMEOUT_EXT		CTL_REG(0x0690)
+#define CR_BCN_FIFO_SEMAPHORE		CTL_REG(0x0694)
+#define CR_IFS_VALUE			CTL_REG(0x0698)
+#define CR_RX_TIME_OUT			CTL_REG(0x069C)
+#define CR_TOTAL_RX_FRM			CTL_REG(0x06A0)
+#define CR_CRC32_CNT			CTL_REG(0x06A4)
+#define CR_CRC16_CNT			CTL_REG(0x06A8)
+#define CR_DECRYPTION_ERR_UNI		CTL_REG(0x06AC)
+#define CR_RX_FIFO_OVERRUN		CTL_REG(0x06B0)
+
+#define CR_DECRYPTION_ERR_MUL		CTL_REG(0x06BC)
+
+#define CR_NAV_CNT			CTL_REG(0x06C4)
+#define CR_NAV_CCA			CTL_REG(0x06C8)
+#define CR_RETRY_CNT			CTL_REG(0x06CC)
+
+#define CR_READ_TCB_ADDR		CTL_REG(0x06E8)
+#define CR_READ_RFD_ADDR		CTL_REG(0x06EC)
+#define CR_CWMIN_CWMAX			CTL_REG(0x06F0)
+#define CR_TOTAL_TX_FRM			CTL_REG(0x06F4)
+
+/* CAM: Continuous Access Mode (power management) */
+#define CR_CAM_MODE			CTL_REG(0x0700)
+#define CR_CAM_ROLL_TB_LOW		CTL_REG(0x0704)
+#define CR_CAM_ROLL_TB_HIGH		CTL_REG(0x0708)
+#define CR_CAM_ADDRESS			CTL_REG(0x070C)
+#define CR_CAM_DATA			CTL_REG(0x0710)
+
+#define CR_ROMDIR			CTL_REG(0x0714)
+
+#define CR_DECRY_ERR_FLG_LOW		CTL_REG(0x0714)
+#define CR_DECRY_ERR_FLG_HIGH		CTL_REG(0x0718)
+
+#define CR_WEPKEY0			CTL_REG(0x0720)
+#define CR_WEPKEY1			CTL_REG(0x0724)
+#define CR_WEPKEY2			CTL_REG(0x0728)
+#define CR_WEPKEY3			CTL_REG(0x072C)
+#define CR_WEPKEY4			CTL_REG(0x0730)
+#define CR_WEPKEY5			CTL_REG(0x0734)
+#define CR_WEPKEY6			CTL_REG(0x0738)
+#define CR_WEPKEY7			CTL_REG(0x073C)
+#define CR_WEPKEY8			CTL_REG(0x0740)
+#define CR_WEPKEY9			CTL_REG(0x0744)
+#define CR_WEPKEY10			CTL_REG(0x0748)
+#define CR_WEPKEY11			CTL_REG(0x074C)
+#define CR_WEPKEY12			CTL_REG(0x0750)
+#define CR_WEPKEY13			CTL_REG(0x0754)
+#define CR_WEPKEY14			CTL_REG(0x0758)
+#define CR_WEPKEY15			CTL_REG(0x075c)
+#define CR_TKIP_MODE			CTL_REG(0x0760)
+
+#define CR_EEPROM_PROTECT0		CTL_REG(0x0758)
+#define CR_EEPROM_PROTECT1		CTL_REG(0x075C)
+
+#define CR_DBG_FIFO_RD			CTL_REG(0x0800)
+#define CR_DBG_SELECT			CTL_REG(0x0804)
+#define CR_FIFO_Length			CTL_REG(0x0808)
+
+
+#define CR_RSSI_MGC			CTL_REG(0x0810)
+
+#define CR_PON				CTL_REG(0x0818)
+#define CR_RX_ON			CTL_REG(0x081C)
+#define CR_TX_ON			CTL_REG(0x0820)
+#define CR_CHIP_EN			CTL_REG(0x0824)
+#define CR_LO_SW			CTL_REG(0x0828)
+#define CR_TXRX_SW			CTL_REG(0x082C)
+#define CR_S_MD				CTL_REG(0x0830)
+
+#define CR_USB_DEBUG_PORT		CTL_REG(0x0888)
+
+#define CR_ZD1211B_TX_PWR_CTL1		CTL_REG(0x0b00)
+#define CR_ZD1211B_TX_PWR_CTL2		CTL_REG(0x0b04)
+#define CR_ZD1211B_TX_PWR_CTL3		CTL_REG(0x0b08)
+#define CR_ZD1211B_TX_PWR_CTL4		CTL_REG(0x0b0c)
+#define CR_ZD1211B_AIFS_CTL1		CTL_REG(0x0b10)
+#define CR_ZD1211B_AIFS_CTL2		CTL_REG(0x0b14)
+#define CR_ZD1211B_TXOP			CTL_REG(0x0b20)
+#define CR_ZD1211B_RETRY_MAX		CTL_REG(0x0b28)
+
+#define AP_RX_FILTER			0x0400feff
+#define STA_RX_FILTER			0x0000ffff
+
+#define CWIN_SIZE			0x007f043f
+
+
+#define HWINT_ENABLED			0x004f0000
+#define HWINT_DISABLED			0
+
+#define E2P_PWR_INT_GUARD		8
+#define E2P_CHANNEL_COUNT		14
+
+/* If you compare this addresses with the ZYDAS orignal driver, please notify
+ * that we use word mapping for the EEPROM.
+ */
+
+/*
+ * Upper 16 bit contains the regulatory domain.
+ */
+#define E2P_SUBID		E2P_REG(0x00)
+#define E2P_POD			E2P_REG(0x02)
+#define E2P_MAC_ADDR_P1		E2P_REG(0x04)
+#define E2P_MAC_ADDR_P2		E2P_REG(0x06)
+#define E2P_PWR_CAL_VALUE1	E2P_REG(0x08)
+#define E2P_PWR_CAL_VALUE2	E2P_REG(0x0a)
+#define E2P_PWR_CAL_VALUE3	E2P_REG(0x0c)
+#define E2P_PWR_CAL_VALUE4      E2P_REG(0x0e)
+#define E2P_PWR_INT_VALUE1	E2P_REG(0x10)
+#define E2P_PWR_INT_VALUE2	E2P_REG(0x12)
+#define E2P_PWR_INT_VALUE3	E2P_REG(0x14)
+#define E2P_PWR_INT_VALUE4	E2P_REG(0x16)
+
+/* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30)
+ * also only 11 channels. */
+#define E2P_ALLOWED_CHANNEL	E2P_REG(0x18)
+
+#define E2P_PHY_REG		E2P_REG(0x1a)
+#define E2P_DEVICE_VER		E2P_REG(0x20)
+#define E2P_36M_CAL_VALUE1	E2P_REG(0x28)
+#define E2P_36M_CAL_VALUE2      E2P_REG(0x2a)
+#define E2P_36M_CAL_VALUE3      E2P_REG(0x2c)
+#define E2P_36M_CAL_VALUE4	E2P_REG(0x2e)
+#define E2P_11A_INT_VALUE1	E2P_REG(0x30)
+#define E2P_11A_INT_VALUE2	E2P_REG(0x32)
+#define E2P_11A_INT_VALUE3	E2P_REG(0x34)
+#define E2P_11A_INT_VALUE4	E2P_REG(0x36)
+#define E2P_48M_CAL_VALUE1	E2P_REG(0x38)
+#define E2P_48M_CAL_VALUE2	E2P_REG(0x3a)
+#define E2P_48M_CAL_VALUE3	E2P_REG(0x3c)
+#define E2P_48M_CAL_VALUE4	E2P_REG(0x3e)
+#define E2P_48M_INT_VALUE1	E2P_REG(0x40)
+#define E2P_48M_INT_VALUE2	E2P_REG(0x42)
+#define E2P_48M_INT_VALUE3	E2P_REG(0x44)
+#define E2P_48M_INT_VALUE4	E2P_REG(0x46)
+#define E2P_54M_CAL_VALUE1	E2P_REG(0x48)	/* ??? */
+#define E2P_54M_CAL_VALUE2	E2P_REG(0x4a)
+#define E2P_54M_CAL_VALUE3	E2P_REG(0x4c)
+#define E2P_54M_CAL_VALUE4	E2P_REG(0x4e)
+#define E2P_54M_INT_VALUE1	E2P_REG(0x50)
+#define E2P_54M_INT_VALUE2	E2P_REG(0x52)
+#define E2P_54M_INT_VALUE3	E2P_REG(0x54)
+#define E2P_54M_INT_VALUE4	E2P_REG(0x56)
+
+/* All 16 bit values */
+#define FW_FIRMWARE_VER         FW_REG(0)
+/* non-zero if USB high speed connection */
+#define FW_USB_SPEED            FW_REG(1)
+#define FW_FIX_TX_RATE          FW_REG(2)
+/* Seems to be able to control LEDs over the firmware */
+#define FW_LINK_STATUS          FW_REG(3)
+#define FW_SOFT_RESET           FW_REG(4)
+#define FW_FLASH_CHK            FW_REG(5)
+
+enum {
+	CR_BASE_OFFSET			= 0x9000,
+	FW_START_OFFSET			= 0xee00,
+	FW_BASE_ADDR_OFFSET		= FW_START_OFFSET + 0x1d,
+	EEPROM_START_OFFSET		= 0xf800,
+	EEPROM_SIZE			= 0x800, /* words */
+	LOAD_CODE_SIZE			= 0xe, /* words */
+	LOAD_VECT_SIZE			= 0x10000 - 0xfff7, /* words */
+	EEPROM_REGS_OFFSET		= LOAD_CODE_SIZE + LOAD_VECT_SIZE,
+	E2P_BASE_OFFSET			= EEPROM_START_OFFSET +
+		                          EEPROM_REGS_OFFSET,
+};
+
+#define FW_REG_TABLE_ADDR	USB_ADDR(FW_START_OFFSET + 0x1d)
+
+enum {
+	/* indices for ofdm_cal_values */
+	OFDM_36M_INDEX = 0,
+	OFDM_48M_INDEX = 1,
+	OFDM_54M_INDEX = 2,
+};
+
+struct zd_chip {
+	struct zd_usb usb;
+	struct zd_rf rf;
+	struct mutex mutex;
+	u8 e2p_mac[ETH_ALEN];
+	/* EepSetPoint in the vendor driver */
+	u8 pwr_cal_values[E2P_CHANNEL_COUNT];
+	/* integration values in the vendor driver */
+	u8 pwr_int_values[E2P_CHANNEL_COUNT];
+	/* SetPointOFDM in the vendor driver */
+	u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT];
+	u8 pa_type:4, patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1,
+	   is_zd1211b:1;
+};
+
+static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb)
+{
+	return container_of(usb, struct zd_chip, usb);
+}
+
+static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf)
+{
+	return container_of(rf, struct zd_chip, rf);
+}
+
+#define zd_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void zd_chip_init(struct zd_chip *chip,
+	         struct net_device *netdev,
+	         struct usb_interface *intf);
+void zd_chip_clear(struct zd_chip *chip);
+int zd_chip_init_hw(struct zd_chip *chip, u8 device_type);
+int zd_chip_reset(struct zd_chip *chip);
+
+static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values,
+	                              const zd_addr_t *addresses,
+				      unsigned int count)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_usb_ioread16v(&chip->usb, values, addresses, count);
+}
+
+static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value,
+	                             const zd_addr_t addr)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_usb_ioread16(&chip->usb, value, addr);
+}
+
+int zd_ioread32v_locked(struct zd_chip *chip, u32 *values,
+	                const zd_addr_t *addresses, unsigned int count);
+
+static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value,
+	                             const zd_addr_t addr)
+{
+	return zd_ioread32v_locked(chip, value, (const zd_addr_t *)&addr, 1);
+}
+
+static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value,
+	                              zd_addr_t addr)
+{
+	struct zd_ioreq16 ioreq;
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	ioreq.addr = addr;
+	ioreq.value = value;
+
+	return zd_usb_iowrite16v(&chip->usb, &ioreq, 1);
+}
+
+int zd_iowrite16a_locked(struct zd_chip *chip,
+                         const struct zd_ioreq16 *ioreqs, unsigned int count);
+
+int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+			  unsigned int count);
+
+static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value,
+	                              zd_addr_t addr)
+{
+	struct zd_ioreq32 ioreq;
+
+	ioreq.addr = addr;
+	ioreq.value = value;
+
+	return _zd_iowrite32v_locked(chip, &ioreq, 1);
+}
+
+int zd_iowrite32a_locked(struct zd_chip *chip,
+	                 const struct zd_ioreq32 *ioreqs, unsigned int count);
+
+static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits)
+{
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	return zd_usb_rfwrite(&chip->usb, value, bits);
+}
+
+int zd_rfwritev_locked(struct zd_chip *chip,
+	               const u32* values, unsigned int count, u8 bits);
+
+/* Locking functions for reading and writing registers.
+ * The different parameters are intentional.
+ */
+int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value);
+int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value);
+int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value);
+int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value);
+int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses,
+	          u32 *values, unsigned int count);
+int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+	           unsigned int count);
+
+int zd_chip_set_channel(struct zd_chip *chip, u8 channel);
+static inline u8 _zd_chip_get_channel(struct zd_chip *chip)
+{
+	return chip->rf.channel;
+}
+u8  zd_chip_get_channel(struct zd_chip *chip);
+int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain);
+void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr);
+int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr);
+int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr);
+int zd_chip_switch_radio_on(struct zd_chip *chip);
+int zd_chip_switch_radio_off(struct zd_chip *chip);
+int zd_chip_enable_int(struct zd_chip *chip);
+void zd_chip_disable_int(struct zd_chip *chip);
+int zd_chip_enable_rx(struct zd_chip *chip);
+void zd_chip_disable_rx(struct zd_chip *chip);
+int zd_chip_enable_hwint(struct zd_chip *chip);
+int zd_chip_disable_hwint(struct zd_chip *chip);
+
+static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type)
+{
+	return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type);
+}
+
+static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type)
+{
+	return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type);
+}
+
+static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates)
+{
+	return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates);
+}
+
+int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates);
+
+static inline int zd_chip_set_rx_filter(struct zd_chip *chip, u32 filter)
+{
+	return zd_iowrite32(chip, CR_RX_FILTER, filter);
+}
+
+int zd_chip_lock_phy_regs(struct zd_chip *chip);
+int zd_chip_unlock_phy_regs(struct zd_chip *chip);
+
+enum led_status {
+	LED_OFF	   = 0,
+	LED_ON     = 1,
+	LED_FLIP   = 2,
+	LED_STATUS = 3,
+};
+
+int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status);
+int zd_chip_led_flip(struct zd_chip *chip, int led,
+	             const unsigned int *phases_msecs, unsigned int count);
+
+int zd_set_beacon_interval(struct zd_chip *chip, u32 interval);
+
+static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval)
+{
+	return zd_ioread32(chip, CR_BCN_INTERVAL, interval);
+}
+
+struct rx_status;
+
+u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size,
+	               const struct rx_status *status);
+u8 zd_rx_strength_percent(u8 rssi);
+
+u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status);
+
+#endif /* _ZD_CHIP_H */
diff --git a/drivers/net/wireless/zd1211rw/zd_def.h b/drivers/net/wireless/zd1211rw/zd_def.h
new file mode 100644
index 0000000..4659068
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_def.h
@@ -0,0 +1,48 @@
+/* zd_def.h
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_DEF_H
+#define _ZD_DEF_H
+
+#include <linux/kernel.h>
+#include <linux/stringify.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#define dev_printk_f(level, dev, fmt, args...) \
+	dev_printk(level, dev, "%s() " fmt, __func__, ##args)
+
+#ifdef DEBUG
+#  define dev_dbg_f(dev, fmt, args...) \
+	  dev_printk_f(KERN_DEBUG, dev, fmt, ## args)
+#else
+#  define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0)
+#endif /* DEBUG */
+
+#ifdef DEBUG
+#  define ZD_ASSERT(x) \
+do { \
+	if (!(x)) { \
+		pr_debug("%s:%d ASSERT %s VIOLATED!\n", \
+			__FILE__, __LINE__, __stringify(x)); \
+	} \
+} while (0)
+#else
+#  define ZD_ASSERT(x) do { } while (0)
+#endif
+
+#endif /* _ZD_DEF_H */
diff --git a/drivers/net/wireless/zd1211rw/zd_ieee80211.c b/drivers/net/wireless/zd1211rw/zd_ieee80211.c
new file mode 100644
index 0000000..66905f7
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_ieee80211.c
@@ -0,0 +1,191 @@
+/* zd_ieee80211.c
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * A lot of this code is generic and should be moved into the upper layers
+ * at some point.
+ */
+
+#include <linux/errno.h>
+#include <linux/wireless.h>
+#include <linux/kernel.h>
+#include <net/ieee80211.h>
+
+#include "zd_def.h"
+#include "zd_ieee80211.h"
+#include "zd_mac.h"
+
+static const struct channel_range channel_ranges[] = {
+	[0]			 = { 0,  0},
+	[ZD_REGDOMAIN_FCC]	 = { 1, 12},
+	[ZD_REGDOMAIN_IC]	 = { 1, 12},
+	[ZD_REGDOMAIN_ETSI]	 = { 1, 14},
+	[ZD_REGDOMAIN_JAPAN]	 = { 1, 14},
+	[ZD_REGDOMAIN_SPAIN]	 = { 1, 14},
+	[ZD_REGDOMAIN_FRANCE]	 = { 1, 14},
+	[ZD_REGDOMAIN_JAPAN_ADD] = {14, 15},
+};
+
+const struct channel_range *zd_channel_range(u8 regdomain)
+{
+	if (regdomain >= ARRAY_SIZE(channel_ranges))
+		regdomain = 0;
+	return &channel_ranges[regdomain];
+}
+
+int zd_regdomain_supports_channel(u8 regdomain, u8 channel)
+{
+	const struct channel_range *range = zd_channel_range(regdomain);
+	return range->start <= channel && channel < range->end;
+}
+
+int zd_regdomain_supported(u8 regdomain)
+{
+	const struct channel_range *range = zd_channel_range(regdomain);
+	return range->start != 0;
+}
+
+/* Stores channel frequencies in MHz. */
+static const u16 channel_frequencies[] = {
+	2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447,
+	2452, 2457, 2462, 2467, 2472, 2484,
+};
+
+#define NUM_CHANNELS ARRAY_SIZE(channel_frequencies)
+
+static int compute_freq(struct iw_freq *freq, u32 mhz, u32 hz)
+{
+	u32 factor;
+
+	freq->e = 0;
+	if (mhz >= 1000000000U) {
+		pr_debug("zd1211 mhz %u to large\n", mhz);
+		freq->m = 0;
+		return -EINVAL;
+	}
+
+	factor = 1000;
+	while (mhz >= factor) {
+
+		freq->e += 1;
+		factor *= 10;
+	}
+
+	factor /= 1000U;
+	freq->m = mhz * (1000000U/factor) + hz/factor;
+
+	return 0;
+}
+
+int zd_channel_to_freq(struct iw_freq *freq, u8 channel)
+{
+	if (channel > NUM_CHANNELS) {
+		freq->m = 0;
+		freq->e = 0;
+		return -EINVAL;
+	}
+	if (!channel) {
+		freq->m = 0;
+		freq->e = 0;
+		return -EINVAL;
+	}
+	return compute_freq(freq, channel_frequencies[channel-1], 0);
+}
+
+static int freq_to_mhz(const struct iw_freq *freq)
+{
+	u32 factor;
+	int e;
+
+	/* Such high frequencies are not supported. */
+	if (freq->e > 6)
+		return -EINVAL;
+
+	factor = 1;
+	for (e = freq->e; e > 0; --e) {
+		factor *= 10;
+	}
+	factor = 1000000U / factor;
+
+	if (freq->m % factor) {
+		return -EINVAL;
+	}
+
+	return freq->m / factor;
+}
+
+int zd_find_channel(u8 *channel, const struct iw_freq *freq)
+{
+	int i, r;
+	u32 mhz;
+
+	if (!(freq->flags & IW_FREQ_FIXED))
+		return 0;
+
+	if (freq->m < 1000) {
+		if (freq->m  > NUM_CHANNELS || freq->m == 0)
+			return -EINVAL;
+		*channel = freq->m;
+		return 1;
+	}
+
+	r = freq_to_mhz(freq);
+	if (r < 0)
+		return r;
+	mhz = r;
+
+	for (i = 0; i < NUM_CHANNELS; i++) {
+		if (mhz == channel_frequencies[i]) {
+			*channel = i+1;
+			return 1;
+		}
+	}
+
+	return -EINVAL;
+}
+
+int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain)
+{
+	struct ieee80211_geo geo;
+	const struct channel_range *range;
+	int i;
+	u8 channel;
+
+	dev_dbg(zd_mac_dev(zd_netdev_mac(ieee->dev)),
+		"regdomain %#04x\n", regdomain);
+
+	range = zd_channel_range(regdomain);
+	if (range->start == 0) {
+		dev_err(zd_mac_dev(zd_netdev_mac(ieee->dev)),
+			"zd1211 regdomain %#04x not supported\n",
+			regdomain);
+		return -EINVAL;
+	}
+
+	memset(&geo, 0, sizeof(geo));
+
+	for (i = 0, channel = range->start; channel < range->end; channel++) {
+		struct ieee80211_channel *chan = &geo.bg[i++];
+		chan->freq = channel_frequencies[channel - 1];
+		chan->channel = channel;
+	}
+
+	geo.bg_channels = i;
+	memcpy(geo.name, "XX ", 4);
+	ieee80211_set_geo(ieee, &geo);
+	return 0;
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_ieee80211.h b/drivers/net/wireless/zd1211rw/zd_ieee80211.h
new file mode 100644
index 0000000..3632989
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_ieee80211.h
@@ -0,0 +1,85 @@
+#ifndef _ZD_IEEE80211_H
+#define _ZD_IEEE80211_H
+
+#include <net/ieee80211.h>
+#include "zd_types.h"
+
+/* Additional definitions from the standards.
+ */
+
+#define ZD_REGDOMAIN_FCC	0x10
+#define ZD_REGDOMAIN_IC		0x20
+#define ZD_REGDOMAIN_ETSI	0x30
+#define ZD_REGDOMAIN_SPAIN	0x31
+#define ZD_REGDOMAIN_FRANCE	0x32
+#define ZD_REGDOMAIN_JAPAN_ADD	0x40
+#define ZD_REGDOMAIN_JAPAN	0x41
+
+enum {
+	MIN_CHANNEL24 = 1,
+	MAX_CHANNEL24 = 14,
+};
+
+struct channel_range {
+	u8 start;
+	u8 end; /* exclusive (channel must be less than end) */
+};
+
+struct iw_freq;
+
+int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain);
+
+const struct channel_range *zd_channel_range(u8 regdomain);
+int zd_regdomain_supports_channel(u8 regdomain, u8 channel);
+int zd_regdomain_supported(u8 regdomain);
+
+/* for 2.4 GHz band */
+int zd_channel_to_freq(struct iw_freq *freq, u8 channel);
+int zd_find_channel(u8 *channel, const struct iw_freq *freq);
+
+#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80
+
+struct ofdm_plcp_header {
+	u8 prefix[3];
+	__le16 service;
+} __attribute__((packed));
+
+static inline u8 zd_ofdm_plcp_header_rate(
+	const struct ofdm_plcp_header *header)
+{
+	return header->prefix[0] & 0xf;
+}
+
+#define ZD_OFDM_RATE_6M		0xb
+#define ZD_OFDM_RATE_9M		0xf
+#define ZD_OFDM_RATE_12M	0xa
+#define ZD_OFDM_RATE_18M	0xe
+#define ZD_OFDM_RATE_24M	0x9
+#define ZD_OFDM_RATE_36M	0xd
+#define ZD_OFDM_RATE_48M	0x8
+#define ZD_OFDM_RATE_54M	0xc
+
+struct cck_plcp_header {
+	u8 signal;
+	u8 service;
+	__le16 length;
+	__le16 crc16;
+} __attribute__((packed));
+
+static inline u8 zd_cck_plcp_header_rate(const struct cck_plcp_header *header)
+{
+	return header->signal;
+}
+
+#define ZD_CCK_SIGNAL_1M	0x0a
+#define ZD_CCK_SIGNAL_2M	0x14
+#define ZD_CCK_SIGNAL_5M5	0x37
+#define ZD_CCK_SIGNAL_11M	0x6e
+
+enum ieee80211_std {
+	IEEE80211B = 0x01,
+	IEEE80211A = 0x02,
+	IEEE80211G = 0x04,
+};
+
+#endif /* _ZD_IEEE80211_H */
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
new file mode 100644
index 0000000..3bdc54d
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -0,0 +1,1057 @@
+/* zd_mac.c
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/usb.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "zd_def.h"
+#include "zd_chip.h"
+#include "zd_mac.h"
+#include "zd_ieee80211.h"
+#include "zd_netdev.h"
+#include "zd_rf.h"
+#include "zd_util.h"
+
+static void ieee_init(struct ieee80211_device *ieee);
+static void softmac_init(struct ieee80211softmac_device *sm);
+
+int zd_mac_init(struct zd_mac *mac,
+	        struct net_device *netdev,
+	        struct usb_interface *intf)
+{
+	struct ieee80211_device *ieee = zd_netdev_ieee80211(netdev);
+
+	memset(mac, 0, sizeof(*mac));
+	spin_lock_init(&mac->lock);
+	mac->netdev = netdev;
+
+	ieee_init(ieee);
+	softmac_init(ieee80211_priv(netdev));
+	zd_chip_init(&mac->chip, netdev, intf);
+	return 0;
+}
+
+static int reset_channel(struct zd_mac *mac)
+{
+	int r;
+	unsigned long flags;
+	const struct channel_range *range;
+
+	spin_lock_irqsave(&mac->lock, flags);
+	range = zd_channel_range(mac->regdomain);
+	if (!range->start) {
+		r = -EINVAL;
+		goto out;
+	}
+	mac->requested_channel = range->start;
+	r = 0;
+out:
+	spin_unlock_irqrestore(&mac->lock, flags);
+	return r;
+}
+
+int zd_mac_init_hw(struct zd_mac *mac, u8 device_type)
+{
+	int r;
+	struct zd_chip *chip = &mac->chip;
+	u8 addr[ETH_ALEN];
+	u8 default_regdomain;
+
+	r = zd_chip_enable_int(chip);
+	if (r)
+		goto out;
+	r = zd_chip_init_hw(chip, device_type);
+	if (r)
+		goto disable_int;
+
+	zd_get_e2p_mac_addr(chip, addr);
+	r = zd_write_mac_addr(chip, addr);
+	if (r)
+		goto disable_int;
+	ZD_ASSERT(!irqs_disabled());
+	spin_lock_irq(&mac->lock);
+	memcpy(mac->netdev->dev_addr, addr, ETH_ALEN);
+	spin_unlock_irq(&mac->lock);
+
+	r = zd_read_regdomain(chip, &default_regdomain);
+	if (r)
+		goto disable_int;
+	if (!zd_regdomain_supported(default_regdomain)) {
+		dev_dbg_f(zd_mac_dev(mac),
+			  "Regulatory Domain %#04x is not supported.\n",
+		          default_regdomain);
+		r = -EINVAL;
+		goto disable_int;
+	}
+	spin_lock_irq(&mac->lock);
+	mac->regdomain = mac->default_regdomain = default_regdomain;
+	spin_unlock_irq(&mac->lock);
+	r = reset_channel(mac);
+	if (r)
+		goto disable_int;
+
+	r = zd_set_encryption_type(chip, NO_WEP);
+	if (r)
+		goto disable_int;
+
+	r = zd_geo_init(zd_mac_to_ieee80211(mac), mac->regdomain);
+	if (r)
+		goto disable_int;
+
+	r = 0;
+disable_int:
+	zd_chip_disable_int(chip);
+out:
+	return r;
+}
+
+void zd_mac_clear(struct zd_mac *mac)
+{
+	/* Aquire the lock. */
+	spin_lock(&mac->lock);
+	spin_unlock(&mac->lock);
+	zd_chip_clear(&mac->chip);
+	memset(mac, 0, sizeof(*mac));
+}
+
+static int reset_mode(struct zd_mac *mac)
+{
+	struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
+	struct zd_ioreq32 ioreqs[3] = {
+		{ CR_RX_FILTER, RX_FILTER_BEACON|RX_FILTER_PROBE_RESPONSE|
+			        RX_FILTER_AUTH|RX_FILTER_ASSOC_RESPONSE },
+		{ CR_SNIFFER_ON, 0U },
+		{ CR_ENCRYPTION_TYPE, NO_WEP },
+	};
+
+	if (ieee->iw_mode == IW_MODE_MONITOR) {
+		ioreqs[0].value = 0xffffffff;
+		ioreqs[1].value = 0x1;
+		ioreqs[2].value = ENC_SNIFFER;
+	}
+
+	return zd_iowrite32a(&mac->chip, ioreqs, 3);
+}
+
+int zd_mac_open(struct net_device *netdev)
+{
+	struct zd_mac *mac = zd_netdev_mac(netdev);
+	struct zd_chip *chip = &mac->chip;
+	int r;
+
+	r = zd_chip_enable_int(chip);
+	if (r < 0)
+		goto out;
+
+	r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G);
+	if (r < 0)
+		goto disable_int;
+	r = reset_mode(mac);
+	if (r)
+		goto disable_int;
+	r = zd_chip_switch_radio_on(chip);
+	if (r < 0)
+		goto disable_int;
+	r = zd_chip_set_channel(chip, mac->requested_channel);
+	if (r < 0)
+		goto disable_radio;
+	r = zd_chip_enable_rx(chip);
+	if (r < 0)
+		goto disable_radio;
+	r = zd_chip_enable_hwint(chip);
+	if (r < 0)
+		goto disable_rx;
+
+	ieee80211softmac_start(netdev);
+	return 0;
+disable_rx:
+	zd_chip_disable_rx(chip);
+disable_radio:
+	zd_chip_switch_radio_off(chip);
+disable_int:
+	zd_chip_disable_int(chip);
+out:
+	return r;
+}
+
+int zd_mac_stop(struct net_device *netdev)
+{
+	struct zd_mac *mac = zd_netdev_mac(netdev);
+	struct zd_chip *chip = &mac->chip;
+
+	netif_stop_queue(netdev);
+
+	/*
+	 * The order here deliberately is a little different from the open()
+	 * method, since we need to make sure there is no opportunity for RX
+	 * frames to be processed by softmac after we have stopped it.
+	 */
+
+	zd_chip_disable_rx(chip);
+	ieee80211softmac_stop(netdev);
+
+	zd_chip_disable_hwint(chip);
+	zd_chip_switch_radio_off(chip);
+	zd_chip_disable_int(chip);
+
+	return 0;
+}
+
+int zd_mac_set_mac_address(struct net_device *netdev, void *p)
+{
+	int r;
+	unsigned long flags;
+	struct sockaddr *addr = p;
+	struct zd_mac *mac = zd_netdev_mac(netdev);
+	struct zd_chip *chip = &mac->chip;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	dev_dbg_f(zd_mac_dev(mac),
+		  "Setting MAC to " MAC_FMT "\n", MAC_ARG(addr->sa_data));
+
+	r = zd_write_mac_addr(chip, addr->sa_data);
+	if (r)
+		return r;
+
+	spin_lock_irqsave(&mac->lock, flags);
+	memcpy(netdev->dev_addr, addr->sa_data, ETH_ALEN);
+	spin_unlock_irqrestore(&mac->lock, flags);
+
+	return 0;
+}
+
+int zd_mac_set_regdomain(struct zd_mac *mac, u8 regdomain)
+{
+	int r;
+	u8 channel;
+
+	ZD_ASSERT(!irqs_disabled());
+	spin_lock_irq(&mac->lock);
+	if (regdomain == 0) {
+		regdomain = mac->default_regdomain;
+	}
+	if (!zd_regdomain_supported(regdomain)) {
+		spin_unlock_irq(&mac->lock);
+		return -EINVAL;
+	}
+	mac->regdomain = regdomain;
+	channel = mac->requested_channel;
+	spin_unlock_irq(&mac->lock);
+
+	r = zd_geo_init(zd_mac_to_ieee80211(mac), regdomain);
+	if (r)
+		return r;
+	if (!zd_regdomain_supports_channel(regdomain, channel)) {
+		r = reset_channel(mac);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+u8 zd_mac_get_regdomain(struct zd_mac *mac)
+{
+	unsigned long flags;
+	u8 regdomain;
+
+	spin_lock_irqsave(&mac->lock, flags);
+	regdomain = mac->regdomain;
+	spin_unlock_irqrestore(&mac->lock, flags);
+	return regdomain;
+}
+
+static void set_channel(struct net_device *netdev, u8 channel)
+{
+	struct zd_mac *mac = zd_netdev_mac(netdev);
+
+	dev_dbg_f(zd_mac_dev(mac), "channel %d\n", channel);
+
+	zd_chip_set_channel(&mac->chip, channel);
+}
+
+/* TODO: Should not work in Managed mode. */
+int zd_mac_request_channel(struct zd_mac *mac, u8 channel)
+{
+	unsigned long lock_flags;
+	struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
+
+	if (ieee->iw_mode == IW_MODE_INFRA)
+		return -EPERM;
+
+	spin_lock_irqsave(&mac->lock, lock_flags);
+	if (!zd_regdomain_supports_channel(mac->regdomain, channel)) {
+		spin_unlock_irqrestore(&mac->lock, lock_flags);
+		return -EINVAL;
+	}
+	mac->requested_channel = channel;
+	spin_unlock_irqrestore(&mac->lock, lock_flags);
+	if (netif_running(mac->netdev))
+		return zd_chip_set_channel(&mac->chip, channel);
+	else
+		return 0;
+}
+
+int zd_mac_get_channel(struct zd_mac *mac, u8 *channel, u8 *flags)
+{
+	struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
+
+	*channel = zd_chip_get_channel(&mac->chip);
+	if (ieee->iw_mode != IW_MODE_INFRA) {
+		spin_lock_irq(&mac->lock);
+		*flags = *channel == mac->requested_channel ?
+			MAC_FIXED_CHANNEL : 0;
+		spin_unlock(&mac->lock);
+	} else {
+		*flags = 0;
+	}
+	dev_dbg_f(zd_mac_dev(mac), "channel %u flags %u\n", *channel, *flags);
+	return 0;
+}
+
+/* If wrong rate is given, we are falling back to the slowest rate: 1MBit/s */
+static u8 cs_typed_rate(u8 cs_rate)
+{
+	static const u8 typed_rates[16] = {
+		[ZD_CS_CCK_RATE_1M]	= ZD_CS_CCK|ZD_CS_CCK_RATE_1M,
+		[ZD_CS_CCK_RATE_2M]	= ZD_CS_CCK|ZD_CS_CCK_RATE_2M,
+		[ZD_CS_CCK_RATE_5_5M]	= ZD_CS_CCK|ZD_CS_CCK_RATE_5_5M,
+		[ZD_CS_CCK_RATE_11M]	= ZD_CS_CCK|ZD_CS_CCK_RATE_11M,
+		[ZD_OFDM_RATE_6M]	= ZD_CS_OFDM|ZD_OFDM_RATE_6M,
+		[ZD_OFDM_RATE_9M]	= ZD_CS_OFDM|ZD_OFDM_RATE_9M,
+		[ZD_OFDM_RATE_12M]	= ZD_CS_OFDM|ZD_OFDM_RATE_12M,
+		[ZD_OFDM_RATE_18M]	= ZD_CS_OFDM|ZD_OFDM_RATE_18M,
+		[ZD_OFDM_RATE_24M]	= ZD_CS_OFDM|ZD_OFDM_RATE_24M,
+		[ZD_OFDM_RATE_36M]	= ZD_CS_OFDM|ZD_OFDM_RATE_36M,
+		[ZD_OFDM_RATE_48M]	= ZD_CS_OFDM|ZD_OFDM_RATE_48M,
+		[ZD_OFDM_RATE_54M]	= ZD_CS_OFDM|ZD_OFDM_RATE_54M,
+	};
+
+	ZD_ASSERT(ZD_CS_RATE_MASK == 0x0f);
+	return typed_rates[cs_rate & ZD_CS_RATE_MASK];
+}
+
+/* Fallback to lowest rate, if rate is unknown. */
+static u8 rate_to_cs_rate(u8 rate)
+{
+	switch (rate) {
+	case IEEE80211_CCK_RATE_2MB:
+		return ZD_CS_CCK_RATE_2M;
+	case IEEE80211_CCK_RATE_5MB:
+		return ZD_CS_CCK_RATE_5_5M;
+	case IEEE80211_CCK_RATE_11MB:
+		return ZD_CS_CCK_RATE_11M;
+	case IEEE80211_OFDM_RATE_6MB:
+		return ZD_OFDM_RATE_6M;
+	case IEEE80211_OFDM_RATE_9MB:
+		return ZD_OFDM_RATE_9M;
+	case IEEE80211_OFDM_RATE_12MB:
+		return ZD_OFDM_RATE_12M;
+	case IEEE80211_OFDM_RATE_18MB:
+		return ZD_OFDM_RATE_18M;
+	case IEEE80211_OFDM_RATE_24MB:
+		return ZD_OFDM_RATE_24M;
+	case IEEE80211_OFDM_RATE_36MB:
+		return ZD_OFDM_RATE_36M;
+	case IEEE80211_OFDM_RATE_48MB:
+		return ZD_OFDM_RATE_48M;
+	case IEEE80211_OFDM_RATE_54MB:
+		return ZD_OFDM_RATE_54M;
+	}
+	return ZD_CS_CCK_RATE_1M;
+}
+
+int zd_mac_set_mode(struct zd_mac *mac, u32 mode)
+{
+	struct ieee80211_device *ieee;
+
+	switch (mode) {
+	case IW_MODE_AUTO:
+	case IW_MODE_ADHOC:
+	case IW_MODE_INFRA:
+		mac->netdev->type = ARPHRD_ETHER;
+		break;
+	case IW_MODE_MONITOR:
+		mac->netdev->type = ARPHRD_IEEE80211_RADIOTAP;
+		break;
+	default:
+		dev_dbg_f(zd_mac_dev(mac), "wrong mode %u\n", mode);
+		return -EINVAL;
+	}
+
+	ieee = zd_mac_to_ieee80211(mac);
+	ZD_ASSERT(!irqs_disabled());
+	spin_lock_irq(&ieee->lock);
+	ieee->iw_mode = mode;
+	spin_unlock_irq(&ieee->lock);
+
+	if (netif_running(mac->netdev))
+		return reset_mode(mac);
+
+	return 0;
+}
+
+int zd_mac_get_mode(struct zd_mac *mac, u32 *mode)
+{
+	unsigned long flags;
+	struct ieee80211_device *ieee;
+
+	ieee = zd_mac_to_ieee80211(mac);
+	spin_lock_irqsave(&ieee->lock, flags);
+	*mode = ieee->iw_mode;
+	spin_unlock_irqrestore(&ieee->lock, flags);
+	return 0;
+}
+
+int zd_mac_get_range(struct zd_mac *mac, struct iw_range *range)
+{
+	int i;
+	const struct channel_range *channel_range;
+	u8 regdomain;
+
+	memset(range, 0, sizeof(*range));
+
+	/* FIXME: Not so important and depends on the mode. For 802.11g
+	 * usually this value is used. It seems to be that Bit/s number is
+	 * given here.
+	 */
+	range->throughput = 27 * 1000 * 1000;
+
+	range->max_qual.qual = 100;
+	range->max_qual.level = 100;
+
+	/* FIXME: Needs still to be tuned. */
+	range->avg_qual.qual = 71;
+	range->avg_qual.level = 80;
+
+	/* FIXME: depends on standard? */
+	range->min_rts = 256;
+	range->max_rts = 2346;
+
+	range->min_frag = MIN_FRAG_THRESHOLD;
+	range->max_frag = MAX_FRAG_THRESHOLD;
+
+	range->max_encoding_tokens = WEP_KEYS;
+	range->num_encoding_sizes = 2;
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = WEP_KEY_LEN;
+
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = 20;
+
+	ZD_ASSERT(!irqs_disabled());
+	spin_lock_irq(&mac->lock);
+	regdomain = mac->regdomain;
+	spin_unlock_irq(&mac->lock);
+	channel_range = zd_channel_range(regdomain);
+
+	range->num_channels = channel_range->end - channel_range->start;
+	range->old_num_channels = range->num_channels;
+	range->num_frequency = range->num_channels;
+	range->old_num_frequency = range->num_frequency;
+
+	for (i = 0; i < range->num_frequency; i++) {
+		struct iw_freq *freq = &range->freq[i];
+		freq->i = channel_range->start + i;
+		zd_channel_to_freq(freq, freq->i);
+	}
+
+	return 0;
+}
+
+static int zd_calc_tx_length_us(u8 *service, u8 cs_rate, u16 tx_length)
+{
+	static const u8 rate_divisor[] = {
+		[ZD_CS_CCK_RATE_1M]	=  1,
+		[ZD_CS_CCK_RATE_2M]	=  2,
+		[ZD_CS_CCK_RATE_5_5M]	= 11, /* bits must be doubled */
+		[ZD_CS_CCK_RATE_11M]	= 11,
+		[ZD_OFDM_RATE_6M]	=  6,
+		[ZD_OFDM_RATE_9M]	=  9,
+		[ZD_OFDM_RATE_12M]	= 12,
+		[ZD_OFDM_RATE_18M]	= 18,
+		[ZD_OFDM_RATE_24M]	= 24,
+		[ZD_OFDM_RATE_36M]	= 36,
+		[ZD_OFDM_RATE_48M]	= 48,
+		[ZD_OFDM_RATE_54M]	= 54,
+	};
+
+	u32 bits = (u32)tx_length * 8;
+	u32 divisor;
+
+	divisor = rate_divisor[cs_rate];
+	if (divisor == 0)
+		return -EINVAL;
+
+	switch (cs_rate) {
+	case ZD_CS_CCK_RATE_5_5M:
+		bits = (2*bits) + 10; /* round up to the next integer */
+		break;
+	case ZD_CS_CCK_RATE_11M:
+		if (service) {
+			u32 t = bits % 11;
+			*service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION;
+			if (0 < t && t <= 3) {
+				*service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION;
+			}
+		}
+		bits += 10; /* round up to the next integer */
+		break;
+	}
+
+	return bits/divisor;
+}
+
+enum {
+	R2M_SHORT_PREAMBLE = 0x01,
+	R2M_11A		   = 0x02,
+};
+
+static u8 cs_rate_to_modulation(u8 cs_rate, int flags)
+{
+	u8 modulation;
+
+	modulation = cs_typed_rate(cs_rate);
+	if (flags & R2M_SHORT_PREAMBLE) {
+		switch (ZD_CS_RATE(modulation)) {
+		case ZD_CS_CCK_RATE_2M:
+		case ZD_CS_CCK_RATE_5_5M:
+		case ZD_CS_CCK_RATE_11M:
+			modulation |= ZD_CS_CCK_PREA_SHORT;
+			return modulation;
+		}
+	}
+	if (flags & R2M_11A) {
+		if (ZD_CS_TYPE(modulation) == ZD_CS_OFDM)
+			modulation |= ZD_CS_OFDM_MODE_11A;
+	}
+	return modulation;
+}
+
+static void cs_set_modulation(struct zd_mac *mac, struct zd_ctrlset *cs,
+	                      struct ieee80211_hdr_4addr *hdr)
+{
+	struct ieee80211softmac_device *softmac = ieee80211_priv(mac->netdev);
+	u16 ftype = WLAN_FC_GET_TYPE(le16_to_cpu(hdr->frame_ctl));
+	u8 rate, cs_rate;
+	int is_mgt = (ftype == IEEE80211_FTYPE_MGMT) != 0;
+
+	/* FIXME: 802.11a? short preamble? */
+	rate = ieee80211softmac_suggest_txrate(softmac,
+		is_multicast_ether_addr(hdr->addr1), is_mgt);
+
+	cs_rate = rate_to_cs_rate(rate);
+	cs->modulation = cs_rate_to_modulation(cs_rate, 0);
+}
+
+static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
+	                   struct ieee80211_hdr_4addr *header)
+{
+	unsigned int tx_length = le16_to_cpu(cs->tx_length);
+	u16 fctl = le16_to_cpu(header->frame_ctl);
+	u16 ftype = WLAN_FC_GET_TYPE(fctl);
+	u16 stype = WLAN_FC_GET_STYPE(fctl);
+
+	/*
+	 * CONTROL:
+	 * - start at 0x00
+	 * - if fragment 0, enable bit 0
+	 * - if backoff needed, enable bit 0
+	 * - if burst (backoff not needed) disable bit 0
+	 * - if multicast, enable bit 1
+	 * - if PS-POLL frame, enable bit 2
+	 * - if in INDEPENDENT_BSS mode and zd1205_DestPowerSave, then enable
+	 *   bit 4 (FIXME: wtf)
+	 * - if frag_len > RTS threshold, set bit 5 as long if it isnt
+	 *   multicast or mgt
+	 * - if bit 5 is set, and we are in OFDM mode, unset bit 5 and set bit
+	 *   7
+	 */
+
+	cs->control = 0;
+
+	/* First fragment */
+	if (WLAN_GET_SEQ_FRAG(le16_to_cpu(header->seq_ctl)) == 0)
+		cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
+
+	/* Multicast */
+	if (is_multicast_ether_addr(header->addr1))
+		cs->control |= ZD_CS_MULTICAST;
+
+	/* PS-POLL */
+	if (stype == IEEE80211_STYPE_PSPOLL)
+		cs->control |= ZD_CS_PS_POLL_FRAME;
+
+	if (!is_multicast_ether_addr(header->addr1) &&
+	    ftype != IEEE80211_FTYPE_MGMT &&
+	    tx_length > zd_netdev_ieee80211(mac->netdev)->rts)
+	{
+		/* FIXME: check the logic */
+		if (ZD_CS_TYPE(cs->modulation) == ZD_CS_OFDM) {
+			/* 802.11g */
+			cs->control |= ZD_CS_SELF_CTS;
+		} else { /* 802.11b */
+			cs->control |= ZD_CS_RTS;
+		}
+	}
+
+	/* FIXME: Management frame? */
+}
+
+static int fill_ctrlset(struct zd_mac *mac,
+	                struct ieee80211_txb *txb,
+			int frag_num)
+{
+	int r;
+	struct sk_buff *skb = txb->fragments[frag_num];
+	struct ieee80211_hdr_4addr *hdr =
+		(struct ieee80211_hdr_4addr *) skb->data;
+	unsigned int frag_len = skb->len + IEEE80211_FCS_LEN;
+	unsigned int next_frag_len;
+	unsigned int packet_length;
+	struct zd_ctrlset *cs = (struct zd_ctrlset *)
+		skb_push(skb, sizeof(struct zd_ctrlset));
+
+	if (frag_num+1  < txb->nr_frags) {
+		next_frag_len = txb->fragments[frag_num+1]->len +
+			        IEEE80211_FCS_LEN;
+	} else {
+		next_frag_len = 0;
+	}
+	ZD_ASSERT(frag_len <= 0xffff);
+	ZD_ASSERT(next_frag_len <= 0xffff);
+
+	cs_set_modulation(mac, cs, hdr);
+
+	cs->tx_length = cpu_to_le16(frag_len);
+
+	cs_set_control(mac, cs, hdr);
+
+	packet_length = frag_len + sizeof(struct zd_ctrlset) + 10;
+	ZD_ASSERT(packet_length <= 0xffff);
+	/* ZD1211B: Computing the length difference this way, gives us
+	 * flexibility to compute the packet length.
+	 */
+	cs->packet_length = cpu_to_le16(mac->chip.is_zd1211b ?
+			packet_length - frag_len : packet_length);
+
+	/*
+	 * CURRENT LENGTH:
+	 * - transmit frame length in microseconds
+	 * - seems to be derived from frame length
+	 * - see Cal_Us_Service() in zdinlinef.h
+	 * - if macp->bTxBurstEnable is enabled, then multiply by 4
+	 *  - bTxBurstEnable is never set in the vendor driver
+	 *
+	 * SERVICE:
+	 * - "for PLCP configuration"
+	 * - always 0 except in some situations at 802.11b 11M
+	 * - see line 53 of zdinlinef.h
+	 */
+	cs->service = 0;
+	r = zd_calc_tx_length_us(&cs->service, ZD_CS_RATE(cs->modulation),
+		                 le16_to_cpu(cs->tx_length));
+	if (r < 0)
+		return r;
+	cs->current_length = cpu_to_le16(r);
+
+	if (next_frag_len == 0) {
+		cs->next_frame_length = 0;
+	} else {
+		r = zd_calc_tx_length_us(NULL, ZD_CS_RATE(cs->modulation),
+			                 next_frag_len);
+		if (r < 0)
+			return r;
+		cs->next_frame_length = cpu_to_le16(r);
+	}
+
+	return 0;
+}
+
+static int zd_mac_tx(struct zd_mac *mac, struct ieee80211_txb *txb, int pri)
+{
+	int i, r;
+
+	for (i = 0; i < txb->nr_frags; i++) {
+		struct sk_buff *skb = txb->fragments[i];
+
+		r = fill_ctrlset(mac, txb, i);
+		if (r)
+			return r;
+		r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len);
+		if (r)
+			return r;
+	}
+
+	/* FIXME: shouldn't this be handled by the upper layers? */
+	mac->netdev->trans_start = jiffies;
+
+	ieee80211_txb_free(txb);
+	return 0;
+}
+
+struct zd_rt_hdr {
+	struct ieee80211_radiotap_header rt_hdr;
+	u8  rt_flags;
+	u16 rt_channel;
+	u16 rt_chbitmask;
+	u16 rt_rate;
+};
+
+static void fill_rt_header(void *buffer, struct zd_mac *mac,
+	                   const struct ieee80211_rx_stats *stats,
+			   const struct rx_status *status)
+{
+	struct zd_rt_hdr *hdr = buffer;
+
+	hdr->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+	hdr->rt_hdr.it_pad = 0;
+	hdr->rt_hdr.it_len = cpu_to_le16(sizeof(struct zd_rt_hdr));
+	hdr->rt_hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
+		                 (1 << IEEE80211_RADIOTAP_CHANNEL) |
+				 (1 << IEEE80211_RADIOTAP_RATE));
+
+	hdr->rt_flags = 0;
+	if (status->decryption_type & (ZD_RX_WEP64|ZD_RX_WEP128|ZD_RX_WEP256))
+		hdr->rt_flags |= IEEE80211_RADIOTAP_F_WEP;
+
+	/* FIXME: 802.11a */
+	hdr->rt_channel = cpu_to_le16(ieee80211chan2mhz(
+		                             _zd_chip_get_channel(&mac->chip)));
+	hdr->rt_chbitmask = cpu_to_le16(IEEE80211_CHAN_2GHZ |
+		((status->frame_status & ZD_RX_FRAME_MODULATION_MASK) ==
+		ZD_RX_OFDM ? IEEE80211_CHAN_OFDM : IEEE80211_CHAN_CCK));
+
+	hdr->rt_rate = stats->rate / 5;
+}
+
+/* Returns 1 if the data packet is for us and 0 otherwise. */
+static int is_data_packet_for_us(struct ieee80211_device *ieee,
+	                         struct ieee80211_hdr_4addr *hdr)
+{
+	struct net_device *netdev = ieee->dev;
+	u16 fc = le16_to_cpu(hdr->frame_ctl);
+
+	ZD_ASSERT(WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA);
+
+	switch (ieee->iw_mode) {
+	case IW_MODE_ADHOC:
+		if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != 0 ||
+		    memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) != 0)
+			return 0;
+		break;
+	case IW_MODE_AUTO:
+	case IW_MODE_INFRA:
+		if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) !=
+		    IEEE80211_FCTL_FROMDS ||
+		    memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) != 0)
+			return 0;
+		break;
+	default:
+		ZD_ASSERT(ieee->iw_mode != IW_MODE_MONITOR);
+		return 0;
+	}
+
+	return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 ||
+	       is_multicast_ether_addr(hdr->addr1) ||
+	       (netdev->flags & IFF_PROMISC);
+}
+
+/* Filters receiving packets. If it returns 1 send it to ieee80211_rx, if 0
+ * return. If an error is detected -EINVAL is returned. ieee80211_rx_mgt() is
+ * called here.
+ *
+ * It has been based on ieee80211_rx_any.
+ */
+static int filter_rx(struct ieee80211_device *ieee,
+	             const u8 *buffer, unsigned int length,
+		     struct ieee80211_rx_stats *stats)
+{
+	struct ieee80211_hdr_4addr *hdr;
+	u16 fc;
+
+	if (ieee->iw_mode == IW_MODE_MONITOR)
+		return 1;
+
+	hdr = (struct ieee80211_hdr_4addr *)buffer;
+	fc = le16_to_cpu(hdr->frame_ctl);
+	if ((fc & IEEE80211_FCTL_VERS) != 0)
+		return -EINVAL;
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case IEEE80211_FTYPE_MGMT:
+		if (length < sizeof(struct ieee80211_hdr_3addr))
+			return -EINVAL;
+		ieee80211_rx_mgt(ieee, hdr, stats);
+		return 0;
+	case IEEE80211_FTYPE_CTL:
+		/* Ignore invalid short buffers */
+		return 0;
+	case IEEE80211_FTYPE_DATA:
+		if (length < sizeof(struct ieee80211_hdr_3addr))
+			return -EINVAL;
+		return is_data_packet_for_us(ieee, hdr);
+	}
+
+	return -EINVAL;
+}
+
+static void update_qual_rssi(struct zd_mac *mac, u8 qual_percent, u8 rssi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->qual_average = (7 * mac->qual_average + qual_percent) / 8;
+	mac->rssi_average = (7 * mac->rssi_average + rssi) / 8;
+	spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+static int fill_rx_stats(struct ieee80211_rx_stats *stats,
+	                 const struct rx_status **pstatus,
+		         struct zd_mac *mac,
+			 const u8 *buffer, unsigned int length)
+{
+	const struct rx_status *status;
+
+	*pstatus = status = zd_tail(buffer, length, sizeof(struct rx_status));
+	if (status->frame_status & ZD_RX_ERROR) {
+		/* FIXME: update? */
+		return -EINVAL;
+	}
+	memset(stats, 0, sizeof(struct ieee80211_rx_stats));
+	stats->len = length - (ZD_PLCP_HEADER_SIZE + IEEE80211_FCS_LEN +
+		               + sizeof(struct rx_status));
+	/* FIXME: 802.11a */
+	stats->freq = IEEE80211_24GHZ_BAND;
+	stats->received_channel = _zd_chip_get_channel(&mac->chip);
+	stats->rssi = zd_rx_strength_percent(status->signal_strength);
+	stats->signal = zd_rx_qual_percent(buffer,
+		                          length - sizeof(struct rx_status),
+		                          status);
+	stats->mask = IEEE80211_STATMASK_RSSI | IEEE80211_STATMASK_SIGNAL;
+	stats->rate = zd_rx_rate(buffer, status);
+	if (stats->rate)
+		stats->mask |= IEEE80211_STATMASK_RATE;
+
+	update_qual_rssi(mac, stats->signal, stats->rssi);
+	return 0;
+}
+
+int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length)
+{
+	int r;
+	struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
+	struct ieee80211_rx_stats stats;
+	const struct rx_status *status;
+	struct sk_buff *skb;
+
+	if (length < ZD_PLCP_HEADER_SIZE + IEEE80211_1ADDR_LEN +
+	             IEEE80211_FCS_LEN + sizeof(struct rx_status))
+		return -EINVAL;
+
+	r = fill_rx_stats(&stats, &status, mac, buffer, length);
+	if (r)
+		return r;
+
+	length -= ZD_PLCP_HEADER_SIZE+IEEE80211_FCS_LEN+
+		  sizeof(struct rx_status);
+	buffer += ZD_PLCP_HEADER_SIZE;
+
+	r = filter_rx(ieee, buffer, length, &stats);
+	if (r <= 0)
+		return r;
+
+	skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length);
+	if (!skb)
+		return -ENOMEM;
+	if (ieee->iw_mode == IW_MODE_MONITOR)
+		fill_rt_header(skb_put(skb, sizeof(struct zd_rt_hdr)), mac,
+			       &stats, status);
+	memcpy(skb_put(skb, length), buffer, length);
+
+	r = ieee80211_rx(ieee, skb, &stats);
+	if (!r) {
+		ZD_ASSERT(in_irq());
+		dev_kfree_skb_irq(skb);
+	}
+	return 0;
+}
+
+static int netdev_tx(struct ieee80211_txb *txb, struct net_device *netdev,
+		     int pri)
+{
+	return zd_mac_tx(zd_netdev_mac(netdev), txb, pri);
+}
+
+static void set_security(struct net_device *netdev,
+			 struct ieee80211_security *sec)
+{
+	struct ieee80211_device *ieee = zd_netdev_ieee80211(netdev);
+	struct ieee80211_security *secinfo = &ieee->sec;
+	int keyidx;
+
+	dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n");
+
+	for (keyidx = 0; keyidx<WEP_KEYS; keyidx++)
+		if (sec->flags & (1<<keyidx)) {
+			secinfo->encode_alg[keyidx] = sec->encode_alg[keyidx];
+			secinfo->key_sizes[keyidx] = sec->key_sizes[keyidx];
+			memcpy(secinfo->keys[keyidx], sec->keys[keyidx],
+			       SCM_KEY_LEN);
+		}
+
+	if (sec->flags & SEC_ACTIVE_KEY) {
+		secinfo->active_key = sec->active_key;
+		dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
+			"   .active_key = %d\n", sec->active_key);
+	}
+	if (sec->flags & SEC_UNICAST_GROUP) {
+		secinfo->unicast_uses_group = sec->unicast_uses_group;
+		dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
+			"   .unicast_uses_group = %d\n",
+			sec->unicast_uses_group);
+	}
+	if (sec->flags & SEC_LEVEL) {
+		secinfo->level = sec->level;
+		dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
+			"   .level = %d\n", sec->level);
+	}
+	if (sec->flags & SEC_ENABLED) {
+		secinfo->enabled = sec->enabled;
+		dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
+			"   .enabled = %d\n", sec->enabled);
+	}
+	if (sec->flags & SEC_ENCRYPT) {
+		secinfo->encrypt = sec->encrypt;
+		dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
+			"   .encrypt = %d\n", sec->encrypt);
+	}
+	if (sec->flags & SEC_AUTH_MODE) {
+		secinfo->auth_mode = sec->auth_mode;
+		dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
+			"   .auth_mode = %d\n", sec->auth_mode);
+	}
+}
+
+static void ieee_init(struct ieee80211_device *ieee)
+{
+	ieee->mode = IEEE_B | IEEE_G;
+	ieee->freq_band = IEEE80211_24GHZ_BAND;
+	ieee->modulation = IEEE80211_OFDM_MODULATION | IEEE80211_CCK_MODULATION;
+	ieee->tx_headroom = sizeof(struct zd_ctrlset);
+	ieee->set_security = set_security;
+	ieee->hard_start_xmit = netdev_tx;
+
+	/* Software encryption/decryption for now */
+	ieee->host_build_iv = 0;
+	ieee->host_encrypt = 1;
+	ieee->host_decrypt = 1;
+
+	/* FIXME: default to managed mode, until ieee80211 and zd1211rw can
+	 * correctly support AUTO */
+	ieee->iw_mode = IW_MODE_INFRA;
+}
+
+static void softmac_init(struct ieee80211softmac_device *sm)
+{
+	sm->set_channel = set_channel;
+}
+
+struct iw_statistics *zd_mac_get_wireless_stats(struct net_device *ndev)
+{
+	struct zd_mac *mac = zd_netdev_mac(ndev);
+	struct iw_statistics *iw_stats = &mac->iw_stats;
+
+	memset(iw_stats, 0, sizeof(struct iw_statistics));
+	/* We are not setting the status, because ieee->state is not updated
+	 * at all and this driver doesn't track authentication state.
+	 */
+	spin_lock_irq(&mac->lock);
+	iw_stats->qual.qual = mac->qual_average;
+	iw_stats->qual.level = mac->rssi_average;
+	iw_stats->qual.updated = IW_QUAL_QUAL_UPDATED|IW_QUAL_LEVEL_UPDATED|
+		                 IW_QUAL_NOISE_INVALID;
+	spin_unlock_irq(&mac->lock);
+	/* TODO: update counter */
+	return iw_stats;
+}
+
+#ifdef DEBUG
+static const char* decryption_types[] = {
+	[ZD_RX_NO_WEP] = "none",
+	[ZD_RX_WEP64] = "WEP64",
+	[ZD_RX_TKIP] = "TKIP",
+	[ZD_RX_AES] = "AES",
+	[ZD_RX_WEP128] = "WEP128",
+	[ZD_RX_WEP256] = "WEP256",
+};
+
+static const char *decryption_type_string(u8 type)
+{
+	const char *s;
+
+	if (type < ARRAY_SIZE(decryption_types)) {
+		s = decryption_types[type];
+	} else {
+		s = NULL;
+	}
+	return s ? s : "unknown";
+}
+
+static int is_ofdm(u8 frame_status)
+{
+	return (frame_status & ZD_RX_OFDM);
+}
+
+void zd_dump_rx_status(const struct rx_status *status)
+{
+	const char* modulation;
+	u8 quality;
+
+	if (is_ofdm(status->frame_status)) {
+		modulation = "ofdm";
+		quality = status->signal_quality_ofdm;
+	} else {
+		modulation = "cck";
+		quality = status->signal_quality_cck;
+	}
+	pr_debug("rx status %s strength %#04x qual %#04x decryption %s\n",
+		modulation, status->signal_strength, quality,
+		decryption_type_string(status->decryption_type));
+	if (status->frame_status & ZD_RX_ERROR) {
+		pr_debug("rx error %s%s%s%s%s%s\n",
+			(status->frame_status & ZD_RX_TIMEOUT_ERROR) ?
+				"timeout " : "",
+			(status->frame_status & ZD_RX_FIFO_OVERRUN_ERROR) ?
+				"fifo " : "",
+			(status->frame_status & ZD_RX_DECRYPTION_ERROR) ?
+				"decryption " : "",
+			(status->frame_status & ZD_RX_CRC32_ERROR) ?
+				"crc32 " : "",
+			(status->frame_status & ZD_RX_NO_ADDR1_MATCH_ERROR) ?
+				"addr1 " : "",
+			(status->frame_status & ZD_RX_CRC16_ERROR) ?
+				"crc16" : "");
+	}
+}
+#endif /* DEBUG */
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
new file mode 100644
index 0000000..71e382c
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -0,0 +1,190 @@
+/* zd_mac.c
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_MAC_H
+#define _ZD_MAC_H
+
+#include <linux/wireless.h>
+#include <linux/kernel.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211softmac.h>
+
+#include "zd_chip.h"
+#include "zd_netdev.h"
+
+struct zd_ctrlset {
+	u8     modulation;
+	__le16 tx_length;
+	u8     control;
+	/* stores only the difference to tx_length on ZD1211B */
+	__le16 packet_length;
+	__le16 current_length;
+	u8     service;
+	__le16  next_frame_length;
+} __attribute__((packed));
+
+#define ZD_CS_RESERVED_SIZE	25
+
+/* zd_crtlset field modulation */
+#define ZD_CS_RATE_MASK		0x0f
+#define ZD_CS_TYPE_MASK		0x10
+#define ZD_CS_RATE(modulation) ((modulation) & ZD_CS_RATE_MASK)
+#define ZD_CS_TYPE(modulation) ((modulation) & ZD_CS_TYPE_MASK)
+
+#define ZD_CS_CCK		0x00
+#define ZD_CS_OFDM		0x10
+
+#define ZD_CS_CCK_RATE_1M	0x00
+#define ZD_CS_CCK_RATE_2M	0x01
+#define ZD_CS_CCK_RATE_5_5M	0x02
+#define ZD_CS_CCK_RATE_11M	0x03
+/* The rates for OFDM are encoded as in the PLCP header. Use ZD_OFDM_RATE_*.
+ */
+
+/* bit 5 is preamble (when in CCK mode), or a/g selection (when in OFDM mode) */
+#define ZD_CS_CCK_PREA_LONG	0x00
+#define ZD_CS_CCK_PREA_SHORT	0x20
+#define ZD_CS_OFDM_MODE_11G	0x00
+#define ZD_CS_OFDM_MODE_11A	0x20
+
+/* zd_ctrlset control field */
+#define ZD_CS_NEED_RANDOM_BACKOFF	0x01
+#define ZD_CS_MULTICAST			0x02
+
+#define ZD_CS_FRAME_TYPE_MASK		0x0c
+#define ZD_CS_DATA_FRAME		0x00
+#define ZD_CS_PS_POLL_FRAME		0x04
+#define ZD_CS_MANAGEMENT_FRAME		0x08
+#define ZD_CS_NO_SEQUENCE_CTL_FRAME	0x0c
+
+#define ZD_CS_WAKE_DESTINATION		0x10
+#define ZD_CS_RTS			0x20
+#define ZD_CS_ENCRYPT			0x40
+#define ZD_CS_SELF_CTS			0x80
+
+/* Incoming frames are prepended by a PLCP header */
+#define ZD_PLCP_HEADER_SIZE		5
+
+struct rx_length_info {
+	__le16 length[3];
+	__le16 tag;
+} __attribute__((packed));
+
+#define RX_LENGTH_INFO_TAG		0x697e
+
+struct rx_status {
+	/* rssi */
+	u8 signal_strength;
+	u8 signal_quality_cck;
+	u8 signal_quality_ofdm;
+	u8 decryption_type;
+	u8 frame_status;
+} __attribute__((packed));
+
+/* rx_status field decryption_type */
+#define ZD_RX_NO_WEP	0
+#define ZD_RX_WEP64	1
+#define ZD_RX_TKIP	2
+#define ZD_RX_AES	4
+#define ZD_RX_WEP128	5
+#define ZD_RX_WEP256	6
+
+/* rx_status field frame_status */
+#define ZD_RX_FRAME_MODULATION_MASK	0x01
+#define ZD_RX_CCK			0x00
+#define ZD_RX_OFDM			0x01
+
+#define ZD_RX_TIMEOUT_ERROR		0x02
+#define ZD_RX_FIFO_OVERRUN_ERROR	0x04
+#define ZD_RX_DECRYPTION_ERROR		0x08
+#define ZD_RX_CRC32_ERROR		0x10
+#define ZD_RX_NO_ADDR1_MATCH_ERROR	0x20
+#define ZD_RX_CRC16_ERROR		0x40
+#define ZD_RX_ERROR			0x80
+
+enum mac_flags {
+	MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct zd_mac {
+	struct net_device *netdev;
+	struct zd_chip chip;
+	spinlock_t lock;
+	/* Unlocked reading possible */
+	struct iw_statistics iw_stats;
+	u8 qual_average;
+	u8 rssi_average;
+	u8 regdomain;
+	u8 default_regdomain;
+	u8 requested_channel;
+};
+
+static inline struct ieee80211_device *zd_mac_to_ieee80211(struct zd_mac *mac)
+{
+	return zd_netdev_ieee80211(mac->netdev);
+}
+
+static inline struct zd_mac *zd_netdev_mac(struct net_device *netdev)
+{
+	return ieee80211softmac_priv(netdev);
+}
+
+static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip)
+{
+	return container_of(chip, struct zd_mac, chip);
+}
+
+static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb)
+{
+	return zd_chip_to_mac(zd_usb_to_chip(usb));
+}
+
+#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip))
+
+int zd_mac_init(struct zd_mac *mac,
+                struct net_device *netdev,
+		struct usb_interface *intf);
+void zd_mac_clear(struct zd_mac *mac);
+
+int zd_mac_init_hw(struct zd_mac *mac, u8 device_type);
+
+int zd_mac_open(struct net_device *netdev);
+int zd_mac_stop(struct net_device *netdev);
+int zd_mac_set_mac_address(struct net_device *dev, void *p);
+
+int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length);
+
+int zd_mac_set_regdomain(struct zd_mac *zd_mac, u8 regdomain);
+u8 zd_mac_get_regdomain(struct zd_mac *zd_mac);
+
+int zd_mac_request_channel(struct zd_mac *mac, u8 channel);
+int zd_mac_get_channel(struct zd_mac *mac, u8 *channel, u8 *flags);
+
+int zd_mac_set_mode(struct zd_mac *mac, u32 mode);
+int zd_mac_get_mode(struct zd_mac *mac, u32 *mode);
+
+int zd_mac_get_range(struct zd_mac *mac, struct iw_range *range);
+
+struct iw_statistics *zd_mac_get_wireless_stats(struct net_device *ndev);
+
+#ifdef DEBUG
+void zd_dump_rx_status(const struct rx_status *status);
+#else
+#define zd_dump_rx_status(status)
+#endif /* DEBUG */
+
+#endif /* _ZD_MAC_H */
diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.c b/drivers/net/wireless/zd1211rw/zd_netdev.c
new file mode 100644
index 0000000..9df232c
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_netdev.c
@@ -0,0 +1,267 @@
+/* zd_netdev.c
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211softmac.h>
+#include <net/ieee80211softmac_wx.h>
+#include <net/iw_handler.h>
+
+#include "zd_def.h"
+#include "zd_netdev.h"
+#include "zd_mac.h"
+#include "zd_ieee80211.h"
+
+/* Region 0 means reset regdomain to default. */
+static int zd_set_regdomain(struct net_device *netdev,
+	                    struct iw_request_info *info,
+			    union iwreq_data *req, char *extra)
+{
+	const u8 *regdomain = (u8 *)req;
+	return zd_mac_set_regdomain(zd_netdev_mac(netdev), *regdomain);
+}
+
+static int zd_get_regdomain(struct net_device *netdev,
+	                    struct iw_request_info *info,
+			    union iwreq_data *req, char *extra)
+{
+	u8 *regdomain = (u8 *)req;
+	if (!regdomain)
+		return -EINVAL;
+	*regdomain = zd_mac_get_regdomain(zd_netdev_mac(netdev));
+	return 0;
+}
+
+static const struct iw_priv_args zd_priv_args[] = {
+	{
+		.cmd = ZD_PRIV_SET_REGDOMAIN,
+		.set_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+		.name = "set_regdomain",
+	},
+	{
+		.cmd = ZD_PRIV_GET_REGDOMAIN,
+		.get_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
+		.name = "get_regdomain",
+	},
+};
+
+#define PRIV_OFFSET(x) [(x)-SIOCIWFIRSTPRIV]
+
+static const iw_handler zd_priv_handler[] = {
+	PRIV_OFFSET(ZD_PRIV_SET_REGDOMAIN) = zd_set_regdomain,
+	PRIV_OFFSET(ZD_PRIV_GET_REGDOMAIN) = zd_get_regdomain,
+};
+
+static int iw_get_name(struct net_device *netdev,
+	               struct iw_request_info *info,
+		       union iwreq_data *req, char *extra)
+{
+	/* FIXME: check whether 802.11a will also supported, add also
+	 *        zd1211B, if we support it.
+	 */
+	strlcpy(req->name, "802.11g zd1211", IFNAMSIZ);
+	return 0;
+}
+
+static int iw_set_freq(struct net_device *netdev,
+	               struct iw_request_info *info,
+		       union iwreq_data *req, char *extra)
+{
+	int r;
+	struct zd_mac *mac = zd_netdev_mac(netdev);
+	struct iw_freq *freq = &req->freq;
+	u8 channel;
+
+	r = zd_find_channel(&channel, freq);
+	if (r < 0)
+		return r;
+	r = zd_mac_request_channel(mac, channel);
+	return r;
+}
+
+static int iw_get_freq(struct net_device *netdev,
+	           struct iw_request_info *info,
+		   union iwreq_data *req, char *extra)
+{
+	int r;
+	struct zd_mac *mac = zd_netdev_mac(netdev);
+	struct iw_freq *freq = &req->freq;
+	u8 channel;
+	u8 flags;
+
+	r = zd_mac_get_channel(mac, &channel, &flags);
+	if (r)
+		return r;
+
+	freq->flags = (flags & MAC_FIXED_CHANNEL) ?
+		      IW_FREQ_FIXED : IW_FREQ_AUTO;
+	dev_dbg_f(zd_mac_dev(mac), "channel %s\n",
+		  (flags & MAC_FIXED_CHANNEL) ? "fixed" : "auto");
+	return zd_channel_to_freq(freq, channel);
+}
+
+static int iw_set_mode(struct net_device *netdev,
+	               struct iw_request_info *info,
+		       union iwreq_data *req, char *extra)
+{
+	return zd_mac_set_mode(zd_netdev_mac(netdev), req->mode);
+}
+
+static int iw_get_mode(struct net_device *netdev,
+	               struct iw_request_info *info,
+		       union iwreq_data *req, char *extra)
+{
+	return zd_mac_get_mode(zd_netdev_mac(netdev), &req->mode);
+}
+
+static int iw_get_range(struct net_device *netdev,
+	               struct iw_request_info *info,
+		       union iwreq_data *req, char *extra)
+{
+	struct iw_range *range = (struct iw_range *)extra;
+
+	dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n");
+	req->data.length = sizeof(*range);
+	return zd_mac_get_range(zd_netdev_mac(netdev), range);
+}
+
+static int iw_set_encode(struct net_device *netdev,
+			 struct iw_request_info *info,
+			 union iwreq_data *data,
+			 char *extra)
+{
+	return ieee80211_wx_set_encode(zd_netdev_ieee80211(netdev), info,
+		data, extra);
+}
+
+static int iw_get_encode(struct net_device *netdev,
+			 struct iw_request_info *info,
+			 union iwreq_data *data,
+			 char *extra)
+{
+	return ieee80211_wx_get_encode(zd_netdev_ieee80211(netdev), info,
+		data, extra);
+}
+
+static int iw_set_encodeext(struct net_device *netdev,
+			 struct iw_request_info *info,
+			 union iwreq_data *data,
+			 char *extra)
+{
+	return ieee80211_wx_set_encodeext(zd_netdev_ieee80211(netdev), info,
+		data, extra);
+}
+
+static int iw_get_encodeext(struct net_device *netdev,
+			 struct iw_request_info *info,
+			 union iwreq_data *data,
+			 char *extra)
+{
+	return ieee80211_wx_get_encodeext(zd_netdev_ieee80211(netdev), info,
+		data, extra);
+}
+
+#define WX(x) [(x)-SIOCIWFIRST]
+
+static const iw_handler zd_standard_iw_handlers[] = {
+	WX(SIOCGIWNAME)		= iw_get_name,
+	WX(SIOCSIWFREQ)		= iw_set_freq,
+	WX(SIOCGIWFREQ)		= iw_get_freq,
+	WX(SIOCSIWMODE)		= iw_set_mode,
+	WX(SIOCGIWMODE)		= iw_get_mode,
+	WX(SIOCGIWRANGE)	= iw_get_range,
+	WX(SIOCSIWENCODE)	= iw_set_encode,
+	WX(SIOCGIWENCODE)	= iw_get_encode,
+	WX(SIOCSIWENCODEEXT)	= iw_set_encodeext,
+	WX(SIOCGIWENCODEEXT)	= iw_get_encodeext,
+	WX(SIOCSIWAUTH)		= ieee80211_wx_set_auth,
+	WX(SIOCGIWAUTH)		= ieee80211_wx_get_auth,
+	WX(SIOCSIWSCAN)		= ieee80211softmac_wx_trigger_scan,
+	WX(SIOCGIWSCAN)		= ieee80211softmac_wx_get_scan_results,
+	WX(SIOCSIWESSID)	= ieee80211softmac_wx_set_essid,
+	WX(SIOCGIWESSID)	= ieee80211softmac_wx_get_essid,
+	WX(SIOCSIWAP)		= ieee80211softmac_wx_set_wap,
+	WX(SIOCGIWAP)		= ieee80211softmac_wx_get_wap,
+	WX(SIOCSIWRATE)		= ieee80211softmac_wx_set_rate,
+	WX(SIOCGIWRATE)		= ieee80211softmac_wx_get_rate,
+	WX(SIOCSIWGENIE)	= ieee80211softmac_wx_set_genie,
+	WX(SIOCGIWGENIE)	= ieee80211softmac_wx_get_genie,
+	WX(SIOCSIWMLME)		= ieee80211softmac_wx_set_mlme,
+};
+
+static const struct iw_handler_def iw_handler_def = {
+	.standard		= zd_standard_iw_handlers,
+	.num_standard		= ARRAY_SIZE(zd_standard_iw_handlers),
+	.private		= zd_priv_handler,
+	.num_private		= ARRAY_SIZE(zd_priv_handler),
+	.private_args		= zd_priv_args,
+	.num_private_args	= ARRAY_SIZE(zd_priv_args),
+	.get_wireless_stats	= zd_mac_get_wireless_stats,
+};
+
+struct net_device *zd_netdev_alloc(struct usb_interface *intf)
+{
+	int r;
+	struct net_device *netdev;
+	struct zd_mac *mac;
+
+	netdev = alloc_ieee80211softmac(sizeof(struct zd_mac));
+	if (!netdev) {
+		dev_dbg_f(&intf->dev, "out of memory\n");
+		return NULL;
+	}
+
+	mac = zd_netdev_mac(netdev);
+	r = zd_mac_init(mac, netdev, intf);
+	if (r) {
+		usb_set_intfdata(intf, NULL);
+		free_ieee80211(netdev);
+		return NULL;
+	}
+
+	SET_MODULE_OWNER(netdev);
+	SET_NETDEV_DEV(netdev, &intf->dev);
+
+	dev_dbg_f(&intf->dev, "netdev->flags %#06hx\n", netdev->flags);
+	dev_dbg_f(&intf->dev, "netdev->features %#010lx\n", netdev->features);
+
+	netdev->open = zd_mac_open;
+	netdev->stop = zd_mac_stop;
+	/* netdev->get_stats = */
+	/* netdev->set_multicast_list = */
+	netdev->set_mac_address = zd_mac_set_mac_address;
+	netdev->wireless_handlers = &iw_handler_def;
+	/* netdev->ethtool_ops = */
+
+	return netdev;
+}
+
+void zd_netdev_free(struct net_device *netdev)
+{
+	if (!netdev)
+		return;
+
+	zd_mac_clear(zd_netdev_mac(netdev));
+	free_ieee80211(netdev);
+}
+
+void zd_netdev_disconnect(struct net_device *netdev)
+{
+	unregister_netdev(netdev);
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.h b/drivers/net/wireless/zd1211rw/zd_netdev.h
new file mode 100644
index 0000000..374a957
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_netdev.h
@@ -0,0 +1,45 @@
+/* zd_netdev.h: Header for net device related functions.
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_NETDEV_H
+#define _ZD_NETDEV_H
+
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <net/ieee80211.h>
+
+#define ZD_PRIV_SET_REGDOMAIN (SIOCIWFIRSTPRIV)
+#define ZD_PRIV_GET_REGDOMAIN (SIOCIWFIRSTPRIV+1)
+
+static inline struct ieee80211_device *zd_netdev_ieee80211(
+	struct net_device *ndev)
+{
+	return netdev_priv(ndev);
+}
+
+static inline struct net_device *zd_ieee80211_to_netdev(
+	struct ieee80211_device *ieee)
+{
+	return ieee->dev;
+}
+
+struct net_device *zd_netdev_alloc(struct usb_interface *intf);
+void zd_netdev_free(struct net_device *netdev);
+
+void zd_netdev_disconnect(struct net_device *netdev);
+
+#endif /* _ZD_NETDEV_H */
diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c
new file mode 100644
index 0000000..d3770d2
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_rf.c
@@ -0,0 +1,151 @@
+/* zd_rf.c
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include "zd_def.h"
+#include "zd_rf.h"
+#include "zd_ieee80211.h"
+#include "zd_chip.h"
+
+static const char *rfs[] = {
+	[0]		= "unknown RF0",
+	[1]		= "unknown RF1",
+	[UW2451_RF]	= "UW2451_RF",
+	[UCHIP_RF]	= "UCHIP_RF",
+	[AL2230_RF]	= "AL2230_RF",
+	[AL7230B_RF]	= "AL7230B_RF",
+	[THETA_RF]	= "THETA_RF",
+	[AL2210_RF]	= "AL2210_RF",
+	[MAXIM_NEW_RF]	= "MAXIM_NEW_RF",
+	[UW2453_RF]	= "UW2453_RF",
+	[AL2230S_RF]	= "AL2230S_RF",
+	[RALINK_RF]	= "RALINK_RF",
+	[INTERSIL_RF]	= "INTERSIL_RF",
+	[RF2959_RF]	= "RF2959_RF",
+	[MAXIM_NEW2_RF]	= "MAXIM_NEW2_RF",
+	[PHILIPS_RF]	= "PHILIPS_RF",
+};
+
+const char *zd_rf_name(u8 type)
+{
+	if (type & 0xf0)
+		type = 0;
+	return rfs[type];
+}
+
+void zd_rf_init(struct zd_rf *rf)
+{
+	memset(rf, 0, sizeof(*rf));
+}
+
+void zd_rf_clear(struct zd_rf *rf)
+{
+	memset(rf, 0, sizeof(*rf));
+}
+
+int zd_rf_init_hw(struct zd_rf *rf, u8 type)
+{
+	int r, t;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	switch (type) {
+	case RF2959_RF:
+		r = zd_rf_init_rf2959(rf);
+		if (r)
+			return r;
+		break;
+	case AL2230_RF:
+		r = zd_rf_init_al2230(rf);
+		if (r)
+			return r;
+		break;
+	default:
+		dev_err(zd_chip_dev(chip),
+			"RF %s %#x is not supported\n", zd_rf_name(type), type);
+		rf->type = 0;
+		return -ENODEV;
+	}
+
+	rf->type = type;
+
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		return r;
+	t = rf->init_hw(rf);
+	r = zd_chip_unlock_phy_regs(chip);
+	if (t)
+		r = t;
+	return r;
+}
+
+int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size)
+{
+	return scnprintf(buffer, size, "%s", zd_rf_name(rf->type));
+}
+
+int zd_rf_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+
+	ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex));
+	if (channel < MIN_CHANNEL24)
+		return -EINVAL;
+	if (channel > MAX_CHANNEL24)
+		return -EINVAL;
+	dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel);
+
+	r = rf->set_channel(rf, channel);
+	if (r >= 0)
+		rf->channel = channel;
+	return r;
+}
+
+int zd_switch_radio_on(struct zd_rf *rf)
+{
+	int r, t;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		return r;
+	t = rf->switch_radio_on(rf);
+	r = zd_chip_unlock_phy_regs(chip);
+	if (t)
+		r = t;
+	return r;
+}
+
+int zd_switch_radio_off(struct zd_rf *rf)
+{
+	int r, t;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	/* TODO: move phy regs handling to zd_chip */
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_chip_lock_phy_regs(chip);
+	if (r)
+		return r;
+	t = rf->switch_radio_off(rf);
+	r = zd_chip_unlock_phy_regs(chip);
+	if (t)
+		r = t;
+	return r;
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h
new file mode 100644
index 0000000..ea30f69
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_rf.h
@@ -0,0 +1,82 @@
+/* zd_rf.h
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_RF_H
+#define _ZD_RF_H
+
+#include "zd_types.h"
+
+#define UW2451_RF			0x2
+#define UCHIP_RF			0x3
+#define AL2230_RF			0x4
+#define AL7230B_RF			0x5	/* a,b,g */
+#define THETA_RF			0x6
+#define AL2210_RF			0x7
+#define MAXIM_NEW_RF			0x8
+#define UW2453_RF			0x9
+#define AL2230S_RF			0xa
+#define RALINK_RF			0xb
+#define INTERSIL_RF			0xc
+#define RF2959_RF			0xd
+#define MAXIM_NEW2_RF			0xe
+#define PHILIPS_RF			0xf
+
+#define RF_CHANNEL(ch) [(ch)-1]
+
+/* Provides functions of the RF transceiver. */
+
+enum {
+	RF_REG_BITS = 6,
+	RF_VALUE_BITS = 18,
+	RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS,
+};
+
+struct zd_rf {
+	u8 type;
+
+	u8 channel;
+	/*
+	 * Whether this RF should patch the 6M band edge
+	 * (assuming E2P_POD agrees)
+	 */
+	u8 patch_6m_band_edge:1;
+
+	/* RF-specific functions */
+	int (*init_hw)(struct zd_rf *rf);
+	int (*set_channel)(struct zd_rf *rf, u8 channel);
+	int (*switch_radio_on)(struct zd_rf *rf);
+	int (*switch_radio_off)(struct zd_rf *rf);
+};
+
+const char *zd_rf_name(u8 type);
+void zd_rf_init(struct zd_rf *rf);
+void zd_rf_clear(struct zd_rf *rf);
+int zd_rf_init_hw(struct zd_rf *rf, u8 type);
+
+int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size);
+
+int zd_rf_set_channel(struct zd_rf *rf, u8 channel);
+
+int zd_switch_radio_on(struct zd_rf *rf);
+int zd_switch_radio_off(struct zd_rf *rf);
+
+/* Functions for individual RF chips */
+
+int zd_rf_init_rf2959(struct zd_rf *rf);
+int zd_rf_init_al2230(struct zd_rf *rf);
+
+#endif /* _ZD_RF_H */
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c
new file mode 100644
index 0000000..0948b25
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c
@@ -0,0 +1,308 @@
+/* zd_rf_al2230.c: Functions for the AL2230 RF controller
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+static const u32 al2230_table[][3] = {
+	RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, },
+	RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, },
+	RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, },
+	RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, },
+	RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, },
+	RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, },
+	RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, },
+	RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, },
+	RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, },
+	RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, },
+	RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, },
+	RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, },
+	RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, },
+	RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, },
+};
+
+static int zd1211_al2230_init_hw(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR15,   0x20 }, { CR23,   0x40 }, { CR24,  0x20 },
+		{ CR26,   0x11 }, { CR28,   0x3e }, { CR29,  0x00 },
+		{ CR44,   0x33 }, { CR106,  0x2a }, { CR107, 0x1a },
+		{ CR109,  0x09 }, { CR110,  0x27 }, { CR111, 0x2b },
+		{ CR112,  0x2b }, { CR119,  0x0a }, { CR10,  0x89 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR17,   0x28 },
+		{ CR26,   0x93 }, { CR34,   0x30 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR35,   0x3e },
+		{ CR41,   0x24 }, { CR44,   0x32 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR46,   0x96 },
+		{ CR47,   0x1e }, { CR79,   0x58 }, { CR80,  0x30 },
+		{ CR81,   0x30 }, { CR87,   0x0a }, { CR89,  0x04 },
+		{ CR92,   0x0a }, { CR99,   0x28 }, { CR100, 0x00 },
+		{ CR101,  0x13 }, { CR102,  0x27 }, { CR106, 0x24 },
+		{ CR107,  0x2a }, { CR109,  0x09 }, { CR110, 0x13 },
+		{ CR111,  0x1f }, { CR112,  0x1f }, { CR113, 0x27 },
+		{ CR114,  0x27 },
+		/* for newest (3rd cut) AL2300 */
+		{ CR115,  0x24 },
+		{ CR116,  0x24 }, { CR117,  0xf4 }, { CR118, 0xfc },
+		{ CR119,  0x10 }, { CR120,  0x4f }, { CR121, 0x77 },
+		{ CR122,  0xe0 }, { CR137,  0x88 }, { CR252, 0xff },
+		{ CR253,  0xff },
+
+		/* These following happen separately in the vendor driver */
+		{ },
+
+		/* shdnb(PLL_ON)=0 */
+		{ CR251,  0x2f },
+		/* shdnb(PLL_ON)=1 */
+		{ CR251,  0x3f },
+		{ CR138,  0x28 }, { CR203,  0x06 },
+	};
+
+	static const u32 rv[] = {
+		/* Channel 1 */
+		0x03f790,
+		0x033331,
+		0x00000d,
+
+		0x0b3331,
+		0x03b812,
+		0x00fff3,
+		0x000da4,
+		0x0f4dc5, /* fix freq shift, 0x04edc5 */
+		0x0805b6,
+		0x011687,
+		0x000688,
+		0x0403b9, /* external control TX power (CR31) */
+		0x00dbba,
+		0x00099b,
+		0x0bdffc,
+		0x00000d,
+		0x00500f,
+
+		/* These writes happen separately in the vendor driver */
+		0x00d00f,
+		0x004c0f,
+		0x00540f,
+		0x00700f,
+		0x00500f,
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
+	if (r)
+		return r;
+
+	return 0;
+}
+
+static int zd1211b_al2230_init_hw(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs1[] = {
+		{ CR10,  0x89 }, { CR15,  0x20 },
+		{ CR17,  0x2B }, /* for newest(3rd cut) AL2230 */
+		{ CR23,  0x40 }, { CR24,  0x20 }, { CR26,  0x93 },
+		{ CR28,  0x3e }, { CR29,  0x00 },
+		{ CR33,  0x28 }, /* 5621 */
+		{ CR34,  0x30 },
+		{ CR35,  0x3e }, /* for newest(3rd cut) AL2230 */
+		{ CR41,  0x24 }, { CR44,  0x32 },
+		{ CR46,  0x99 }, /* for newest(3rd cut) AL2230 */
+		{ CR47,  0x1e },
+
+		/* ZD1211B 05.06.10 */
+		{ CR48,  0x00 }, { CR49,  0x00 }, { CR51,  0x01 },
+		{ CR52,  0x80 }, { CR53,  0x7e }, { CR65,  0x00 },
+		{ CR66,  0x00 }, { CR67,  0x00 }, { CR68,  0x00 },
+		{ CR69,  0x28 },
+
+		{ CR79,  0x58 }, { CR80,  0x30 }, { CR81,  0x30 },
+		{ CR87,  0x0a }, { CR89,  0x04 },
+		{ CR91,  0x00 }, /* 5621 */
+		{ CR92,  0x0a },
+		{ CR98,  0x8d }, /* 4804,  for 1212 new algorithm */
+		{ CR99,  0x00 }, /* 5621 */
+		{ CR101, 0x13 }, { CR102, 0x27 },
+		{ CR106, 0x24 }, /* for newest(3rd cut) AL2230 */
+		{ CR107, 0x2a },
+		{ CR109, 0x13 }, /* 4804, for 1212 new algorithm */
+		{ CR110, 0x1f }, /* 4804, for 1212 new algorithm */
+		{ CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 },
+		{ CR114, 0x27 },
+		{ CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) AL2230 */
+		{ CR116, 0x24 },
+		{ CR117, 0xfa }, /* for 1211b */
+		{ CR118, 0xfa }, /* for 1211b */
+		{ CR119, 0x10 },
+		{ CR120, 0x4f },
+		{ CR121, 0x6c }, /* for 1211b */
+		{ CR122, 0xfc }, /* E0->FC at 4902 */
+		{ CR123, 0x57 }, /* 5623 */
+		{ CR125, 0xad }, /* 4804, for 1212 new algorithm */
+		{ CR126, 0x6c }, /* 5614 */
+		{ CR127, 0x03 }, /* 4804, for 1212 new algorithm */
+		{ CR137, 0x50 }, /* 5614 */
+		{ CR138, 0xa8 },
+		{ CR144, 0xac }, /* 5621 */
+		{ CR150, 0x0d }, { CR252, 0x00 }, { CR253, 0x00 },
+	};
+
+	static const u32 rv1[] = {
+		/* channel 1 */
+		0x03f790,
+		0x033331,
+		0x00000d,
+
+		0x0b3331,
+		0x03b812,
+		0x00fff3,
+		0x0005a4,
+		0x0f4dc5, /* fix freq shift 0x044dc5 */
+		0x0805b6,
+		0x0146c7,
+		0x000688,
+		0x0403b9, /* External control TX power (CR31) */
+		0x00dbba,
+		0x00099b,
+		0x0bdffc,
+		0x00000d,
+		0x00580f,
+	};
+
+	static const struct zd_ioreq16 ioreqs2[] = {
+		{ CR47,  0x1e }, { CR_RFCFG, 0x03 },
+	};
+
+	static const u32 rv2[] = {
+		0x00880f,
+		0x00080f,
+	};
+
+	static const struct zd_ioreq16 ioreqs3[] = {
+		{ CR_RFCFG, 0x00 }, { CR47, 0x1e }, { CR251, 0x7f },
+	};
+
+	static const u32 rv3[] = {
+		0x00d80f,
+		0x00780f,
+		0x00580f,
+	};
+
+	static const struct zd_ioreq16 ioreqs4[] = {
+		{ CR138, 0x28 }, { CR203, 0x06 },
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1));
+	if (r)
+		return r;
+	r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS);
+	if (r)
+		return r;
+	r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2));
+	if (r)
+		return r;
+	r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS);
+	if (r)
+		return r;
+	r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3));
+	if (r)
+		return r;
+	r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS);
+	if (r)
+		return r;
+	return zd_iowrite16a_locked(chip, ioreqs4, ARRAY_SIZE(ioreqs4));
+}
+
+static int al2230_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int r;
+	const u32 *rv = al2230_table[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR138, 0x28 },
+		{ CR203, 0x06 },
+	};
+
+	r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS);
+	if (r)
+		return r;
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211_al2230_switch_radio_on(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x00 },
+		{ CR251, 0x3f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x00 },
+		{ CR251, 0x7f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int al2230_switch_radio_off(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR11,  0x04 },
+		{ CR251, 0x2f },
+	};
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rf_init_al2230(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	rf->set_channel = al2230_set_channel;
+	rf->switch_radio_off = al2230_switch_radio_off;
+	if (chip->is_zd1211b) {
+		rf->init_hw = zd1211b_al2230_init_hw;
+		rf->switch_radio_on = zd1211b_al2230_switch_radio_on;
+	} else {
+		rf->init_hw = zd1211_al2230_init_hw;
+		rf->switch_radio_on = zd1211_al2230_switch_radio_on;
+	}
+	rf->patch_6m_band_edge = 1;
+	return 0;
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c
new file mode 100644
index 0000000..5824727
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c
@@ -0,0 +1,279 @@
+/* zd_rf_rfmd.c: Functions for the RFMD RF controller
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+static u32 rf2959_table[][2] = {
+	RF_CHANNEL( 1) = { 0x181979, 0x1e6666 },
+	RF_CHANNEL( 2) = { 0x181989, 0x1e6666 },
+	RF_CHANNEL( 3) = { 0x181999, 0x1e6666 },
+	RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 },
+	RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 },
+	RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 },
+	RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 },
+	RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 },
+	RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 },
+	RF_CHANNEL(10) = { 0x181a09, 0x1e6666 },
+	RF_CHANNEL(11) = { 0x181a19, 0x1e6666 },
+	RF_CHANNEL(12) = { 0x181a29, 0x1e6666 },
+	RF_CHANNEL(13) = { 0x181a39, 0x1e6666 },
+	RF_CHANNEL(14) = { 0x181a60, 0x1c0000 },
+};
+
+#if 0
+static int bits(u32 rw, int from, int to)
+{
+	rw &= ~(0xffffffffU << (to+1));
+	rw >>= from;
+	return rw;
+}
+
+static int bit(u32 rw, int bit)
+{
+	return bits(rw, bit, bit);
+}
+
+static void dump_regwrite(u32 rw)
+{
+	int reg = bits(rw, 18, 22);
+	int rw_flag = bits(rw, 23, 23);
+	PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag);
+
+	switch (reg) {
+	case 0:
+		PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d"
+		       " if_vco_reg_en %d if_vga_en %d",
+		       bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1),
+		       bit(rw, 0));
+		break;
+	case 1:
+		PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d"
+		       " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d"
+		       " ifloopc %d dac1 %d",
+		       bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14),
+		       bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10),
+		       bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3));
+		break;
+	case 2:
+		PDEBUG("reg2 IFPLL2 n1 %d num1 %d",
+		       bits(rw, 6, 17), bits(rw, 0, 5));
+		break;
+	case 3:
+		PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17));
+		break;
+	case 4:
+		PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d",
+		       bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3));
+		break;
+	case 5:
+		PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d"
+		       " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d"
+		       " dac %d",
+		       bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14),
+		       bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10),
+		       bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3));
+		break;
+	case 6:
+		PDEBUG("reg6 RFPLL2 n %d num %d",
+		       bits(rw, 6, 17), bits(rw, 0, 5));
+		break;
+	case 7:
+		PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17));
+		break;
+	case 8:
+		PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d",
+		       bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3));
+		break;
+	case 9:
+		PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d",
+		       bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7),
+		       bits(rw, 0, 2));
+		break;
+	case 10:
+		PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d"
+		       " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d"
+		       " intbiasen %d tybypass %d",
+		       bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14),
+		       bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2),
+		       bit(rw, 1), bit(rw, 0));
+		break;
+	case 11:
+		PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d"
+			" tx_delay %d",
+			bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8),
+			bits(rw, 0, 2));
+		break;
+	case 12:
+		PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d",
+		       bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5));
+		break;
+	case 13:
+		PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d"
+		       " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d"
+		       " rf_biasvco %d",
+		       bit(rw, 17), bit(rw, 16), bit(rw, 15),
+		       bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4),
+		       bits(rw, 0, 2));
+		break;
+	case 14:
+		PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d"
+		       " tx_acal %d tx_pcal %d",
+		       bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8),
+		       bits(rw, 0, 3));
+		break;
+	}
+}
+#endif /* 0 */
+
+static int rf2959_init_hw(struct zd_rf *rf)
+{
+	int r;
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR2,   0x1E }, { CR9,   0x20 }, { CR10,  0x89 },
+		{ CR11,  0x00 }, { CR15,  0xD0 }, { CR17,  0x68 },
+		{ CR19,  0x4a }, { CR20,  0x0c }, { CR21,  0x0E },
+		{ CR23,  0x48 },
+		/* normal size for cca threshold */
+		{ CR24,  0x14 },
+		/* { CR24,  0x20 }, */
+		{ CR26,  0x90 }, { CR27,  0x30 }, { CR29,  0x20 },
+		{ CR31,  0xb2 }, { CR32,  0x43 }, { CR33,  0x28 },
+		{ CR38,  0x30 }, { CR34,  0x0f }, { CR35,  0xF0 },
+		{ CR41,  0x2a }, { CR46,  0x7F }, { CR47,  0x1E },
+		{ CR51,  0xc5 }, { CR52,  0xc5 }, { CR53,  0xc5 },
+		{ CR79,  0x58 }, { CR80,  0x30 }, { CR81,  0x30 },
+		{ CR82,  0x00 }, { CR83,  0x24 }, { CR84,  0x04 },
+		{ CR85,  0x00 }, { CR86,  0x10 }, { CR87,  0x2A },
+		{ CR88,  0x10 }, { CR89,  0x24 }, { CR90,  0x18 },
+		/* { CR91,  0x18 }, */
+		/* should solve continous CTS frame problems */
+		{ CR91,  0x00 },
+		{ CR92,  0x0a }, { CR93,  0x00 }, { CR94,  0x01 },
+		{ CR95,  0x00 }, { CR96,  0x40 }, { CR97,  0x37 },
+		{ CR98,  0x05 }, { CR99,  0x28 }, { CR100, 0x00 },
+		{ CR101, 0x13 }, { CR102, 0x27 }, { CR103, 0x27 },
+		{ CR104, 0x18 }, { CR105, 0x12 },
+		/* normal size */
+		{ CR106, 0x1a },
+		/* { CR106, 0x22 }, */
+		{ CR107, 0x24 }, { CR108, 0x0a }, { CR109, 0x13 },
+		{ CR110, 0x2F }, { CR111, 0x27 }, { CR112, 0x27 },
+		{ CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x40 },
+		{ CR116, 0x40 }, { CR117, 0xF0 }, { CR118, 0xF0 },
+		{ CR119, 0x16 },
+		/* no TX continuation */
+		{ CR122, 0x00 },
+		/* { CR122, 0xff }, */
+		{ CR127, 0x03 }, { CR131, 0x08 }, { CR138, 0x28 },
+		{ CR148, 0x44 }, { CR150, 0x10 }, { CR169, 0xBB },
+		{ CR170, 0xBB },
+	};
+
+	static const u32 rv[] = {
+		0x000007,  /* REG0(CFG1) */
+		0x07dd43,  /* REG1(IFPLL1) */
+		0x080959,  /* REG2(IFPLL2) */
+		0x0e6666,
+		0x116a57,  /* REG4 */
+		0x17dd43,  /* REG5 */
+		0x1819f9,  /* REG6 */
+		0x1e6666,
+		0x214554,
+		0x25e7fa,
+		0x27fffa,
+		/* The Zydas driver somehow forgets to set this value. It's
+		 * only set for Japan. We are using internal power control
+		 * for now.
+		 */
+		0x294128, /* internal power */
+		/* 0x28252c, */ /* External control TX power */
+		/* CR31_CCK, CR51_6-36M, CR52_48M, CR53_54M */
+		0x2c0000,
+		0x300000,
+		0x340000,  /* REG13(0xD) */
+		0x381e0f,  /* REG14(0xE) */
+		/* Bogus, RF2959's data sheet doesn't know register 27, which is
+		 * actually referenced here. The commented 0x11 is 17.
+		 */
+		0x6c180f,  /* REG27(0x11) */
+	};
+
+	r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+	if (r)
+		return r;
+
+	return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
+}
+
+static int rf2959_set_channel(struct zd_rf *rf, u8 channel)
+{
+	int i, r;
+	u32 *rv = rf2959_table[channel-1];
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	for (i = 0; i < 2; i++) {
+		r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS);
+		if (r)
+			return r;
+	}
+	return 0;
+}
+
+static int rf2959_switch_radio_on(struct zd_rf *rf)
+{
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR10, 0x89 },
+		{ CR11, 0x00 },
+	};
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int rf2959_switch_radio_off(struct zd_rf *rf)
+{
+	static const struct zd_ioreq16 ioreqs[] = {
+		{ CR10, 0x15 },
+		{ CR11, 0x81 },
+	};
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rf_init_rf2959(struct zd_rf *rf)
+{
+	struct zd_chip *chip = zd_rf_to_chip(rf);
+
+	if (chip->is_zd1211b) {
+		dev_err(zd_chip_dev(chip),
+		       "RF2959 is currently not supported for ZD1211B"
+		       " devices\n");
+		return -ENODEV;
+	}
+	rf->init_hw = rf2959_init_hw;
+	rf->set_channel = rf2959_set_channel;
+	rf->switch_radio_on = rf2959_switch_radio_on;
+	rf->switch_radio_off = rf2959_switch_radio_off;
+	return 0;
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_types.h b/drivers/net/wireless/zd1211rw/zd_types.h
new file mode 100644
index 0000000..0155a15
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_types.h
@@ -0,0 +1,71 @@
+/* zd_types.h
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_TYPES_H
+#define _ZD_TYPES_H
+
+#include <linux/types.h>
+
+/* We have three register spaces mapped into the overall USB address space of
+ * 64K words (16-bit values). There is the control register space of
+ * double-word registers, the eeprom register space and the firmware register
+ * space. The control register space is byte mapped, the others are word
+ * mapped.
+ *
+ * For that reason, we are using byte offsets for control registers and word
+ * offsets for everything else.
+ */
+
+typedef u32 __nocast zd_addr_t;
+
+enum {
+	ADDR_BASE_MASK		= 0xff000000,
+	ADDR_OFFSET_MASK	= 0x0000ffff,
+	ADDR_ZERO_MASK		= 0x00ff0000,
+	NULL_BASE		= 0x00000000,
+	USB_BASE		= 0x01000000,
+	CR_BASE			= 0x02000000,
+	CR_MAX_OFFSET		= 0x0b30,
+	E2P_BASE		= 0x03000000,
+	E2P_MAX_OFFSET		= 0x007e,
+	FW_BASE			= 0x04000000,
+	FW_MAX_OFFSET		= 0x0005,
+};
+
+#define ZD_ADDR_BASE(addr) ((u32)(addr) & ADDR_BASE_MASK)
+#define ZD_OFFSET(addr) ((u32)(addr) & ADDR_OFFSET_MASK)
+
+#define ZD_ADDR(base, offset) \
+	((zd_addr_t)(((base) & ADDR_BASE_MASK) | ((offset) & ADDR_OFFSET_MASK)))
+
+#define ZD_NULL_ADDR    ((zd_addr_t)0)
+#define USB_REG(offset)  ZD_ADDR(USB_BASE, offset)	/* word addressing */
+#define CTL_REG(offset)  ZD_ADDR(CR_BASE, offset)	/* byte addressing */
+#define E2P_REG(offset)  ZD_ADDR(E2P_BASE, offset)	/* word addressing */
+#define FW_REG(offset)   ZD_ADDR(FW_BASE, offset)	/* word addressing */
+
+static inline zd_addr_t zd_inc_word(zd_addr_t addr)
+{
+	u32 base = ZD_ADDR_BASE(addr);
+	u32 offset = ZD_OFFSET(addr);
+
+	offset += base == CR_BASE ? 2 : 1;
+
+	return base | offset;
+}
+
+#endif /* _ZD_TYPES_H */
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
new file mode 100644
index 0000000..ce1cb2c
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -0,0 +1,1316 @@
+/* zd_usb.c
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/unaligned.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <net/ieee80211.h>
+
+#include "zd_def.h"
+#include "zd_netdev.h"
+#include "zd_mac.h"
+#include "zd_usb.h"
+#include "zd_util.h"
+
+static struct usb_device_id usb_ids[] = {
+	/* ZD1211 */
+	{ USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 },
+	{ USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 },
+	/* ZD1211B */
+	{ USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B },
+	{ USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B },
+	{}
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip.");
+MODULE_AUTHOR("Ulrich Kunitz");
+MODULE_AUTHOR("Daniel Drake");
+MODULE_VERSION("1.0");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+#define FW_ZD1211_PREFIX	"zd1211/zd1211_"
+#define FW_ZD1211B_PREFIX	"zd1211/zd1211b_"
+
+/* register address handling */
+
+#ifdef DEBUG
+static int check_addr(struct zd_usb *usb, zd_addr_t addr)
+{
+	u32 base = ZD_ADDR_BASE(addr);
+	u32 offset = ZD_OFFSET(addr);
+
+	if ((u32)addr & ADDR_ZERO_MASK)
+		goto invalid_address;
+	switch (base) {
+	case USB_BASE:
+		break;
+	case CR_BASE:
+		if (offset > CR_MAX_OFFSET) {
+			dev_dbg(zd_usb_dev(usb),
+				"CR offset %#010x larger than"
+				" CR_MAX_OFFSET %#10x\n",
+				offset, CR_MAX_OFFSET);
+			goto invalid_address;
+		}
+		if (offset & 1) {
+			dev_dbg(zd_usb_dev(usb),
+				"CR offset %#010x is not a multiple of 2\n",
+				offset);
+			goto invalid_address;
+		}
+		break;
+	case E2P_BASE:
+		if (offset > E2P_MAX_OFFSET) {
+			dev_dbg(zd_usb_dev(usb),
+				"E2P offset %#010x larger than"
+				" E2P_MAX_OFFSET %#010x\n",
+				offset, E2P_MAX_OFFSET);
+			goto invalid_address;
+		}
+		break;
+	case FW_BASE:
+		if (!usb->fw_base_offset) {
+			dev_dbg(zd_usb_dev(usb),
+			       "ERROR: fw base offset has not been set\n");
+			return -EAGAIN;
+		}
+		if (offset > FW_MAX_OFFSET) {
+			dev_dbg(zd_usb_dev(usb),
+				"FW offset %#10x is larger than"
+				" FW_MAX_OFFSET %#010x\n",
+				offset, FW_MAX_OFFSET);
+			goto invalid_address;
+		}
+		break;
+	default:
+		dev_dbg(zd_usb_dev(usb),
+			"address has unsupported base %#010x\n", addr);
+		goto invalid_address;
+	}
+
+	return 0;
+invalid_address:
+	dev_dbg(zd_usb_dev(usb),
+		"ERROR: invalid address: %#010x\n", addr);
+	return -EINVAL;
+}
+#endif /* DEBUG */
+
+static u16 usb_addr(struct zd_usb *usb, zd_addr_t addr)
+{
+	u32 base;
+	u16 offset;
+
+	base = ZD_ADDR_BASE(addr);
+	offset = ZD_OFFSET(addr);
+
+	ZD_ASSERT(check_addr(usb, addr) == 0);
+
+	switch (base) {
+	case CR_BASE:
+		offset += CR_BASE_OFFSET;
+		break;
+	case E2P_BASE:
+		offset += E2P_BASE_OFFSET;
+		break;
+	case FW_BASE:
+		offset += usb->fw_base_offset;
+		break;
+	}
+
+	return offset;
+}
+
+/* USB device initialization */
+
+static int request_fw_file(
+	const struct firmware **fw, const char *name, struct device *device)
+{
+	int r;
+
+	dev_dbg_f(device, "fw name %s\n", name);
+
+	r = request_firmware(fw, name, device);
+	if (r)
+		dev_err(device,
+		       "Could not load firmware file %s. Error number %d\n",
+		       name, r);
+	return r;
+}
+
+static inline u16 get_bcdDevice(const struct usb_device *udev)
+{
+	return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+enum upload_code_flags {
+	REBOOT = 1,
+};
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+static int upload_code(struct usb_device *udev,
+	const u8 *data, size_t size, u16 code_offset, int flags)
+{
+	u8 *p;
+	int r;
+
+	/* USB request blocks need "kmalloced" buffers.
+	 */
+	p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL);
+	if (!p) {
+		dev_err(&udev->dev, "out of memory\n");
+		r = -ENOMEM;
+		goto error;
+	}
+
+	size &= ~1;
+	while (size > 0) {
+		size_t transfer_size = size <= MAX_TRANSFER_SIZE ?
+			size : MAX_TRANSFER_SIZE;
+
+		dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size);
+
+		memcpy(p, data, transfer_size);
+		r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			USB_REQ_FIRMWARE_DOWNLOAD,
+			USB_DIR_OUT | USB_TYPE_VENDOR,
+			code_offset, 0, p, transfer_size, 1000 /* ms */);
+		if (r < 0) {
+			dev_err(&udev->dev,
+			       "USB control request for firmware upload"
+			       " failed. Error number %d\n", r);
+			goto error;
+		}
+		transfer_size = r & ~1;
+
+		size -= transfer_size;
+		data += transfer_size;
+		code_offset += transfer_size/sizeof(u16);
+	}
+
+	if (flags & REBOOT) {
+		u8 ret;
+
+		r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			USB_REQ_FIRMWARE_CONFIRM,
+			USB_DIR_IN | USB_TYPE_VENDOR,
+			0, 0, &ret, sizeof(ret), 5000 /* ms */);
+		if (r != sizeof(ret)) {
+			dev_err(&udev->dev,
+				"control request firmeware confirmation failed."
+				" Return value %d\n", r);
+			if (r >= 0)
+				r = -ENODEV;
+			goto error;
+		}
+		if (ret & 0x80) {
+			dev_err(&udev->dev,
+				"Internal error while downloading."
+				" Firmware confirm return value %#04x\n",
+				(unsigned int)ret);
+			r = -ENODEV;
+			goto error;
+		}
+		dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n",
+			(unsigned int)ret);
+	}
+
+	r = 0;
+error:
+	kfree(p);
+	return r;
+}
+
+static u16 get_word(const void *data, u16 offset)
+{
+	const __le16 *p = data;
+	return le16_to_cpu(p[offset]);
+}
+
+static char *get_fw_name(char *buffer, size_t size, u8 device_type,
+	               const char* postfix)
+{
+	scnprintf(buffer, size, "%s%s",
+		device_type == DEVICE_ZD1211B ?
+			FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX,
+		postfix);
+	return buffer;
+}
+
+static int upload_firmware(struct usb_device *udev, u8 device_type)
+{
+	int r;
+	u16 fw_bcdDevice;
+	u16 bcdDevice;
+	const struct firmware *ub_fw = NULL;
+	const struct firmware *uph_fw = NULL;
+	char fw_name[128];
+
+	bcdDevice = get_bcdDevice(udev);
+
+	r = request_fw_file(&ub_fw,
+		get_fw_name(fw_name, sizeof(fw_name), device_type,  "ub"),
+		&udev->dev);
+	if (r)
+		goto error;
+
+	fw_bcdDevice = get_word(ub_fw->data, EEPROM_REGS_OFFSET);
+
+	/* FIXME: do we have any reason to perform the kludge that the vendor
+	 * driver does when there is a version mismatch? (their driver uploads
+	 * different firmwares and stuff)
+	 */
+	if (fw_bcdDevice != bcdDevice) {
+		dev_info(&udev->dev,
+			"firmware device id %#06x and actual device id "
+			"%#06x differ, continuing anyway\n",
+			fw_bcdDevice, bcdDevice);
+	} else {
+		dev_dbg_f(&udev->dev,
+			"firmware device id %#06x is equal to the "
+			"actual device id\n", fw_bcdDevice);
+	}
+
+
+	r = request_fw_file(&uph_fw,
+		get_fw_name(fw_name, sizeof(fw_name), device_type, "uphr"),
+		&udev->dev);
+	if (r)
+		goto error;
+
+	r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START_OFFSET,
+		        REBOOT);
+	if (r) {
+		dev_err(&udev->dev,
+			"Could not upload firmware code uph. Error number %d\n",
+			r);
+	}
+
+	/* FALL-THROUGH */
+error:
+	release_firmware(ub_fw);
+	release_firmware(uph_fw);
+	return r;
+}
+
+static void disable_read_regs_int(struct zd_usb *usb)
+{
+	struct zd_usb_interrupt *intr = &usb->intr;
+
+	ZD_ASSERT(in_interrupt());
+	spin_lock(&intr->lock);
+	intr->read_regs_enabled = 0;
+	spin_unlock(&intr->lock);
+}
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+static inline void handle_regs_int(struct urb *urb)
+{
+	struct zd_usb *usb = urb->context;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	int len;
+
+	ZD_ASSERT(in_interrupt());
+	spin_lock(&intr->lock);
+
+	if (intr->read_regs_enabled) {
+		intr->read_regs.length = len = urb->actual_length;
+
+		if (len > sizeof(intr->read_regs.buffer))
+			len = sizeof(intr->read_regs.buffer);
+		memcpy(intr->read_regs.buffer, urb->transfer_buffer, len);
+		intr->read_regs_enabled = 0;
+		complete(&intr->read_regs.completion);
+		goto out;
+	}
+
+	dev_dbg_f(urb_dev(urb), "regs interrupt ignored\n");
+out:
+	spin_unlock(&intr->lock);
+}
+
+static inline void handle_retry_failed_int(struct urb *urb)
+{
+	dev_dbg_f(urb_dev(urb), "retry failed interrupt\n");
+}
+
+
+static void int_urb_complete(struct urb *urb, struct pt_regs *pt_regs)
+{
+	int r;
+	struct usb_int_header *hdr;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+		goto kfree;
+	case -EPIPE:
+		usb_clear_halt(urb->dev, EP_INT_IN);
+		/* FALL-THROUGH */
+	default:
+		goto resubmit;
+	}
+
+	if (urb->actual_length < sizeof(hdr)) {
+		dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb);
+		goto resubmit;
+	}
+
+	hdr = urb->transfer_buffer;
+	if (hdr->type != USB_INT_TYPE) {
+		dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb);
+		goto resubmit;
+	}
+
+	switch (hdr->id) {
+	case USB_INT_ID_REGS:
+		handle_regs_int(urb);
+		break;
+	case USB_INT_ID_RETRY_FAILED:
+		handle_retry_failed_int(urb);
+		break;
+	default:
+		dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb,
+			(unsigned int)hdr->id);
+		goto resubmit;
+	}
+
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg_f(urb_dev(urb), "resubmit urb %p\n", urb);
+		goto kfree;
+	}
+	return;
+kfree:
+	kfree(urb->transfer_buffer);
+}
+
+static inline int int_urb_interval(struct usb_device *udev)
+{
+	switch (udev->speed) {
+	case USB_SPEED_HIGH:
+		return 4;
+	case USB_SPEED_LOW:
+		return 10;
+	case USB_SPEED_FULL:
+	default:
+		return 1;
+	}
+}
+
+static inline int usb_int_enabled(struct zd_usb *usb)
+{
+	unsigned long flags;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	struct urb *urb;
+
+	spin_lock_irqsave(&intr->lock, flags);
+	urb = intr->urb;
+	spin_unlock_irqrestore(&intr->lock, flags);
+	return urb != NULL;
+}
+
+int zd_usb_enable_int(struct zd_usb *usb)
+{
+	int r;
+	struct usb_device *udev;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	void *transfer_buffer = NULL;
+	struct urb *urb;
+
+	dev_dbg_f(zd_usb_dev(usb), "\n");
+
+	urb = usb_alloc_urb(0, GFP_NOFS);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	ZD_ASSERT(!irqs_disabled());
+	spin_lock_irq(&intr->lock);
+	if (intr->urb) {
+		spin_unlock_irq(&intr->lock);
+		r = 0;
+		goto error_free_urb;
+	}
+	intr->urb = urb;
+	spin_unlock_irq(&intr->lock);
+
+	/* TODO: make it a DMA buffer */
+	r = -ENOMEM;
+	transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_NOFS);
+	if (!transfer_buffer) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"couldn't allocate transfer_buffer\n");
+		goto error_set_urb_null;
+	}
+
+	udev = zd_usb_to_usbdev(usb);
+	usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN),
+			 transfer_buffer, USB_MAX_EP_INT_BUFFER,
+			 int_urb_complete, usb,
+			 intr->interval);
+
+	dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb);
+	r = usb_submit_urb(urb, GFP_NOFS);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "Couldn't submit urb. Error number %d\n", r);
+		goto error;
+	}
+
+	return 0;
+error:
+	kfree(transfer_buffer);
+error_set_urb_null:
+	spin_lock_irq(&intr->lock);
+	intr->urb = NULL;
+	spin_unlock_irq(&intr->lock);
+error_free_urb:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+void zd_usb_disable_int(struct zd_usb *usb)
+{
+	unsigned long flags;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	struct urb *urb;
+
+	spin_lock_irqsave(&intr->lock, flags);
+	urb = intr->urb;
+	if (!urb) {
+		spin_unlock_irqrestore(&intr->lock, flags);
+		return;
+	}
+	intr->urb = NULL;
+	spin_unlock_irqrestore(&intr->lock, flags);
+
+	usb_kill_urb(urb);
+	dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb);
+	usb_free_urb(urb);
+}
+
+static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
+			     unsigned int length)
+{
+	int i;
+	struct zd_mac *mac = zd_usb_to_mac(usb);
+	const struct rx_length_info *length_info;
+
+	if (length < sizeof(struct rx_length_info)) {
+		/* It's not a complete packet anyhow. */
+		return;
+	}
+	length_info = (struct rx_length_info *)
+		(buffer + length - sizeof(struct rx_length_info));
+
+	/* It might be that three frames are merged into a single URB
+	 * transaction. We have to check for the length info tag.
+	 *
+	 * While testing we discovered that length_info might be unaligned,
+	 * because if USB transactions are merged, the last packet will not
+	 * be padded. Unaligned access might also happen if the length_info
+	 * structure is not present.
+	 */
+	if (get_unaligned(&length_info->tag) == RX_LENGTH_INFO_TAG) {
+		unsigned int l, k, n;
+		for (i = 0, l = 0;; i++) {
+			k = le16_to_cpu(get_unaligned(
+				&length_info->length[i]));
+			n = l+k;
+			if (n > length)
+				return;
+			zd_mac_rx(mac, buffer+l, k);
+			if (i >= 2)
+				return;
+			l = (n+3) & ~3;
+		}
+	} else {
+		zd_mac_rx(mac, buffer, length);
+	}
+}
+
+static void rx_urb_complete(struct urb *urb, struct pt_regs *pt_regs)
+{
+	struct zd_usb *usb;
+	struct zd_usb_rx *rx;
+	const u8 *buffer;
+	unsigned int length;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+		return;
+	case -EPIPE:
+		usb_clear_halt(urb->dev, EP_DATA_IN);
+		/* FALL-THROUGH */
+	default:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+
+	buffer = urb->transfer_buffer;
+	length = urb->actual_length;
+	usb = urb->context;
+	rx = &usb->rx;
+
+	if (length%rx->usb_packet_size > rx->usb_packet_size-4) {
+		/* If there is an old first fragment, we don't care. */
+		dev_dbg_f(urb_dev(urb), "*** first fragment ***\n");
+		ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment));
+		spin_lock(&rx->lock);
+		memcpy(rx->fragment, buffer, length);
+		rx->fragment_length = length;
+		spin_unlock(&rx->lock);
+		goto resubmit;
+	}
+
+	spin_lock(&rx->lock);
+	if (rx->fragment_length > 0) {
+		/* We are on a second fragment, we believe */
+		ZD_ASSERT(length + rx->fragment_length <=
+			  ARRAY_SIZE(rx->fragment));
+		dev_dbg_f(urb_dev(urb), "*** second fragment ***\n");
+		memcpy(rx->fragment+rx->fragment_length, buffer, length);
+		handle_rx_packet(usb, rx->fragment,
+			         rx->fragment_length + length);
+		rx->fragment_length = 0;
+		spin_unlock(&rx->lock);
+	} else {
+		spin_unlock(&rx->lock);
+		handle_rx_packet(usb, buffer, length);
+	}
+
+resubmit:
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+struct urb *alloc_urb(struct zd_usb *usb)
+{
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_NOFS);
+	if (!urb)
+		return NULL;
+	buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_NOFS,
+		                  &urb->transfer_dma);
+	if (!buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+		          buffer, USB_MAX_RX_SIZE,
+			  rx_urb_complete, usb);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	return urb;
+}
+
+void free_urb(struct urb *urb)
+{
+	if (!urb)
+		return;
+	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+		        urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+}
+
+int zd_usb_enable_rx(struct zd_usb *usb)
+{
+	int i, r;
+	struct zd_usb_rx *rx = &usb->rx;
+	struct urb **urbs;
+
+	dev_dbg_f(zd_usb_dev(usb), "\n");
+
+	r = -ENOMEM;
+	urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_NOFS);
+	if (!urbs)
+		goto error;
+	for (i = 0; i < URBS_COUNT; i++) {
+		urbs[i] = alloc_urb(usb);
+		if (!urbs[i])
+			goto error;
+	}
+
+	ZD_ASSERT(!irqs_disabled());
+	spin_lock_irq(&rx->lock);
+	if (rx->urbs) {
+		spin_unlock_irq(&rx->lock);
+		r = 0;
+		goto error;
+	}
+	rx->urbs = urbs;
+	rx->urbs_count = URBS_COUNT;
+	spin_unlock_irq(&rx->lock);
+
+	for (i = 0; i < URBS_COUNT; i++) {
+		r = usb_submit_urb(urbs[i], GFP_NOFS);
+		if (r)
+			goto error_submit;
+	}
+
+	return 0;
+error_submit:
+	for (i = 0; i < URBS_COUNT; i++) {
+		usb_kill_urb(urbs[i]);
+	}
+	spin_lock_irq(&rx->lock);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irq(&rx->lock);
+error:
+	if (urbs) {
+		for (i = 0; i < URBS_COUNT; i++)
+			free_urb(urbs[i]);
+	}
+	return r;
+}
+
+void zd_usb_disable_rx(struct zd_usb *usb)
+{
+	int i;
+	unsigned long flags;
+	struct urb **urbs;
+	unsigned int count;
+	struct zd_usb_rx *rx = &usb->rx;
+
+	spin_lock_irqsave(&rx->lock, flags);
+	urbs = rx->urbs;
+	count = rx->urbs_count;
+	spin_unlock_irqrestore(&rx->lock, flags);
+	if (!urbs)
+		return;
+
+	for (i = 0; i < count; i++) {
+		usb_kill_urb(urbs[i]);
+		free_urb(urbs[i]);
+	}
+	kfree(urbs);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	rx->urbs = NULL;
+	rx->urbs_count = 0;
+	spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+static void tx_urb_complete(struct urb *urb, struct pt_regs *pt_regs)
+{
+	int r;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ESHUTDOWN:
+	case -EINVAL:
+	case -ENODEV:
+	case -ENOENT:
+	case -ECONNRESET:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		break;
+	case -EPIPE:
+		usb_clear_halt(urb->dev, EP_DATA_OUT);
+		/* FALL-THROUGH */
+	default:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+		goto resubmit;
+	}
+free_urb:
+	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+		        urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb(urb);
+	return;
+resubmit:
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r) {
+		dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r);
+		goto free_urb;
+	}
+}
+
+/* Puts the frame on the USB endpoint. It doesn't wait for
+ * completion. The frame must contain the control set.
+ */
+int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length)
+{
+	int r;
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
+	struct urb *urb;
+	void *buffer;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	buffer = usb_buffer_alloc(zd_usb_to_usbdev(usb), length, GFP_ATOMIC,
+		                  &urb->transfer_dma);
+	if (!buffer) {
+		r = -ENOMEM;
+		goto error_free_urb;
+	}
+	memcpy(buffer, frame, length);
+
+	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+		          buffer, length, tx_urb_complete, NULL);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r)
+		goto error;
+	return 0;
+error:
+	usb_buffer_free(zd_usb_to_usbdev(usb), length, buffer,
+		        urb->transfer_dma);
+error_free_urb:
+	usb_free_urb(urb);
+out:
+	return r;
+}
+
+static inline void init_usb_interrupt(struct zd_usb *usb)
+{
+	struct zd_usb_interrupt *intr = &usb->intr;
+
+	spin_lock_init(&intr->lock);
+	intr->interval = int_urb_interval(zd_usb_to_usbdev(usb));
+	init_completion(&intr->read_regs.completion);
+	intr->read_regs.cr_int_addr = cpu_to_le16(usb_addr(usb, CR_INTERRUPT));
+}
+
+static inline void init_usb_rx(struct zd_usb *usb)
+{
+	struct zd_usb_rx *rx = &usb->rx;
+	spin_lock_init(&rx->lock);
+	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) {
+		rx->usb_packet_size = 512;
+	} else {
+		rx->usb_packet_size = 64;
+	}
+	ZD_ASSERT(rx->fragment_length == 0);
+}
+
+static inline void init_usb_tx(struct zd_usb *usb)
+{
+	/* FIXME: at this point we will allocate a fixed number of urb's for
+	 * use in a cyclic scheme */
+}
+
+void zd_usb_init(struct zd_usb *usb, struct net_device *netdev,
+	         struct usb_interface *intf)
+{
+	memset(usb, 0, sizeof(*usb));
+	usb->intf = usb_get_intf(intf);
+	usb_set_intfdata(usb->intf, netdev);
+	init_usb_interrupt(usb);
+	init_usb_tx(usb);
+	init_usb_rx(usb);
+}
+
+int zd_usb_init_hw(struct zd_usb *usb)
+{
+	int r;
+	struct zd_chip *chip = zd_usb_to_chip(usb);
+
+	ZD_ASSERT(mutex_is_locked(&chip->mutex));
+	r = zd_ioread16_locked(chip, &usb->fw_base_offset,
+		        USB_REG((u16)FW_BASE_ADDR_OFFSET));
+	if (r)
+		return r;
+	dev_dbg_f(zd_usb_dev(usb), "fw_base_offset: %#06hx\n",
+		 usb->fw_base_offset);
+
+	return 0;
+}
+
+void zd_usb_clear(struct zd_usb *usb)
+{
+	usb_set_intfdata(usb->intf, NULL);
+	usb_put_intf(usb->intf);
+	memset(usb, 0, sizeof(*usb));
+	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+static const char *speed(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return "low";
+	case USB_SPEED_FULL:
+		return "full";
+	case USB_SPEED_HIGH:
+		return "high";
+	default:
+		return "unknown speed";
+	}
+}
+
+static int scnprint_id(struct usb_device *udev, char *buffer, size_t size)
+{
+	return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s",
+		le16_to_cpu(udev->descriptor.idVendor),
+		le16_to_cpu(udev->descriptor.idProduct),
+		get_bcdDevice(udev),
+		speed(udev->speed));
+}
+
+int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size)
+{
+	struct usb_device *udev = interface_to_usbdev(usb->intf);
+	return scnprint_id(udev, buffer, size);
+}
+
+#ifdef DEBUG
+static void print_id(struct usb_device *udev)
+{
+	char buffer[40];
+
+	scnprint_id(udev, buffer, sizeof(buffer));
+	buffer[sizeof(buffer)-1] = 0;
+	dev_dbg_f(&udev->dev, "%s\n", buffer);
+}
+#else
+#define print_id(udev) do { } while (0)
+#endif
+
+static int probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	int r;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct net_device *netdev = NULL;
+
+	print_id(udev);
+
+	switch (udev->speed) {
+	case USB_SPEED_LOW:
+	case USB_SPEED_FULL:
+	case USB_SPEED_HIGH:
+		break;
+	default:
+		dev_dbg_f(&intf->dev, "Unknown USB speed\n");
+		r = -ENODEV;
+		goto error;
+	}
+
+	netdev = zd_netdev_alloc(intf);
+	if (netdev == NULL) {
+		r = -ENOMEM;
+		goto error;
+	}
+
+	r = upload_firmware(udev, id->driver_info);
+	if (r) {
+		dev_err(&intf->dev,
+		       "couldn't load firmware. Error number %d\n", r);
+		goto error;
+	}
+
+	r = usb_reset_configuration(udev);
+	if (r) {
+		dev_dbg_f(&intf->dev,
+			"couldn't reset configuration. Error number %d\n", r);
+		goto error;
+	}
+
+	/* At this point the interrupt endpoint is not generally enabled. We
+	 * save the USB bandwidth until the network device is opened. But
+	 * notify that the initialization of the MAC will require the
+	 * interrupts to be temporary enabled.
+	 */
+	r = zd_mac_init_hw(zd_netdev_mac(netdev), id->driver_info);
+	if (r) {
+		dev_dbg_f(&intf->dev,
+		         "couldn't initialize mac. Error number %d\n", r);
+		goto error;
+	}
+
+	r = register_netdev(netdev);
+	if (r) {
+		dev_dbg_f(&intf->dev,
+			 "couldn't register netdev. Error number %d\n", r);
+		goto error;
+	}
+
+	dev_dbg_f(&intf->dev, "successful\n");
+	dev_info(&intf->dev,"%s\n", netdev->name);
+	return 0;
+error:
+	usb_reset_device(interface_to_usbdev(intf));
+	zd_netdev_free(netdev);
+	return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+	struct net_device *netdev = zd_intf_to_netdev(intf);
+	struct zd_mac *mac = zd_netdev_mac(netdev);
+	struct zd_usb *usb = &mac->chip.usb;
+
+	dev_dbg_f(zd_usb_dev(usb), "\n");
+
+	zd_netdev_disconnect(netdev);
+
+	/* Just in case something has gone wrong! */
+	zd_usb_disable_rx(usb);
+	zd_usb_disable_int(usb);
+
+	/* If the disconnect has been caused by a removal of the
+	 * driver module, the reset allows reloading of the driver. If the
+	 * reset will not be executed here, the upload of the firmware in the
+	 * probe function caused by the reloading of the driver will fail.
+	 */
+	usb_reset_device(interface_to_usbdev(intf));
+
+	/* If somebody still waits on this lock now, this is an error. */
+	zd_netdev_free(netdev);
+	dev_dbg(&intf->dev, "disconnected\n");
+}
+
+static struct usb_driver driver = {
+	.name		= "zd1211rw",
+	.id_table	= usb_ids,
+	.probe		= probe,
+	.disconnect	= disconnect,
+};
+
+static int __init usb_init(void)
+{
+	int r;
+
+	pr_debug("usb_init()\n");
+
+	r = usb_register(&driver);
+	if (r) {
+		printk(KERN_ERR "usb_register() failed. Error number %d\n", r);
+		return r;
+	}
+
+	pr_debug("zd1211rw initialized\n");
+	return 0;
+}
+
+static void __exit usb_exit(void)
+{
+	pr_debug("usb_exit()\n");
+	usb_deregister(&driver);
+}
+
+module_init(usb_init);
+module_exit(usb_exit);
+
+static int usb_int_regs_length(unsigned int count)
+{
+	return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data);
+}
+
+static void prepare_read_regs_int(struct zd_usb *usb)
+{
+	struct zd_usb_interrupt *intr = &usb->intr;
+
+	spin_lock(&intr->lock);
+	intr->read_regs_enabled = 1;
+	INIT_COMPLETION(intr->read_regs.completion);
+	spin_unlock(&intr->lock);
+}
+
+static int get_results(struct zd_usb *usb, u16 *values,
+	               struct usb_req_read_regs *req, unsigned int count)
+{
+	int r;
+	int i;
+	struct zd_usb_interrupt *intr = &usb->intr;
+	struct read_regs_int *rr = &intr->read_regs;
+	struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer;
+
+	spin_lock(&intr->lock);
+
+	r = -EIO;
+	/* The created block size seems to be larger than expected.
+	 * However results appear to be correct.
+	 */
+	if (rr->length < usb_int_regs_length(count)) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "error: actual length %d less than expected %d\n",
+			 rr->length, usb_int_regs_length(count));
+		goto error_unlock;
+	}
+	if (rr->length > sizeof(rr->buffer)) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "error: actual length %d exceeds buffer size %zu\n",
+			 rr->length, sizeof(rr->buffer));
+		goto error_unlock;
+	}
+
+	for (i = 0; i < count; i++) {
+		struct reg_data *rd = &regs->regs[i];
+		if (rd->addr != req->addr[i]) {
+			dev_dbg_f(zd_usb_dev(usb),
+				 "rd[%d] addr %#06hx expected %#06hx\n", i,
+				 le16_to_cpu(rd->addr),
+				 le16_to_cpu(req->addr[i]));
+			goto error_unlock;
+		}
+		values[i] = le16_to_cpu(rd->value);
+	}
+
+	r = 0;
+error_unlock:
+	spin_unlock(&intr->lock);
+	return r;
+}
+
+int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
+	             const zd_addr_t *addresses, unsigned int count)
+{
+	int r;
+	int i, req_len, actual_req_len;
+	struct usb_device *udev;
+	struct usb_req_read_regs *req = NULL;
+	unsigned long timeout;
+
+	if (count < 1) {
+		dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n");
+		return -EINVAL;
+	}
+	if (count > USB_MAX_IOREAD16_COUNT) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "error: count %u exceeds possible max %u\n",
+			 count, USB_MAX_IOREAD16_COUNT);
+		return -EINVAL;
+	}
+	if (in_atomic()) {
+		dev_dbg_f(zd_usb_dev(usb),
+			 "error: io in atomic context not supported\n");
+		return -EWOULDBLOCK;
+	}
+	if (!usb_int_enabled(usb)) {
+		 dev_dbg_f(zd_usb_dev(usb),
+			  "error: usb interrupt not enabled\n");
+		return -EWOULDBLOCK;
+	}
+
+	req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16);
+	req = kmalloc(req_len, GFP_NOFS);
+	if (!req)
+		return -ENOMEM;
+	req->id = cpu_to_le16(USB_REQ_READ_REGS);
+	for (i = 0; i < count; i++)
+		req->addr[i] = cpu_to_le16(usb_addr(usb, addresses[i]));
+
+	udev = zd_usb_to_usbdev(usb);
+	prepare_read_regs_int(usb);
+	r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
+		         req, req_len, &actual_req_len, 1000 /* ms */);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error in usb_bulk_msg(). Error number %d\n", r);
+		goto error;
+	}
+	if (req_len != actual_req_len) {
+		dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()\n"
+			" req_len %d != actual_req_len %d\n",
+			req_len, actual_req_len);
+		r = -EIO;
+		goto error;
+	}
+
+	timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion,
+	                                      msecs_to_jiffies(1000));
+	if (!timeout) {
+		disable_read_regs_int(usb);
+		dev_dbg_f(zd_usb_dev(usb), "read timed out\n");
+		r = -ETIMEDOUT;
+		goto error;
+	}
+
+	r = get_results(usb, values, req, count);
+error:
+	kfree(req);
+	return r;
+}
+
+int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
+	              unsigned int count)
+{
+	int r;
+	struct usb_device *udev;
+	struct usb_req_write_regs *req = NULL;
+	int i, req_len, actual_req_len;
+
+	if (count == 0)
+		return 0;
+	if (count > USB_MAX_IOWRITE16_COUNT) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: count %u exceeds possible max %u\n",
+			count, USB_MAX_IOWRITE16_COUNT);
+		return -EINVAL;
+	}
+	if (in_atomic()) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: io in atomic context not supported\n");
+		return -EWOULDBLOCK;
+	}
+
+	req_len = sizeof(struct usb_req_write_regs) +
+		  count * sizeof(struct reg_data);
+	req = kmalloc(req_len, GFP_NOFS);
+	if (!req)
+		return -ENOMEM;
+
+	req->id = cpu_to_le16(USB_REQ_WRITE_REGS);
+	for (i = 0; i < count; i++) {
+		struct reg_data *rw  = &req->reg_writes[i];
+		rw->addr = cpu_to_le16(usb_addr(usb, ioreqs[i].addr));
+		rw->value = cpu_to_le16(ioreqs[i].value);
+	}
+
+	udev = zd_usb_to_usbdev(usb);
+	r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
+		         req, req_len, &actual_req_len, 1000 /* ms */);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error in usb_bulk_msg(). Error number %d\n", r);
+		goto error;
+	}
+	if (req_len != actual_req_len) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error in usb_bulk_msg()"
+			" req_len %d != actual_req_len %d\n",
+			req_len, actual_req_len);
+		r = -EIO;
+		goto error;
+	}
+
+	/* FALL-THROUGH with r == 0 */
+error:
+	kfree(req);
+	return r;
+}
+
+int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
+{
+	int r;
+	struct usb_device *udev;
+	struct usb_req_rfwrite *req = NULL;
+	int i, req_len, actual_req_len;
+	u16 bit_value_template;
+
+	if (in_atomic()) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: io in atomic context not supported\n");
+		return -EWOULDBLOCK;
+	}
+	if (bits < USB_MIN_RFWRITE_BIT_COUNT) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: bits %d are smaller than"
+			" USB_MIN_RFWRITE_BIT_COUNT %d\n",
+			bits, USB_MIN_RFWRITE_BIT_COUNT);
+		return -EINVAL;
+	}
+	if (bits > USB_MAX_RFWRITE_BIT_COUNT) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n",
+			bits, USB_MAX_RFWRITE_BIT_COUNT);
+		return -EINVAL;
+	}
+#ifdef DEBUG
+	if (value & (~0UL << bits)) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error: value %#09x has bits >= %d set\n",
+			value, bits);
+		return -EINVAL;
+	}
+#endif /* DEBUG */
+
+	dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits);
+
+	r = zd_usb_ioread16(usb, &bit_value_template, CR203);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error %d: Couldn't read CR203\n", r);
+		goto out;
+	}
+	bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA);
+
+	req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16);
+	req = kmalloc(req_len, GFP_NOFS);
+	if (!req)
+		return -ENOMEM;
+
+	req->id = cpu_to_le16(USB_REQ_WRITE_RF);
+	/* 1: 3683a, but not used in ZYDAS driver */
+	req->value = cpu_to_le16(2);
+	req->bits = cpu_to_le16(bits);
+
+	for (i = 0; i < bits; i++) {
+		u16 bv = bit_value_template;
+		if (value & (1 << (bits-1-i)))
+			bv |= RF_DATA;
+		req->bit_values[i] = cpu_to_le16(bv);
+	}
+
+	udev = zd_usb_to_usbdev(usb);
+	r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
+		         req, req_len, &actual_req_len, 1000 /* ms */);
+	if (r) {
+		dev_dbg_f(zd_usb_dev(usb),
+			"error in usb_bulk_msg(). Error number %d\n", r);
+		goto out;
+	}
+	if (req_len != actual_req_len) {
+		dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()"
+			" req_len %d != actual_req_len %d\n",
+			req_len, actual_req_len);
+		r = -EIO;
+		goto out;
+	}
+
+	/* FALL-THROUGH with r == 0 */
+out:
+	kfree(req);
+	return r;
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
new file mode 100644
index 0000000..d642028
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -0,0 +1,240 @@
+/* zd_usb.h: Header for USB interface implemented by ZD1211 chip
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_USB_H
+#define _ZD_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "zd_def.h"
+#include "zd_types.h"
+
+enum devicetype {
+	DEVICE_ZD1211  = 0,
+	DEVICE_ZD1211B = 1,
+};
+
+enum endpoints {
+	EP_CTRL	    = 0,
+	EP_DATA_OUT = 1,
+	EP_DATA_IN  = 2,
+	EP_INT_IN   = 3,
+	EP_REGS_OUT = 4,
+};
+
+enum {
+	USB_MAX_TRANSFER_SIZE		= 4096, /* bytes */
+	/* FIXME: The original driver uses this value. We have to check,
+	 * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be
+	 * used if one combined frame is split over two USB transactions.
+	 */
+	USB_MAX_RX_SIZE			= 4800, /* bytes */
+	USB_MAX_IOWRITE16_COUNT		= 15,
+	USB_MAX_IOWRITE32_COUNT		= USB_MAX_IOWRITE16_COUNT/2,
+	USB_MAX_IOREAD16_COUNT		= 15,
+	USB_MAX_IOREAD32_COUNT		= USB_MAX_IOREAD16_COUNT/2,
+	USB_MIN_RFWRITE_BIT_COUNT	= 16,
+	USB_MAX_RFWRITE_BIT_COUNT	= 28,
+	USB_MAX_EP_INT_BUFFER		= 64,
+	USB_ZD1211B_BCD_DEVICE		= 0x4810,
+};
+
+enum control_requests {
+	USB_REQ_WRITE_REGS		= 0x21,
+	USB_REQ_READ_REGS		= 0x22,
+	USB_REQ_WRITE_RF		= 0x23,
+	USB_REQ_PROG_FLASH		= 0x24,
+	USB_REQ_EEPROM_START		= 0x0128, /* ? request is a byte */
+	USB_REQ_EEPROM_MID		= 0x28,
+	USB_REQ_EEPROM_END		= 0x0228, /* ? request is a byte */
+	USB_REQ_FIRMWARE_DOWNLOAD	= 0x30,
+	USB_REQ_FIRMWARE_CONFIRM	= 0x31,
+	USB_REQ_FIRMWARE_READ_DATA	= 0x32,
+};
+
+struct usb_req_read_regs {
+	__le16 id;
+	__le16 addr[0];
+} __attribute__((packed));
+
+struct reg_data {
+	__le16 addr;
+	__le16 value;
+} __attribute__((packed));
+
+struct usb_req_write_regs {
+	__le16 id;
+	struct reg_data reg_writes[0];
+} __attribute__((packed));
+
+enum {
+	RF_IF_LE = 0x02,
+	RF_CLK   = 0x04,
+	RF_DATA	 = 0x08,
+};
+
+struct usb_req_rfwrite {
+	__le16 id;
+	__le16 value;
+	/* 1: 3683a */
+	/* 2: other (default) */
+	__le16 bits;
+	/* RF2595: 24 */
+	__le16 bit_values[0];
+	/* (CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */
+} __attribute__((packed));
+
+/* USB interrupt */
+
+enum usb_int_id {
+	USB_INT_TYPE			= 0x01,
+	USB_INT_ID_REGS			= 0x90,
+	USB_INT_ID_RETRY_FAILED		= 0xa0,
+};
+
+enum usb_int_flags {
+	USB_INT_READ_REGS_EN		= 0x01,
+};
+
+struct usb_int_header {
+	u8 type;	/* must always be 1 */
+	u8 id;
+} __attribute__((packed));
+
+struct usb_int_regs {
+	struct usb_int_header hdr;
+	struct reg_data regs[0];
+} __attribute__((packed));
+
+struct usb_int_retry_fail {
+	struct usb_int_header hdr;
+	u8 new_rate;
+	u8 _dummy;
+	u8 addr[ETH_ALEN];
+	u8 ibss_wakeup_dest;
+} __attribute__((packed));
+
+struct read_regs_int {
+	struct completion completion;
+	/* Stores the USB int structure and contains the USB address of the
+	 * first requested register before request.
+	 */
+	u8 buffer[USB_MAX_EP_INT_BUFFER];
+	int length;
+	__le16 cr_int_addr;
+};
+
+struct zd_ioreq16 {
+	zd_addr_t addr;
+	u16 value;
+};
+
+struct zd_ioreq32 {
+	zd_addr_t addr;
+	u32 value;
+};
+
+struct zd_usb_interrupt {
+	struct read_regs_int read_regs;
+	spinlock_t lock;
+	struct urb *urb;
+	int interval;
+	u8 read_regs_enabled:1;
+};
+
+static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr)
+{
+	return (struct usb_int_regs *)intr->read_regs.buffer;
+}
+
+#define URBS_COUNT 5
+
+struct zd_usb_rx {
+	spinlock_t lock;
+	u8 fragment[2*USB_MAX_RX_SIZE];
+	unsigned int fragment_length;
+	unsigned int usb_packet_size;
+	struct urb **urbs;
+	int urbs_count;
+};
+
+struct zd_usb_tx {
+	spinlock_t lock;
+};
+
+/* Contains the usb parts. The structure doesn't require a lock, because intf
+ * and fw_base_offset, will not be changed after initialization.
+ */
+struct zd_usb {
+	struct zd_usb_interrupt intr;
+	struct zd_usb_rx rx;
+	struct zd_usb_tx tx;
+	struct usb_interface *intf;
+	u16 fw_base_offset;
+};
+
+#define zd_usb_dev(usb) (&usb->intf->dev)
+
+static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb)
+{
+	return interface_to_usbdev(usb->intf);
+}
+
+static inline struct net_device *zd_intf_to_netdev(struct usb_interface *intf)
+{
+	return usb_get_intfdata(intf);
+}
+
+static inline struct net_device *zd_usb_to_netdev(struct zd_usb *usb)
+{
+	return zd_intf_to_netdev(usb->intf);
+}
+
+void zd_usb_init(struct zd_usb *usb, struct net_device *netdev,
+	         struct usb_interface *intf);
+int zd_usb_init_hw(struct zd_usb *usb);
+void zd_usb_clear(struct zd_usb *usb);
+
+int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size);
+
+int zd_usb_enable_int(struct zd_usb *usb);
+void zd_usb_disable_int(struct zd_usb *usb);
+
+int zd_usb_enable_rx(struct zd_usb *usb);
+void zd_usb_disable_rx(struct zd_usb *usb);
+
+int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length);
+
+int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
+	         const zd_addr_t *addresses, unsigned int count);
+
+static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value,
+	                      const zd_addr_t addr)
+{
+	return zd_usb_ioread16v(usb, value, (const zd_addr_t *)&addr, 1);
+}
+
+int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
+	              unsigned int count);
+
+int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits);
+
+#endif /* _ZD_USB_H */
diff --git a/drivers/net/wireless/zd1211rw/zd_util.c b/drivers/net/wireless/zd1211rw/zd_util.c
new file mode 100644
index 0000000..d20036c
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_util.c
@@ -0,0 +1,82 @@
+/* zd_util.c
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Utility program
+ */
+
+#include "zd_def.h"
+#include "zd_util.h"
+
+#ifdef DEBUG
+static char hex(u8 v)
+{
+	v &= 0xf;
+	return (v < 10 ? '0' : 'a' - 10) + v;
+}
+
+static char hex_print(u8 c)
+{
+	return (0x20 <= c && c < 0x7f) ? c : '.';
+}
+
+static void dump_line(const u8 *bytes, size_t size)
+{
+	char c;
+	size_t i;
+
+	size = size <= 8 ? size : 8;
+	printk(KERN_DEBUG "zd1211 %p ", bytes);
+	for (i = 0; i < 8; i++) {
+		switch (i) {
+		case 1:
+		case 5:
+			c = '.';
+			break;
+		case 3:
+			c = ':';
+			break;
+		default:
+			c = ' ';
+		}
+		if (i < size) {
+			printk("%c%c%c", hex(bytes[i] >> 4), hex(bytes[i]), c);
+		} else {
+			printk("  %c", c);
+		}
+	}
+
+	for (i = 0; i < size; i++)
+		printk("%c", hex_print(bytes[i]));
+	printk("\n");
+}
+
+void zd_hexdump(const void *bytes, size_t size)
+{
+	size_t i = 0;
+
+	do {
+		dump_line((u8 *)bytes + i, size-i);
+		i += 8;
+	} while (i < size);
+}
+#endif /* DEBUG */
+
+void *zd_tail(const void *buffer, size_t buffer_size, size_t tail_size)
+{
+	if (buffer_size < tail_size)
+		return NULL;
+	return (u8 *)buffer + (buffer_size - tail_size);
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_util.h b/drivers/net/wireless/zd1211rw/zd_util.h
new file mode 100644
index 0000000..ce26f7a
--- /dev/null
+++ b/drivers/net/wireless/zd1211rw/zd_util.h
@@ -0,0 +1,29 @@
+/* zd_util.h
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ZD_UTIL_H
+#define _ZD_UTIL_H
+
+void *zd_tail(const void *buffer, size_t buffer_size, size_t tail_size);
+
+#ifdef DEBUG
+void zd_hexdump(const void *bytes, size_t size);
+#else
+#define zd_hexdump(bytes, size)
+#endif /* DEBUG */
+
+#endif /* _ZD_UTIL_H */
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index bbbf7e2..8459a182 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -19,37 +19,13 @@
 
 	Support and updates available at
 	http://www.scyld.com/network/yellowfin.html
+	[link no longer provides useful info -jgarzik]
 
-
-	Linux kernel changelog:
-	-----------------------
-
-	LK1.1.1 (jgarzik): Port to 2.4 kernel
-
-	LK1.1.2 (jgarzik):
-	* Merge in becker version 1.05
-
-	LK1.1.3 (jgarzik):
-	* Various cleanups
-	* Update yellowfin_timer to correctly calculate duplex.
-	(suggested by Manfred Spraul)
-
-	LK1.1.4 (val@nmt.edu):
-	* Fix three endian-ness bugs
-	* Support dual function SYM53C885E ethernet chip
-	
-	LK1.1.5 (val@nmt.edu):
-	* Fix forced full-duplex bug I introduced
-
-	LK1.1.6 (val@nmt.edu):
-	* Only print warning on truly "oversized" packets
-	* Fix theoretical bug on gigabit cards - return to 1.1.3 behavior
-	
 */
 
 #define DRV_NAME	"yellowfin"
-#define DRV_VERSION	"1.05+LK1.1.6"
-#define DRV_RELDATE	"Feb 11, 2002"
+#define DRV_VERSION	"2.0"
+#define DRV_RELDATE	"Jun 27, 2006"
 
 #define PFX DRV_NAME ": "
 
@@ -239,8 +215,11 @@
 	HasMACAddrBug=32, /* Only on early revs.  */
 	DontUseEeprom=64, /* Don't read the MAC from the EEPROm. */
 };
+
 /* The PCI I/O space extent. */
-#define YELLOWFIN_SIZE 0x100
+enum {
+	YELLOWFIN_SIZE	= 0x100,
+};
 
 struct pci_id_info {
         const char *name;
@@ -248,16 +227,14 @@
                 int     pci, pci_mask, subsystem, subsystem_mask;
                 int revision, revision_mask;                            /* Only 8 bits. */
         } id;
-        int io_size;                            /* Needed for I/O region check or ioremap(). */
         int drv_flags;                          /* Driver use, intended as capability flags. */
 };
 
 static const struct pci_id_info pci_id_tbl[] = {
 	{"Yellowfin G-NIC Gigabit Ethernet", { 0x07021000, 0xffffffff},
-	 YELLOWFIN_SIZE,
 	 FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug | DontUseEeprom},
 	{"Symbios SYM83C885", { 0x07011000, 0xffffffff},
-	 YELLOWFIN_SIZE, HasMII | DontUseEeprom },
+	  HasMII | DontUseEeprom },
 	{ }
 };
 
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 15f6cd4..77e7202a 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -1052,7 +1052,7 @@
 
 static void ahci_error_handler(struct ata_port *ap)
 {
-	if (!(ap->flags & ATA_FLAG_FROZEN)) {
+	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
 		/* restart engine */
 		ahci_stop_engine(ap);
 		ahci_start_engine(ap);
@@ -1323,6 +1323,17 @@
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
+	/* JMicron-specific fixup: make sure we're in AHCI mode */
+	/* This is protected from races with ata_jmicron by the pci probe
+	   locking */
+	if (pdev->vendor == PCI_VENDOR_ID_JMICRON) {
+		/* AHCI enable, AHCI on function 0 */
+		pci_write_config_byte(pdev, 0x41, 0xa1);
+		/* Function 1 is the PATA controller */
+		if (PCI_FUNC(pdev->devfn))
+			return -ENODEV;
+	}
+
 	rc = pci_enable_device(pdev);
 	if (rc)
 		return rc;
@@ -1378,10 +1389,6 @@
 	if (have_msi)
 		hpriv->flags |= AHCI_FLAG_MSI;
 
-	/* JMicron-specific fixup: make sure we're in AHCI mode */
-	if (pdev->vendor == 0x197b)
-		pci_write_config_byte(pdev, 0x41, 0xa1);
-
 	/* initialize adapter */
 	rc = ahci_host_init(probe_ent);
 	if (rc)
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 1c960ac..386e5f2 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -61,9 +61,9 @@
 #include "libata.h"
 
 /* debounce timing parameters in msecs { interval, duration, timeout } */
-const unsigned long sata_deb_timing_boot[]		= {   5,  100, 2000 };
-const unsigned long sata_deb_timing_eh[]		= {  25,  500, 2000 };
-const unsigned long sata_deb_timing_before_fsrst[]	= { 100, 2000, 5000 };
+const unsigned long sata_deb_timing_normal[]		= {   5,  100, 2000 };
+const unsigned long sata_deb_timing_hotplug[]		= {  25,  500, 2000 };
+const unsigned long sata_deb_timing_long[]		= { 100, 2000, 5000 };
 
 static unsigned int ata_dev_init_params(struct ata_device *dev,
 					u16 heads, u16 sectors);
@@ -907,7 +907,7 @@
 {
 	int rc;
 
-	if (ap->flags & ATA_FLAG_FLUSH_PORT_TASK)
+	if (ap->pflags & ATA_PFLAG_FLUSH_PORT_TASK)
 		return;
 
 	PREPARE_WORK(&ap->port_task, fn, data);
@@ -938,7 +938,7 @@
 	DPRINTK("ENTER\n");
 
 	spin_lock_irqsave(ap->lock, flags);
-	ap->flags |= ATA_FLAG_FLUSH_PORT_TASK;
+	ap->pflags |= ATA_PFLAG_FLUSH_PORT_TASK;
 	spin_unlock_irqrestore(ap->lock, flags);
 
 	DPRINTK("flush #1\n");
@@ -957,7 +957,7 @@
 	}
 
 	spin_lock_irqsave(ap->lock, flags);
-	ap->flags &= ~ATA_FLAG_FLUSH_PORT_TASK;
+	ap->pflags &= ~ATA_PFLAG_FLUSH_PORT_TASK;
 	spin_unlock_irqrestore(ap->lock, flags);
 
 	if (ata_msg_ctl(ap))
@@ -1009,7 +1009,7 @@
 	spin_lock_irqsave(ap->lock, flags);
 
 	/* no internal command while frozen */
-	if (ap->flags & ATA_FLAG_FROZEN) {
+	if (ap->pflags & ATA_PFLAG_FROZEN) {
 		spin_unlock_irqrestore(ap->lock, flags);
 		return AC_ERR_SYSTEM;
 	}
@@ -1325,6 +1325,19 @@
 		snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth);
 }
 
+static void ata_set_port_max_cmd_len(struct ata_port *ap)
+{
+	int i;
+
+	if (ap->host) {
+		ap->host->max_cmd_len = 0;
+		for (i = 0; i < ATA_MAX_DEVICES; i++)
+			ap->host->max_cmd_len = max_t(unsigned int,
+						      ap->host->max_cmd_len,
+						      ap->device[i].cdb_len);
+	}
+}
+
 /**
  *	ata_dev_configure - Configure the specified ATA/ATAPI device
  *	@dev: Target device to configure
@@ -1344,7 +1357,7 @@
 	struct ata_port *ap = dev->ap;
 	const u16 *id = dev->id;
 	unsigned int xfer_mask;
-	int i, rc;
+	int rc;
 
 	if (!ata_dev_enabled(dev) && ata_msg_info(ap)) {
 		ata_dev_printk(dev, KERN_INFO,
@@ -1404,7 +1417,7 @@
 			ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc));
 
 			/* print device info to dmesg */
-			if (ata_msg_info(ap))
+			if (ata_msg_drv(ap) && print_info)
 				ata_dev_printk(dev, KERN_INFO, "ATA-%d, "
 					"max %s, %Lu sectors: %s %s\n",
 					ata_id_major_version(id),
@@ -1427,7 +1440,7 @@
 			}
 
 			/* print device info to dmesg */
-			if (ata_msg_info(ap))
+			if (ata_msg_drv(ap) && print_info)
 				ata_dev_printk(dev, KERN_INFO, "ATA-%d, "
 					"max %s, %Lu sectors: CHS %u/%u/%u\n",
 					ata_id_major_version(id),
@@ -1439,7 +1452,7 @@
 
 		if (dev->id[59] & 0x100) {
 			dev->multi_count = dev->id[59] & 0xff;
-			if (ata_msg_info(ap))
+			if (ata_msg_drv(ap) && print_info)
 				ata_dev_printk(dev, KERN_INFO,
 					"ata%u: dev %u multi count %u\n",
 					ap->id, dev->devno, dev->multi_count);
@@ -1468,21 +1481,17 @@
 		}
 
 		/* print device info to dmesg */
-		if (ata_msg_info(ap))
+		if (ata_msg_drv(ap) && print_info)
 			ata_dev_printk(dev, KERN_INFO, "ATAPI, max %s%s\n",
 				       ata_mode_string(xfer_mask),
 				       cdb_intr_string);
 	}
 
-	ap->host->max_cmd_len = 0;
-	for (i = 0; i < ATA_MAX_DEVICES; i++)
-		ap->host->max_cmd_len = max_t(unsigned int,
-					      ap->host->max_cmd_len,
-					      ap->device[i].cdb_len);
+	ata_set_port_max_cmd_len(ap);
 
 	/* limit bridge transfers to udma5, 200 sectors */
 	if (ata_dev_knobble(dev)) {
-		if (ata_msg_info(ap))
+		if (ata_msg_drv(ap) && print_info)
 			ata_dev_printk(dev, KERN_INFO,
 				       "applying bridge limits\n");
 		dev->udma_mask &= ATA_UDMA5;
@@ -2137,7 +2146,7 @@
 		 * return error code and failing device on failure.
 		 */
 		for (i = 0; i < ATA_MAX_DEVICES; i++) {
-			if (ata_dev_enabled(&ap->device[i])) {
+			if (ata_dev_ready(&ap->device[i])) {
 				ap->ops->set_mode(ap);
 				break;
 			}
@@ -2203,7 +2212,8 @@
 	for (i = 0; i < ATA_MAX_DEVICES; i++) {
 		dev = &ap->device[i];
 
-		if (!ata_dev_enabled(dev))
+		/* don't udpate suspended devices' xfer mode */
+		if (!ata_dev_ready(dev))
 			continue;
 
 		rc = ata_dev_set_mode(dev);
@@ -2579,7 +2589,7 @@
 
 	/* first, debounce phy if SATA */
 	if (ap->cbl == ATA_CBL_SATA) {
-		rc = sata_phy_debounce(ap, sata_deb_timing_eh);
+		rc = sata_phy_debounce(ap, sata_deb_timing_hotplug);
 
 		/* if debounced successfully and offline, no need to wait */
 		if ((rc == 0 || rc == -EOPNOTSUPP) && ata_port_offline(ap))
@@ -2615,16 +2625,17 @@
 int ata_std_prereset(struct ata_port *ap)
 {
 	struct ata_eh_context *ehc = &ap->eh_context;
-	const unsigned long *timing;
+	const unsigned long *timing = sata_ehc_deb_timing(ehc);
 	int rc;
 
-	/* hotplug? */
-	if (ehc->i.flags & ATA_EHI_HOTPLUGGED) {
-		if (ap->flags & ATA_FLAG_HRST_TO_RESUME)
-			ehc->i.action |= ATA_EH_HARDRESET;
-		if (ap->flags & ATA_FLAG_SKIP_D2H_BSY)
-			ata_wait_spinup(ap);
-	}
+	/* handle link resume & hotplug spinup */
+	if ((ehc->i.flags & ATA_EHI_RESUME_LINK) &&
+	    (ap->flags & ATA_FLAG_HRST_TO_RESUME))
+		ehc->i.action |= ATA_EH_HARDRESET;
+
+	if ((ehc->i.flags & ATA_EHI_HOTPLUGGED) &&
+	    (ap->flags & ATA_FLAG_SKIP_D2H_BSY))
+		ata_wait_spinup(ap);
 
 	/* if we're about to do hardreset, nothing more to do */
 	if (ehc->i.action & ATA_EH_HARDRESET)
@@ -2632,11 +2643,6 @@
 
 	/* if SATA, resume phy */
 	if (ap->cbl == ATA_CBL_SATA) {
-		if (ap->flags & ATA_FLAG_LOADING)
-			timing = sata_deb_timing_boot;
-		else
-			timing = sata_deb_timing_eh;
-
 		rc = sata_phy_resume(ap, timing);
 		if (rc && rc != -EOPNOTSUPP) {
 			/* phy resume failed */
@@ -2724,6 +2730,8 @@
  */
 int sata_std_hardreset(struct ata_port *ap, unsigned int *class)
 {
+	struct ata_eh_context *ehc = &ap->eh_context;
+	const unsigned long *timing = sata_ehc_deb_timing(ehc);
 	u32 scontrol;
 	int rc;
 
@@ -2761,7 +2769,7 @@
 	msleep(1);
 
 	/* bring phy back */
-	sata_phy_resume(ap, sata_deb_timing_eh);
+	sata_phy_resume(ap, timing);
 
 	/* TODO: phy layer with polling, timeouts, etc. */
 	if (ata_port_offline(ap)) {
@@ -4285,7 +4293,7 @@
 	unsigned int i;
 
 	/* no command while frozen */
-	if (unlikely(ap->flags & ATA_FLAG_FROZEN))
+	if (unlikely(ap->pflags & ATA_PFLAG_FROZEN))
 		return NULL;
 
 	/* the last tag is reserved for internal command. */
@@ -4407,7 +4415,7 @@
 	 * taken care of.
 	 */
 	if (ap->ops->error_handler) {
-		WARN_ON(ap->flags & ATA_FLAG_FROZEN);
+		WARN_ON(ap->pflags & ATA_PFLAG_FROZEN);
 
 		if (unlikely(qc->err_mask))
 			qc->flags |= ATA_QCFLAG_FAILED;
@@ -5001,86 +5009,120 @@
 	return 0;
 }
 
-static int ata_standby_drive(struct ata_device *dev)
+static int ata_host_set_request_pm(struct ata_host_set *host_set,
+				   pm_message_t mesg, unsigned int action,
+				   unsigned int ehi_flags, int wait)
 {
-	unsigned int err_mask;
+	unsigned long flags;
+	int i, rc;
 
-	err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1);
-	if (err_mask) {
-		ata_dev_printk(dev, KERN_ERR, "failed to standby drive "
-			       "(err_mask=0x%x)\n", err_mask);
-		return -EIO;
-	}
+	for (i = 0; i < host_set->n_ports; i++) {
+		struct ata_port *ap = host_set->ports[i];
 
-	return 0;
-}
+		/* Previous resume operation might still be in
+		 * progress.  Wait for PM_PENDING to clear.
+		 */
+		if (ap->pflags & ATA_PFLAG_PM_PENDING) {
+			ata_port_wait_eh(ap);
+			WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
+		}
 
-static int ata_start_drive(struct ata_device *dev)
-{
-	unsigned int err_mask;
+		/* request PM ops to EH */
+		spin_lock_irqsave(ap->lock, flags);
 
-	err_mask = ata_do_simple_cmd(dev, ATA_CMD_IDLEIMMEDIATE);
-	if (err_mask) {
-		ata_dev_printk(dev, KERN_ERR, "failed to start drive "
-			       "(err_mask=0x%x)\n", err_mask);
-		return -EIO;
+		ap->pm_mesg = mesg;
+		if (wait) {
+			rc = 0;
+			ap->pm_result = &rc;
+		}
+
+		ap->pflags |= ATA_PFLAG_PM_PENDING;
+		ap->eh_info.action |= action;
+		ap->eh_info.flags |= ehi_flags;
+
+		ata_port_schedule_eh(ap);
+
+		spin_unlock_irqrestore(ap->lock, flags);
+
+		/* wait and check result */
+		if (wait) {
+			ata_port_wait_eh(ap);
+			WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
+			if (rc)
+				return rc;
+		}
 	}
 
 	return 0;
 }
 
 /**
- *	ata_device_resume - wakeup a previously suspended devices
- *	@dev: the device to resume
+ *	ata_host_set_suspend - suspend host_set
+ *	@host_set: host_set to suspend
+ *	@mesg: PM message
  *
- *	Kick the drive back into action, by sending it an idle immediate
- *	command and making sure its transfer mode matches between drive
- *	and host.
+ *	Suspend @host_set.  Actual operation is performed by EH.  This
+ *	function requests EH to perform PM operations and waits for EH
+ *	to finish.
  *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno on failure.
  */
-int ata_device_resume(struct ata_device *dev)
+int ata_host_set_suspend(struct ata_host_set *host_set, pm_message_t mesg)
 {
-	struct ata_port *ap = dev->ap;
+	int i, j, rc;
 
-	if (ap->flags & ATA_FLAG_SUSPENDED) {
-		struct ata_device *failed_dev;
+	rc = ata_host_set_request_pm(host_set, mesg, 0, ATA_EHI_QUIET, 1);
+	if (rc)
+		goto fail;
 
-		ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
-		ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 200000);
+	/* EH is quiescent now.  Fail if we have any ready device.
+	 * This happens if hotplug occurs between completion of device
+	 * suspension and here.
+	 */
+	for (i = 0; i < host_set->n_ports; i++) {
+		struct ata_port *ap = host_set->ports[i];
 
-		ap->flags &= ~ATA_FLAG_SUSPENDED;
-		while (ata_set_mode(ap, &failed_dev))
-			ata_dev_disable(failed_dev);
+		for (j = 0; j < ATA_MAX_DEVICES; j++) {
+			struct ata_device *dev = &ap->device[j];
+
+			if (ata_dev_ready(dev)) {
+				ata_port_printk(ap, KERN_WARNING,
+						"suspend failed, device %d "
+						"still active\n", dev->devno);
+				rc = -EBUSY;
+				goto fail;
+			}
+		}
 	}
-	if (!ata_dev_enabled(dev))
-		return 0;
-	if (dev->class == ATA_DEV_ATA)
-		ata_start_drive(dev);
 
+	host_set->dev->power.power_state = mesg;
 	return 0;
+
+ fail:
+	ata_host_set_resume(host_set);
+	return rc;
 }
 
 /**
- *	ata_device_suspend - prepare a device for suspend
- *	@dev: the device to suspend
- *	@state: target power management state
+ *	ata_host_set_resume - resume host_set
+ *	@host_set: host_set to resume
  *
- *	Flush the cache on the drive, if appropriate, then issue a
- *	standbynow command.
+ *	Resume @host_set.  Actual operation is performed by EH.  This
+ *	function requests EH to perform PM operations and returns.
+ *	Note that all resume operations are performed parallely.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
  */
-int ata_device_suspend(struct ata_device *dev, pm_message_t state)
+void ata_host_set_resume(struct ata_host_set *host_set)
 {
-	struct ata_port *ap = dev->ap;
-
-	if (!ata_dev_enabled(dev))
-		return 0;
-	if (dev->class == ATA_DEV_ATA)
-		ata_flush_cache(dev);
-
-	if (state.event != PM_EVENT_FREEZE)
-		ata_standby_drive(dev);
-	ap->flags |= ATA_FLAG_SUSPENDED;
-	return 0;
+	ata_host_set_request_pm(host_set, PMSG_ON, ATA_EH_SOFTRESET,
+				ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
+	host_set->dev->power.power_state = PMSG_ON;
 }
 
 /**
@@ -5440,6 +5482,7 @@
 		}
 
 		if (ap->ops->error_handler) {
+			struct ata_eh_info *ehi = &ap->eh_info;
 			unsigned long flags;
 
 			ata_port_probe(ap);
@@ -5447,10 +5490,11 @@
 			/* kick EH for boot probing */
 			spin_lock_irqsave(ap->lock, flags);
 
-			ap->eh_info.probe_mask = (1 << ATA_MAX_DEVICES) - 1;
-			ap->eh_info.action |= ATA_EH_SOFTRESET;
+			ehi->probe_mask = (1 << ATA_MAX_DEVICES) - 1;
+			ehi->action |= ATA_EH_SOFTRESET;
+			ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
 
-			ap->flags |= ATA_FLAG_LOADING;
+			ap->pflags |= ATA_PFLAG_LOADING;
 			ata_port_schedule_eh(ap);
 
 			spin_unlock_irqrestore(ap->lock, flags);
@@ -5518,7 +5562,7 @@
 
 	/* tell EH we're leaving & flush EH */
 	spin_lock_irqsave(ap->lock, flags);
-	ap->flags |= ATA_FLAG_UNLOADING;
+	ap->pflags |= ATA_PFLAG_UNLOADING;
 	spin_unlock_irqrestore(ap->lock, flags);
 
 	ata_port_wait_eh(ap);
@@ -5723,20 +5767,55 @@
 	return (tmp == bits->val) ? 1 : 0;
 }
 
-int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state)
 {
 	pci_save_state(pdev);
-	pci_disable_device(pdev);
-	pci_set_power_state(pdev, PCI_D3hot);
-	return 0;
+
+	if (state.event == PM_EVENT_SUSPEND) {
+		pci_disable_device(pdev);
+		pci_set_power_state(pdev, PCI_D3hot);
+	}
 }
 
-int ata_pci_device_resume(struct pci_dev *pdev)
+void ata_pci_device_do_resume(struct pci_dev *pdev)
 {
 	pci_set_power_state(pdev, PCI_D0);
 	pci_restore_state(pdev);
 	pci_enable_device(pdev);
 	pci_set_master(pdev);
+}
+
+int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+	int rc = 0;
+
+	rc = ata_host_set_suspend(host_set, state);
+	if (rc)
+		return rc;
+
+	if (host_set->next) {
+		rc = ata_host_set_suspend(host_set->next, state);
+		if (rc) {
+			ata_host_set_resume(host_set);
+			return rc;
+		}
+	}
+
+	ata_pci_device_do_suspend(pdev, state);
+
+	return 0;
+}
+
+int ata_pci_device_resume(struct pci_dev *pdev)
+{
+	struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+
+	ata_pci_device_do_resume(pdev);
+	ata_host_set_resume(host_set);
+	if (host_set->next)
+		ata_host_set_resume(host_set->next);
+
 	return 0;
 }
 #endif /* CONFIG_PCI */
@@ -5842,9 +5921,9 @@
  * Do not depend on ABI/API stability.
  */
 
-EXPORT_SYMBOL_GPL(sata_deb_timing_boot);
-EXPORT_SYMBOL_GPL(sata_deb_timing_eh);
-EXPORT_SYMBOL_GPL(sata_deb_timing_before_fsrst);
+EXPORT_SYMBOL_GPL(sata_deb_timing_normal);
+EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug);
+EXPORT_SYMBOL_GPL(sata_deb_timing_long);
 EXPORT_SYMBOL_GPL(ata_std_bios_param);
 EXPORT_SYMBOL_GPL(ata_std_ports);
 EXPORT_SYMBOL_GPL(ata_device_add);
@@ -5916,6 +5995,8 @@
 EXPORT_SYMBOL_GPL(sata_scr_write_flush);
 EXPORT_SYMBOL_GPL(ata_port_online);
 EXPORT_SYMBOL_GPL(ata_port_offline);
+EXPORT_SYMBOL_GPL(ata_host_set_suspend);
+EXPORT_SYMBOL_GPL(ata_host_set_resume);
 EXPORT_SYMBOL_GPL(ata_id_string);
 EXPORT_SYMBOL_GPL(ata_id_c_string);
 EXPORT_SYMBOL_GPL(ata_scsi_simulate);
@@ -5930,14 +6011,14 @@
 EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
 EXPORT_SYMBOL_GPL(ata_pci_init_one);
 EXPORT_SYMBOL_GPL(ata_pci_remove_one);
+EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend);
+EXPORT_SYMBOL_GPL(ata_pci_device_do_resume);
 EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
 EXPORT_SYMBOL_GPL(ata_pci_device_resume);
 EXPORT_SYMBOL_GPL(ata_pci_default_filter);
 EXPORT_SYMBOL_GPL(ata_pci_clear_simplex);
 #endif /* CONFIG_PCI */
 
-EXPORT_SYMBOL_GPL(ata_device_suspend);
-EXPORT_SYMBOL_GPL(ata_device_resume);
 EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
 EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
 
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index bf5a72a..4b6aa30 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -47,6 +47,8 @@
 
 static void __ata_port_freeze(struct ata_port *ap);
 static void ata_eh_finish(struct ata_port *ap);
+static void ata_eh_handle_port_suspend(struct ata_port *ap);
+static void ata_eh_handle_port_resume(struct ata_port *ap);
 
 static void ata_ering_record(struct ata_ering *ering, int is_io,
 			     unsigned int err_mask)
@@ -190,7 +192,6 @@
 void ata_scsi_error(struct Scsi_Host *host)
 {
 	struct ata_port *ap = ata_shost_to_port(host);
-	spinlock_t *ap_lock = ap->lock;
 	int i, repeat_cnt = ATA_EH_MAX_REPEAT;
 	unsigned long flags;
 
@@ -217,7 +218,7 @@
 		struct scsi_cmnd *scmd, *tmp;
 		int nr_timedout = 0;
 
-		spin_lock_irqsave(ap_lock, flags);
+		spin_lock_irqsave(ap->lock, flags);
 
 		list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) {
 			struct ata_queued_cmd *qc;
@@ -256,43 +257,49 @@
 		if (nr_timedout)
 			__ata_port_freeze(ap);
 
-		spin_unlock_irqrestore(ap_lock, flags);
+		spin_unlock_irqrestore(ap->lock, flags);
 	} else
-		spin_unlock_wait(ap_lock);
+		spin_unlock_wait(ap->lock);
 
  repeat:
 	/* invoke error handler */
 	if (ap->ops->error_handler) {
+		/* process port resume request */
+		ata_eh_handle_port_resume(ap);
+
 		/* fetch & clear EH info */
-		spin_lock_irqsave(ap_lock, flags);
+		spin_lock_irqsave(ap->lock, flags);
 
 		memset(&ap->eh_context, 0, sizeof(ap->eh_context));
 		ap->eh_context.i = ap->eh_info;
 		memset(&ap->eh_info, 0, sizeof(ap->eh_info));
 
-		ap->flags |= ATA_FLAG_EH_IN_PROGRESS;
-		ap->flags &= ~ATA_FLAG_EH_PENDING;
+		ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
+		ap->pflags &= ~ATA_PFLAG_EH_PENDING;
 
-		spin_unlock_irqrestore(ap_lock, flags);
+		spin_unlock_irqrestore(ap->lock, flags);
 
-		/* invoke EH.  if unloading, just finish failed qcs */
-		if (!(ap->flags & ATA_FLAG_UNLOADING))
+		/* invoke EH, skip if unloading or suspended */
+		if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
 			ap->ops->error_handler(ap);
 		else
 			ata_eh_finish(ap);
 
+		/* process port suspend request */
+		ata_eh_handle_port_suspend(ap);
+
 		/* Exception might have happend after ->error_handler
 		 * recovered the port but before this point.  Repeat
 		 * EH in such case.
 		 */
-		spin_lock_irqsave(ap_lock, flags);
+		spin_lock_irqsave(ap->lock, flags);
 
-		if (ap->flags & ATA_FLAG_EH_PENDING) {
+		if (ap->pflags & ATA_PFLAG_EH_PENDING) {
 			if (--repeat_cnt) {
 				ata_port_printk(ap, KERN_INFO,
 					"EH pending after completion, "
 					"repeating EH (cnt=%d)\n", repeat_cnt);
-				spin_unlock_irqrestore(ap_lock, flags);
+				spin_unlock_irqrestore(ap->lock, flags);
 				goto repeat;
 			}
 			ata_port_printk(ap, KERN_ERR, "EH pending after %d "
@@ -302,14 +309,14 @@
 		/* this run is complete, make sure EH info is clear */
 		memset(&ap->eh_info, 0, sizeof(ap->eh_info));
 
-		/* Clear host_eh_scheduled while holding ap_lock such
+		/* Clear host_eh_scheduled while holding ap->lock such
 		 * that if exception occurs after this point but
 		 * before EH completion, SCSI midlayer will
 		 * re-initiate EH.
 		 */
 		host->host_eh_scheduled = 0;
 
-		spin_unlock_irqrestore(ap_lock, flags);
+		spin_unlock_irqrestore(ap->lock, flags);
 	} else {
 		WARN_ON(ata_qc_from_tag(ap, ap->active_tag) == NULL);
 		ap->ops->eng_timeout(ap);
@@ -321,24 +328,23 @@
 	scsi_eh_flush_done_q(&ap->eh_done_q);
 
 	/* clean up */
-	spin_lock_irqsave(ap_lock, flags);
+	spin_lock_irqsave(ap->lock, flags);
 
-	if (ap->flags & ATA_FLAG_LOADING) {
-		ap->flags &= ~ATA_FLAG_LOADING;
-	} else {
-		if (ap->flags & ATA_FLAG_SCSI_HOTPLUG)
-			queue_work(ata_aux_wq, &ap->hotplug_task);
-		if (ap->flags & ATA_FLAG_RECOVERED)
-			ata_port_printk(ap, KERN_INFO, "EH complete\n");
-	}
+	if (ap->pflags & ATA_PFLAG_LOADING)
+		ap->pflags &= ~ATA_PFLAG_LOADING;
+	else if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG)
+		queue_work(ata_aux_wq, &ap->hotplug_task);
 
-	ap->flags &= ~(ATA_FLAG_SCSI_HOTPLUG | ATA_FLAG_RECOVERED);
+	if (ap->pflags & ATA_PFLAG_RECOVERED)
+		ata_port_printk(ap, KERN_INFO, "EH complete\n");
+
+	ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED);
 
 	/* tell wait_eh that we're done */
-	ap->flags &= ~ATA_FLAG_EH_IN_PROGRESS;
+	ap->pflags &= ~ATA_PFLAG_EH_IN_PROGRESS;
 	wake_up_all(&ap->eh_wait_q);
 
-	spin_unlock_irqrestore(ap_lock, flags);
+	spin_unlock_irqrestore(ap->lock, flags);
 
 	DPRINTK("EXIT\n");
 }
@@ -360,7 +366,7 @@
  retry:
 	spin_lock_irqsave(ap->lock, flags);
 
-	while (ap->flags & (ATA_FLAG_EH_PENDING | ATA_FLAG_EH_IN_PROGRESS)) {
+	while (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) {
 		prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE);
 		spin_unlock_irqrestore(ap->lock, flags);
 		schedule();
@@ -489,7 +495,7 @@
 	WARN_ON(!ap->ops->error_handler);
 
 	qc->flags |= ATA_QCFLAG_FAILED;
-	qc->ap->flags |= ATA_FLAG_EH_PENDING;
+	qc->ap->pflags |= ATA_PFLAG_EH_PENDING;
 
 	/* The following will fail if timeout has already expired.
 	 * ata_scsi_error() takes care of such scmds on EH entry.
@@ -513,7 +519,7 @@
 {
 	WARN_ON(!ap->ops->error_handler);
 
-	ap->flags |= ATA_FLAG_EH_PENDING;
+	ap->pflags |= ATA_PFLAG_EH_PENDING;
 	scsi_schedule_eh(ap->host);
 
 	DPRINTK("port EH scheduled\n");
@@ -578,7 +584,7 @@
 	if (ap->ops->freeze)
 		ap->ops->freeze(ap);
 
-	ap->flags |= ATA_FLAG_FROZEN;
+	ap->pflags |= ATA_PFLAG_FROZEN;
 
 	DPRINTK("ata%u port frozen\n", ap->id);
 }
@@ -646,7 +652,7 @@
 
 	spin_lock_irqsave(ap->lock, flags);
 
-	ap->flags &= ~ATA_FLAG_FROZEN;
+	ap->pflags &= ~ATA_PFLAG_FROZEN;
 
 	if (ap->ops->thaw)
 		ap->ops->thaw(ap);
@@ -731,7 +737,7 @@
 
 	if (ata_scsi_offline_dev(dev)) {
 		dev->flags |= ATA_DFLAG_DETACHED;
-		ap->flags |= ATA_FLAG_SCSI_HOTPLUG;
+		ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;
 	}
 
 	/* clear per-dev EH actions */
@@ -760,8 +766,12 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(ap->lock, flags);
+
 	ata_eh_clear_action(dev, &ap->eh_info, action);
-	ap->flags |= ATA_FLAG_RECOVERED;
+
+	if (!(ap->eh_context.i.flags & ATA_EHI_QUIET))
+		ap->pflags |= ATA_PFLAG_RECOVERED;
+
 	spin_unlock_irqrestore(ap->lock, flags);
 }
 
@@ -1027,7 +1037,7 @@
 	int tag, rc;
 
 	/* if frozen, we can't do much */
-	if (ap->flags & ATA_FLAG_FROZEN)
+	if (ap->pflags & ATA_PFLAG_FROZEN)
 		return;
 
 	/* is it NCQ device error? */
@@ -1275,6 +1285,9 @@
 
 	DPRINTK("ENTER\n");
 
+	if (ehc->i.flags & ATA_EHI_NO_AUTOPSY)
+		return;
+
 	/* obtain and analyze SError */
 	rc = sata_scr_read(ap, SCR_ERROR, &serror);
 	if (rc == 0) {
@@ -1327,7 +1340,7 @@
 	}
 
 	/* enforce default EH actions */
-	if (ap->flags & ATA_FLAG_FROZEN ||
+	if (ap->pflags & ATA_PFLAG_FROZEN ||
 	    all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT))
 		action |= ATA_EH_SOFTRESET;
 	else if (all_err_mask)
@@ -1346,7 +1359,7 @@
 
 	/* record autopsy result */
 	ehc->i.dev = failed_dev;
-	ehc->i.action = action;
+	ehc->i.action |= action;
 
 	DPRINTK("EXIT\n");
 }
@@ -1385,7 +1398,7 @@
 		return;
 
 	frozen = "";
-	if (ap->flags & ATA_FLAG_FROZEN)
+	if (ap->pflags & ATA_PFLAG_FROZEN)
 		frozen = " frozen";
 
 	if (ehc->i.dev) {
@@ -1465,7 +1478,7 @@
 	struct ata_eh_context *ehc = &ap->eh_context;
 	unsigned int *classes = ehc->classes;
 	int tries = ATA_EH_RESET_TRIES;
-	int verbose = !(ap->flags & ATA_FLAG_LOADING);
+	int verbose = !(ehc->i.flags & ATA_EHI_QUIET);
 	unsigned int action;
 	ata_reset_fn_t reset;
 	int i, did_followup_srst, rc;
@@ -1605,7 +1618,7 @@
 		dev = &ap->device[i];
 		action = ata_eh_dev_action(dev);
 
-		if (action & ATA_EH_REVALIDATE && ata_dev_enabled(dev)) {
+		if (action & ATA_EH_REVALIDATE && ata_dev_ready(dev)) {
 			if (ata_port_offline(ap)) {
 				rc = -EIO;
 				break;
@@ -1636,7 +1649,7 @@
 			}
 
 			spin_lock_irqsave(ap->lock, flags);
-			ap->flags |= ATA_FLAG_SCSI_HOTPLUG;
+			ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;
 			spin_unlock_irqrestore(ap->lock, flags);
 		}
 	}
@@ -1648,6 +1661,164 @@
 	return rc;
 }
 
+/**
+ *	ata_eh_suspend - handle suspend EH action
+ *	@ap: target host port
+ *	@r_failed_dev: result parameter to indicate failing device
+ *
+ *	Handle suspend EH action.  Disk devices are spinned down and
+ *	other types of devices are just marked suspended.  Once
+ *	suspended, no EH action to the device is allowed until it is
+ *	resumed.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise
+ */
+static int ata_eh_suspend(struct ata_port *ap, struct ata_device **r_failed_dev)
+{
+	struct ata_device *dev;
+	int i, rc = 0;
+
+	DPRINTK("ENTER\n");
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		unsigned long flags;
+		unsigned int action, err_mask;
+
+		dev = &ap->device[i];
+		action = ata_eh_dev_action(dev);
+
+		if (!ata_dev_enabled(dev) || !(action & ATA_EH_SUSPEND))
+			continue;
+
+		WARN_ON(dev->flags & ATA_DFLAG_SUSPENDED);
+
+		ata_eh_about_to_do(ap, dev, ATA_EH_SUSPEND);
+
+		if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) {
+			/* flush cache */
+			rc = ata_flush_cache(dev);
+			if (rc)
+				break;
+
+			/* spin down */
+			err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1);
+			if (err_mask) {
+				ata_dev_printk(dev, KERN_ERR, "failed to "
+					       "spin down (err_mask=0x%x)\n",
+					       err_mask);
+				rc = -EIO;
+				break;
+			}
+		}
+
+		spin_lock_irqsave(ap->lock, flags);
+		dev->flags |= ATA_DFLAG_SUSPENDED;
+		spin_unlock_irqrestore(ap->lock, flags);
+
+		ata_eh_done(ap, dev, ATA_EH_SUSPEND);
+	}
+
+	if (rc)
+		*r_failed_dev = dev;
+
+	DPRINTK("EXIT\n");
+	return 0;
+}
+
+/**
+ *	ata_eh_prep_resume - prep for resume EH action
+ *	@ap: target host port
+ *
+ *	Clear SUSPENDED in preparation for scheduled resume actions.
+ *	This allows other parts of EH to access the devices being
+ *	resumed.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ */
+static void ata_eh_prep_resume(struct ata_port *ap)
+{
+	struct ata_device *dev;
+	unsigned long flags;
+	int i;
+
+	DPRINTK("ENTER\n");
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		unsigned int action;
+
+		dev = &ap->device[i];
+		action = ata_eh_dev_action(dev);
+
+		if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME))
+			continue;
+
+		spin_lock_irqsave(ap->lock, flags);
+		dev->flags &= ~ATA_DFLAG_SUSPENDED;
+		spin_unlock_irqrestore(ap->lock, flags);
+	}
+
+	DPRINTK("EXIT\n");
+}
+
+/**
+ *	ata_eh_resume - handle resume EH action
+ *	@ap: target host port
+ *	@r_failed_dev: result parameter to indicate failing device
+ *
+ *	Handle resume EH action.  Target devices are already reset and
+ *	revalidated.  Spinning up is the only operation left.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise
+ */
+static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev)
+{
+	struct ata_device *dev;
+	int i, rc = 0;
+
+	DPRINTK("ENTER\n");
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		unsigned int action, err_mask;
+
+		dev = &ap->device[i];
+		action = ata_eh_dev_action(dev);
+
+		if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME))
+			continue;
+
+		ata_eh_about_to_do(ap, dev, ATA_EH_RESUME);
+
+		if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) {
+			err_mask = ata_do_simple_cmd(dev,
+						     ATA_CMD_IDLEIMMEDIATE);
+			if (err_mask) {
+				ata_dev_printk(dev, KERN_ERR, "failed to "
+					       "spin up (err_mask=0x%x)\n",
+					       err_mask);
+				rc = -EIO;
+				break;
+			}
+		}
+
+		ata_eh_done(ap, dev, ATA_EH_RESUME);
+	}
+
+	if (rc)
+		*r_failed_dev = dev;
+
+	DPRINTK("EXIT\n");
+	return 0;
+}
+
 static int ata_port_nr_enabled(struct ata_port *ap)
 {
 	int i, cnt = 0;
@@ -1673,7 +1844,19 @@
 	struct ata_eh_context *ehc = &ap->eh_context;
 	int i;
 
-	if (ap->flags & ATA_FLAG_FROZEN || ata_port_nr_enabled(ap))
+	/* skip if all possible devices are suspended */
+	for (i = 0; i < ata_port_max_devices(ap); i++) {
+		struct ata_device *dev = &ap->device[i];
+
+		if (ata_dev_absent(dev) || ata_dev_ready(dev))
+			break;
+	}
+
+	if (i == ata_port_max_devices(ap))
+		return 1;
+
+	/* always thaw frozen port and recover failed devices */
+	if (ap->pflags & ATA_PFLAG_FROZEN || ata_port_nr_enabled(ap))
 		return 0;
 
 	/* skip if class codes for all vacant slots are ATA_DEV_NONE */
@@ -1744,9 +1927,12 @@
 	rc = 0;
 
 	/* if UNLOADING, finish immediately */
-	if (ap->flags & ATA_FLAG_UNLOADING)
+	if (ap->pflags & ATA_PFLAG_UNLOADING)
 		goto out;
 
+	/* prep for resume */
+	ata_eh_prep_resume(ap);
+
 	/* skip EH if possible. */
 	if (ata_eh_skip_recovery(ap))
 		ehc->i.action = 0;
@@ -1774,6 +1960,11 @@
 	if (rc)
 		goto dev_fail;
 
+	/* resume devices */
+	rc = ata_eh_resume(ap, &dev);
+	if (rc)
+		goto dev_fail;
+
 	/* configure transfer mode if the port has been reset */
 	if (ehc->i.flags & ATA_EHI_DID_RESET) {
 		rc = ata_set_mode(ap, &dev);
@@ -1783,6 +1974,11 @@
 		}
 	}
 
+	/* suspend devices */
+	rc = ata_eh_suspend(ap, &dev);
+	if (rc)
+		goto dev_fail;
+
 	goto out;
 
  dev_fail:
@@ -1908,11 +2104,124 @@
 	       ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
 	       ata_postreset_fn_t postreset)
 {
-	if (!(ap->flags & ATA_FLAG_LOADING)) {
-		ata_eh_autopsy(ap);
-		ata_eh_report(ap);
-	}
-
+	ata_eh_autopsy(ap);
+	ata_eh_report(ap);
 	ata_eh_recover(ap, prereset, softreset, hardreset, postreset);
 	ata_eh_finish(ap);
 }
+
+/**
+ *	ata_eh_handle_port_suspend - perform port suspend operation
+ *	@ap: port to suspend
+ *
+ *	Suspend @ap.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ */
+static void ata_eh_handle_port_suspend(struct ata_port *ap)
+{
+	unsigned long flags;
+	int rc = 0;
+
+	/* are we suspending? */
+	spin_lock_irqsave(ap->lock, flags);
+	if (!(ap->pflags & ATA_PFLAG_PM_PENDING) ||
+	    ap->pm_mesg.event == PM_EVENT_ON) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(ap->lock, flags);
+
+	WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED);
+
+	/* suspend */
+	ata_eh_freeze_port(ap);
+
+	if (ap->ops->port_suspend)
+		rc = ap->ops->port_suspend(ap, ap->pm_mesg);
+
+	/* report result */
+	spin_lock_irqsave(ap->lock, flags);
+
+	ap->pflags &= ~ATA_PFLAG_PM_PENDING;
+	if (rc == 0)
+		ap->pflags |= ATA_PFLAG_SUSPENDED;
+	else
+		ata_port_schedule_eh(ap);
+
+	if (ap->pm_result) {
+		*ap->pm_result = rc;
+		ap->pm_result = NULL;
+	}
+
+	spin_unlock_irqrestore(ap->lock, flags);
+
+	return;
+}
+
+/**
+ *	ata_eh_handle_port_resume - perform port resume operation
+ *	@ap: port to resume
+ *
+ *	Resume @ap.
+ *
+ *	This function also waits upto one second until all devices
+ *	hanging off this port requests resume EH action.  This is to
+ *	prevent invoking EH and thus reset multiple times on resume.
+ *
+ *	On DPM resume, where some of devices might not be resumed
+ *	together, this may delay port resume upto one second, but such
+ *	DPM resumes are rare and 1 sec delay isn't too bad.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ */
+static void ata_eh_handle_port_resume(struct ata_port *ap)
+{
+	unsigned long timeout;
+	unsigned long flags;
+	int i, rc = 0;
+
+	/* are we resuming? */
+	spin_lock_irqsave(ap->lock, flags);
+	if (!(ap->pflags & ATA_PFLAG_PM_PENDING) ||
+	    ap->pm_mesg.event != PM_EVENT_ON) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(ap->lock, flags);
+
+	/* spurious? */
+	if (!(ap->pflags & ATA_PFLAG_SUSPENDED))
+		goto done;
+
+	if (ap->ops->port_resume)
+		rc = ap->ops->port_resume(ap);
+
+	/* give devices time to request EH */
+	timeout = jiffies + HZ; /* 1s max */
+	while (1) {
+		for (i = 0; i < ATA_MAX_DEVICES; i++) {
+			struct ata_device *dev = &ap->device[i];
+			unsigned int action = ata_eh_dev_action(dev);
+
+			if ((dev->flags & ATA_DFLAG_SUSPENDED) &&
+			    !(action & ATA_EH_RESUME))
+				break;
+		}
+
+		if (i == ATA_MAX_DEVICES || time_after(jiffies, timeout))
+			break;
+		msleep(10);
+	}
+
+ done:
+	spin_lock_irqsave(ap->lock, flags);
+	ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
+	if (ap->pm_result) {
+		*ap->pm_result = rc;
+		ap->pm_result = NULL;
+	}
+	spin_unlock_irqrestore(ap->lock, flags);
+}
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 2915bca..7ced41e 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -397,20 +397,129 @@
 	}
 }
 
-int ata_scsi_device_resume(struct scsi_device *sdev)
-{
-	struct ata_port *ap = ata_shost_to_port(sdev->host);
-	struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
-
-	return ata_device_resume(dev);
-}
-
+/**
+ *	ata_scsi_device_suspend - suspend ATA device associated with sdev
+ *	@sdev: the SCSI device to suspend
+ *	@state: target power management state
+ *
+ *	Request suspend EH action on the ATA device associated with
+ *	@sdev and wait for the operation to complete.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ *
+ *	RETURNS:
+ *	0 on success, -errno otherwise.
+ */
 int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state)
 {
 	struct ata_port *ap = ata_shost_to_port(sdev->host);
-	struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
+	struct ata_device *dev = ata_scsi_find_dev(ap, sdev);
+	unsigned long flags;
+	unsigned int action;
+	int rc = 0;
 
-	return ata_device_suspend(dev, state);
+	if (!dev)
+		goto out;
+
+	spin_lock_irqsave(ap->lock, flags);
+
+	/* wait for the previous resume to complete */
+	while (dev->flags & ATA_DFLAG_SUSPENDED) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		ata_port_wait_eh(ap);
+		spin_lock_irqsave(ap->lock, flags);
+	}
+
+	/* if @sdev is already detached, nothing to do */
+	if (sdev->sdev_state == SDEV_OFFLINE ||
+	    sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL)
+		goto out_unlock;
+
+	/* request suspend */
+	action = ATA_EH_SUSPEND;
+	if (state.event != PM_EVENT_SUSPEND)
+		action |= ATA_EH_PM_FREEZE;
+	ap->eh_info.dev_action[dev->devno] |= action;
+	ap->eh_info.flags |= ATA_EHI_QUIET;
+	ata_port_schedule_eh(ap);
+
+	spin_unlock_irqrestore(ap->lock, flags);
+
+	/* wait for EH to do the job */
+	ata_port_wait_eh(ap);
+
+	spin_lock_irqsave(ap->lock, flags);
+
+	/* If @sdev is still attached but the associated ATA device
+	 * isn't suspended, the operation failed.
+	 */
+	if (sdev->sdev_state != SDEV_OFFLINE &&
+	    sdev->sdev_state != SDEV_CANCEL && sdev->sdev_state != SDEV_DEL &&
+	    !(dev->flags & ATA_DFLAG_SUSPENDED))
+		rc = -EIO;
+
+ out_unlock:
+	spin_unlock_irqrestore(ap->lock, flags);
+ out:
+	if (rc == 0)
+		sdev->sdev_gendev.power.power_state = state;
+	return rc;
+}
+
+/**
+ *	ata_scsi_device_resume - resume ATA device associated with sdev
+ *	@sdev: the SCSI device to resume
+ *
+ *	Request resume EH action on the ATA device associated with
+ *	@sdev and return immediately.  This enables parallel
+ *	wakeup/spinup of devices.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep).
+ *
+ *	RETURNS:
+ *	0.
+ */
+int ata_scsi_device_resume(struct scsi_device *sdev)
+{
+	struct ata_port *ap = ata_shost_to_port(sdev->host);
+	struct ata_device *dev = ata_scsi_find_dev(ap, sdev);
+	struct ata_eh_info *ehi = &ap->eh_info;
+	unsigned long flags;
+	unsigned int action;
+
+	if (!dev)
+		goto out;
+
+	spin_lock_irqsave(ap->lock, flags);
+
+	/* if @sdev is already detached, nothing to do */
+	if (sdev->sdev_state == SDEV_OFFLINE ||
+	    sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL)
+		goto out_unlock;
+
+	/* request resume */
+	action = ATA_EH_RESUME;
+	if (sdev->sdev_gendev.power.power_state.event == PM_EVENT_SUSPEND)
+		__ata_ehi_hotplugged(ehi);
+	else
+		action |= ATA_EH_PM_FREEZE | ATA_EH_SOFTRESET;
+	ehi->dev_action[dev->devno] |= action;
+
+	/* We don't want autopsy and verbose EH messages.  Disable
+	 * those if we're the only device on this link.
+	 */
+	if (ata_port_max_devices(ap) == 1)
+		ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
+
+	ata_port_schedule_eh(ap);
+
+ out_unlock:
+	spin_unlock_irqrestore(ap->lock, flags);
+ out:
+	sdev->sdev_gendev.power.power_state = PMSG_ON;
+	return 0;
 }
 
 /**
@@ -2930,7 +3039,7 @@
 	struct ata_port *ap = data;
 	int i;
 
-	if (ap->flags & ATA_FLAG_UNLOADING) {
+	if (ap->pflags & ATA_PFLAG_UNLOADING) {
 		DPRINTK("ENTER/EXIT - unloading\n");
 		return;
 	}
@@ -3011,6 +3120,7 @@
 		if (dev) {
 			ap->eh_info.probe_mask |= 1 << dev->devno;
 			ap->eh_info.action |= ATA_EH_SOFTRESET;
+			ap->eh_info.flags |= ATA_EHI_RESUME_LINK;
 		} else
 			rc = -EINVAL;
 	}
diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
index 7aabb45..d0a8507 100644
--- a/drivers/scsi/sata_sil.c
+++ b/drivers/scsi/sata_sil.c
@@ -109,6 +109,7 @@
 };
 
 static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static int sil_pci_device_resume(struct pci_dev *pdev);
 static void sil_dev_config(struct ata_port *ap, struct ata_device *dev);
 static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);
 static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
@@ -160,6 +161,8 @@
 	.id_table		= sil_pci_tbl,
 	.probe			= sil_init_one,
 	.remove			= ata_pci_remove_one,
+	.suspend		= ata_pci_device_suspend,
+	.resume			= sil_pci_device_resume,
 };
 
 static struct scsi_host_template sil_sht = {
@@ -178,6 +181,8 @@
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.suspend		= ata_scsi_device_suspend,
+	.resume			= ata_scsi_device_resume,
 };
 
 static const struct ata_port_operations sil_ops = {
@@ -370,7 +375,7 @@
 		 * during hardreset makes controllers with broken SIEN
 		 * repeat probing needlessly.
 		 */
-		if (!(ap->flags & ATA_FLAG_FROZEN)) {
+		if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
 			ata_ehi_hotplugged(&ap->eh_info);
 			ap->eh_info.serror |= serror;
 		}
@@ -561,6 +566,52 @@
 	}
 }
 
+static void sil_init_controller(struct pci_dev *pdev,
+				int n_ports, unsigned long host_flags,
+				void __iomem *mmio_base)
+{
+	u8 cls;
+	u32 tmp;
+	int i;
+
+	/* Initialize FIFO PCI bus arbitration */
+	cls = sil_get_device_cache_line(pdev);
+	if (cls) {
+		cls >>= 3;
+		cls++;  /* cls = (line_size/8)+1 */
+		for (i = 0; i < n_ports; i++)
+			writew(cls << 8 | cls,
+			       mmio_base + sil_port[i].fifo_cfg);
+	} else
+		dev_printk(KERN_WARNING, &pdev->dev,
+			   "cache line size not set.  Driver may not function\n");
+
+	/* Apply R_ERR on DMA activate FIS errata workaround */
+	if (host_flags & SIL_FLAG_RERR_ON_DMA_ACT) {
+		int cnt;
+
+		for (i = 0, cnt = 0; i < n_ports; i++) {
+			tmp = readl(mmio_base + sil_port[i].sfis_cfg);
+			if ((tmp & 0x3) != 0x01)
+				continue;
+			if (!cnt)
+				dev_printk(KERN_INFO, &pdev->dev,
+					   "Applying R_ERR on DMA activate "
+					   "FIS errata fix\n");
+			writel(tmp & ~0x3, mmio_base + sil_port[i].sfis_cfg);
+			cnt++;
+		}
+	}
+
+	if (n_ports == 4) {
+		/* flip the magic "make 4 ports work" bit */
+		tmp = readl(mmio_base + sil_port[2].bmdma);
+		if ((tmp & SIL_INTR_STEERING) == 0)
+			writel(tmp | SIL_INTR_STEERING,
+			       mmio_base + sil_port[2].bmdma);
+	}
+}
+
 static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version;
@@ -570,8 +621,6 @@
 	int rc;
 	unsigned int i;
 	int pci_dev_busy = 0;
-	u32 tmp;
-	u8 cls;
 
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
@@ -630,42 +679,8 @@
 		ata_std_ports(&probe_ent->port[i]);
 	}
 
-	/* Initialize FIFO PCI bus arbitration */
-	cls = sil_get_device_cache_line(pdev);
-	if (cls) {
-		cls >>= 3;
-		cls++;  /* cls = (line_size/8)+1 */
-		for (i = 0; i < probe_ent->n_ports; i++)
-			writew(cls << 8 | cls,
-			       mmio_base + sil_port[i].fifo_cfg);
-	} else
-		dev_printk(KERN_WARNING, &pdev->dev,
-			   "cache line size not set.  Driver may not function\n");
-
-	/* Apply R_ERR on DMA activate FIS errata workaround */
-	if (probe_ent->host_flags & SIL_FLAG_RERR_ON_DMA_ACT) {
-		int cnt;
-
-		for (i = 0, cnt = 0; i < probe_ent->n_ports; i++) {
-			tmp = readl(mmio_base + sil_port[i].sfis_cfg);
-			if ((tmp & 0x3) != 0x01)
-				continue;
-			if (!cnt)
-				dev_printk(KERN_INFO, &pdev->dev,
-					   "Applying R_ERR on DMA activate "
-					   "FIS errata fix\n");
-			writel(tmp & ~0x3, mmio_base + sil_port[i].sfis_cfg);
-			cnt++;
-		}
-	}
-
-	if (ent->driver_data == sil_3114) {
-		/* flip the magic "make 4 ports work" bit */
-		tmp = readl(mmio_base + sil_port[2].bmdma);
-		if ((tmp & SIL_INTR_STEERING) == 0)
-			writel(tmp | SIL_INTR_STEERING,
-			       mmio_base + sil_port[2].bmdma);
-	}
+	sil_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags,
+			    mmio_base);
 
 	pci_set_master(pdev);
 
@@ -685,6 +700,18 @@
 	return rc;
 }
 
+static int sil_pci_device_resume(struct pci_dev *pdev)
+{
+	struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+
+	ata_pci_device_do_resume(pdev);
+	sil_init_controller(pdev, host_set->n_ports, host_set->ports[0]->flags,
+			    host_set->mmio_base);
+	ata_host_set_resume(host_set);
+
+	return 0;
+}
+
 static int __init sil_init(void)
 {
 	return pci_module_init(&sil_pci_driver);
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 07a1c6a..2e0f4a4 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -92,6 +92,7 @@
 	HOST_CTRL_STOP		= (1 << 18), /* latched PCI STOP */
 	HOST_CTRL_DEVSEL	= (1 << 19), /* latched PCI DEVSEL */
 	HOST_CTRL_REQ64		= (1 << 20), /* latched PCI REQ64 */
+	HOST_CTRL_GLOBAL_RST	= (1 << 31), /* global reset */
 
 	/*
 	 * Port registers
@@ -338,6 +339,7 @@
 static void sil24_port_stop(struct ata_port *ap);
 static void sil24_host_stop(struct ata_host_set *host_set);
 static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
+static int sil24_pci_device_resume(struct pci_dev *pdev);
 
 static const struct pci_device_id sil24_pci_tbl[] = {
 	{ 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 },
@@ -353,6 +355,8 @@
 	.id_table		= sil24_pci_tbl,
 	.probe			= sil24_init_one,
 	.remove			= ata_pci_remove_one, /* safe? */
+	.suspend		= ata_pci_device_suspend,
+	.resume			= sil24_pci_device_resume,
 };
 
 static struct scsi_host_template sil24_sht = {
@@ -372,6 +376,8 @@
 	.slave_configure	= ata_scsi_slave_config,
 	.slave_destroy		= ata_scsi_slave_destroy,
 	.bios_param		= ata_std_bios_param,
+	.suspend		= ata_scsi_device_suspend,
+	.resume			= ata_scsi_device_resume,
 };
 
 static const struct ata_port_operations sil24_ops = {
@@ -607,7 +613,7 @@
 	/* SStatus oscillates between zero and valid status after
 	 * DEV_RST, debounce it.
 	 */
-	rc = sata_phy_debounce(ap, sata_deb_timing_before_fsrst);
+	rc = sata_phy_debounce(ap, sata_deb_timing_long);
 	if (rc) {
 		reason = "PHY debouncing failed";
 		goto err;
@@ -988,6 +994,64 @@
 	kfree(hpriv);
 }
 
+static void sil24_init_controller(struct pci_dev *pdev, int n_ports,
+				  unsigned long host_flags,
+				  void __iomem *host_base,
+				  void __iomem *port_base)
+{
+	u32 tmp;
+	int i;
+
+	/* GPIO off */
+	writel(0, host_base + HOST_FLASH_CMD);
+
+	/* clear global reset & mask interrupts during initialization */
+	writel(0, host_base + HOST_CTRL);
+
+	/* init ports */
+	for (i = 0; i < n_ports; i++) {
+		void __iomem *port = port_base + i * PORT_REGS_SIZE;
+
+		/* Initial PHY setting */
+		writel(0x20c, port + PORT_PHY_CFG);
+
+		/* Clear port RST */
+		tmp = readl(port + PORT_CTRL_STAT);
+		if (tmp & PORT_CS_PORT_RST) {
+			writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
+			tmp = ata_wait_register(port + PORT_CTRL_STAT,
+						PORT_CS_PORT_RST,
+						PORT_CS_PORT_RST, 10, 100);
+			if (tmp & PORT_CS_PORT_RST)
+				dev_printk(KERN_ERR, &pdev->dev,
+				           "failed to clear port RST\n");
+		}
+
+		/* Configure IRQ WoC */
+		if (host_flags & SIL24_FLAG_PCIX_IRQ_WOC)
+			writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
+		else
+			writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
+
+		/* Zero error counters. */
+		writel(0x8000, port + PORT_DECODE_ERR_THRESH);
+		writel(0x8000, port + PORT_CRC_ERR_THRESH);
+		writel(0x8000, port + PORT_HSHK_ERR_THRESH);
+		writel(0x0000, port + PORT_DECODE_ERR_CNT);
+		writel(0x0000, port + PORT_CRC_ERR_CNT);
+		writel(0x0000, port + PORT_HSHK_ERR_CNT);
+
+		/* Always use 64bit activation */
+		writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
+
+		/* Clear port multiplier enable and resume bits */
+		writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
+	}
+
+	/* Turn on interrupts */
+	writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
+}
+
 static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	static int printed_version = 0;
@@ -1076,9 +1140,6 @@
 		}
 	}
 
-	/* GPIO off */
-	writel(0, host_base + HOST_FLASH_CMD);
-
 	/* Apply workaround for completion IRQ loss on PCI-X errata */
 	if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) {
 		tmp = readl(host_base + HOST_CTRL);
@@ -1090,56 +1151,18 @@
 			probe_ent->host_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;
 	}
 
-	/* clear global reset & mask interrupts during initialization */
-	writel(0, host_base + HOST_CTRL);
-
 	for (i = 0; i < probe_ent->n_ports; i++) {
-		void __iomem *port = port_base + i * PORT_REGS_SIZE;
-		unsigned long portu = (unsigned long)port;
+		unsigned long portu =
+			(unsigned long)port_base + i * PORT_REGS_SIZE;
 
 		probe_ent->port[i].cmd_addr = portu;
 		probe_ent->port[i].scr_addr = portu + PORT_SCONTROL;
 
 		ata_std_ports(&probe_ent->port[i]);
-
-		/* Initial PHY setting */
-		writel(0x20c, port + PORT_PHY_CFG);
-
-		/* Clear port RST */
-		tmp = readl(port + PORT_CTRL_STAT);
-		if (tmp & PORT_CS_PORT_RST) {
-			writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
-			tmp = ata_wait_register(port + PORT_CTRL_STAT,
-						PORT_CS_PORT_RST,
-						PORT_CS_PORT_RST, 10, 100);
-			if (tmp & PORT_CS_PORT_RST)
-				dev_printk(KERN_ERR, &pdev->dev,
-				           "failed to clear port RST\n");
-		}
-
-		/* Configure IRQ WoC */
-		if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC)
-			writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT);
-		else
-			writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
-
-		/* Zero error counters. */
-		writel(0x8000, port + PORT_DECODE_ERR_THRESH);
-		writel(0x8000, port + PORT_CRC_ERR_THRESH);
-		writel(0x8000, port + PORT_HSHK_ERR_THRESH);
-		writel(0x0000, port + PORT_DECODE_ERR_CNT);
-		writel(0x0000, port + PORT_CRC_ERR_CNT);
-		writel(0x0000, port + PORT_HSHK_ERR_CNT);
-
-		/* Always use 64bit activation */
-		writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
-
-		/* Clear port multiplier enable and resume bits */
-		writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
 	}
 
-	/* Turn on interrupts */
-	writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL);
+	sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags,
+			      host_base, port_base);
 
 	pci_set_master(pdev);
 
@@ -1162,6 +1185,25 @@
 	return rc;
 }
 
+static int sil24_pci_device_resume(struct pci_dev *pdev)
+{
+	struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
+	struct sil24_host_priv *hpriv = host_set->private_data;
+
+	ata_pci_device_do_resume(pdev);
+
+	if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
+		writel(HOST_CTRL_GLOBAL_RST, hpriv->host_base + HOST_CTRL);
+
+	sil24_init_controller(pdev, host_set->n_ports,
+			      host_set->ports[0]->flags,
+			      hpriv->host_base, hpriv->port_base);
+
+	ata_host_set_resume(host_set);
+
+	return 0;
+}
+
 static int __init sil24_init(void)
 {
 	return pci_module_init(&sil24_pci_driver);
diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c
index 916fe6f..ad37871 100644
--- a/drivers/scsi/sata_vsc.c
+++ b/drivers/scsi/sata_vsc.c
@@ -297,7 +297,7 @@
 	.bmdma_status		= ata_bmdma_status,
 	.qc_prep		= ata_qc_prep,
 	.qc_issue		= ata_qc_issue_prot,
-	.data_xfer		= ata_pio_data_xfer,
+	.data_xfer		= ata_mmio_data_xfer,
 	.freeze			= ata_bmdma_freeze,
 	.thaw			= ata_bmdma_thaw,
 	.error_handler		= ata_bmdma_error_handler,
diff --git a/drivers/serial/at91_serial.c b/drivers/serial/at91_serial.c
index a7d6643..54c6b2a 100644
--- a/drivers/serial/at91_serial.c
+++ b/drivers/serial/at91_serial.c
@@ -41,6 +41,7 @@
 #include <asm/mach/serial_at91.h>
 #include <asm/arch/board.h>
 #include <asm/arch/system.h>
+#include <asm/arch/gpio.h>
 
 #if defined(CONFIG_SERIAL_AT91_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
 #define SUPPORT_SYSRQ
@@ -140,9 +141,9 @@
 		 */
 		if (port->mapbase == AT91_BASE_US0) {
 			if (mctrl & TIOCM_RTS)
-				at91_sys_write(AT91_PIOA + PIO_CODR, AT91_PA21_RTS0);
+				at91_set_gpio_value(AT91_PIN_PA21, 0);
 			else
-				at91_sys_write(AT91_PIOA + PIO_SODR, AT91_PA21_RTS0);
+				at91_set_gpio_value(AT91_PIN_PA21, 1);
 		}
 	}
 
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 5980c45..89ba0df 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -454,7 +454,7 @@
 	fl->fl_ops = &nlmclnt_lock_ops;
 }
 
-static void do_vfs_lock(struct file_lock *fl)
+static int do_vfs_lock(struct file_lock *fl)
 {
 	int res = 0;
 	switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
@@ -467,9 +467,7 @@
 		default:
 			BUG();
 	}
-	if (res < 0)
-		printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
-				__FUNCTION__);
+	return res;
 }
 
 /*
@@ -498,6 +496,7 @@
 	struct nlm_host	*host = req->a_host;
 	struct nlm_res	*resp = &req->a_res;
 	struct nlm_wait *block = NULL;
+	unsigned char fl_flags = fl->fl_flags;
 	int status = -ENOLCK;
 
 	if (!host->h_monitored && nsm_monitor(host) < 0) {
@@ -505,6 +504,10 @@
 					host->h_name);
 		goto out;
 	}
+	fl->fl_flags |= FL_ACCESS;
+	status = do_vfs_lock(fl);
+	if (status < 0)
+		goto out;
 
 	block = nlmclnt_prepare_block(host, fl);
 again:
@@ -539,9 +542,10 @@
 			up_read(&host->h_rwsem);
 			goto again;
 		}
-		fl->fl_flags |= FL_SLEEP;
 		/* Ensure the resulting lock will get added to granted list */
-		do_vfs_lock(fl);
+		fl->fl_flags = fl_flags | FL_SLEEP;
+		if (do_vfs_lock(fl) < 0)
+			printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
 		up_read(&host->h_rwsem);
 	}
 	status = nlm_stat_to_errno(resp->status);
@@ -552,6 +556,7 @@
 		nlmclnt_cancel(host, req->a_args.block, fl);
 out:
 	nlm_release_call(req);
+	fl->fl_flags = fl_flags;
 	return status;
 }
 
@@ -606,15 +611,19 @@
 {
 	struct nlm_host	*host = req->a_host;
 	struct nlm_res	*resp = &req->a_res;
-	int		status;
+	int status = 0;
 
 	/*
 	 * Note: the server is supposed to either grant us the unlock
 	 * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either
 	 * case, we want to unlock.
 	 */
+	fl->fl_flags |= FL_EXISTS;
 	down_read(&host->h_rwsem);
-	do_vfs_lock(fl);
+	if (do_vfs_lock(fl) == -ENOENT) {
+		up_read(&host->h_rwsem);
+		goto out;
+	}
 	up_read(&host->h_rwsem);
 
 	if (req->a_flags & RPC_TASK_ASYNC)
@@ -624,7 +633,6 @@
 	if (status < 0)
 		goto out;
 
-	status = 0;
 	if (resp->status == NLM_LCK_GRANTED)
 		goto out;
 
diff --git a/fs/locks.c b/fs/locks.c
index 1ad29c9..b0b41a6 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -725,6 +725,10 @@
 /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
  * at the head of the list, but that's secret knowledge known only to
  * flock_lock_file and posix_lock_file.
+ *
+ * Note that if called with an FL_EXISTS argument, the caller may determine
+ * whether or not a lock was successfully freed by testing the return
+ * value for -ENOENT.
  */
 static int flock_lock_file(struct file *filp, struct file_lock *request)
 {
@@ -735,6 +739,8 @@
 	int found = 0;
 
 	lock_kernel();
+	if (request->fl_flags & FL_ACCESS)
+		goto find_conflict;
 	for_each_lock(inode, before) {
 		struct file_lock *fl = *before;
 		if (IS_POSIX(fl))
@@ -750,8 +756,11 @@
 		break;
 	}
 
-	if (request->fl_type == F_UNLCK)
+	if (request->fl_type == F_UNLCK) {
+		if ((request->fl_flags & FL_EXISTS) && !found)
+			error = -ENOENT;
 		goto out;
+	}
 
 	error = -ENOMEM;
 	new_fl = locks_alloc_lock();
@@ -764,6 +773,7 @@
 	if (found)
 		cond_resched();
 
+find_conflict:
 	for_each_lock(inode, before) {
 		struct file_lock *fl = *before;
 		if (IS_POSIX(fl))
@@ -777,6 +787,8 @@
 			locks_insert_block(fl, request);
 		goto out;
 	}
+	if (request->fl_flags & FL_ACCESS)
+		goto out;
 	locks_copy_lock(new_fl, request);
 	locks_insert_lock(&inode->i_flock, new_fl);
 	new_fl = NULL;
@@ -948,8 +960,11 @@
 
 	error = 0;
 	if (!added) {
-		if (request->fl_type == F_UNLCK)
+		if (request->fl_type == F_UNLCK) {
+			if (request->fl_flags & FL_EXISTS)
+				error = -ENOENT;
 			goto out;
+		}
 
 		if (!new_fl) {
 			error = -ENOLCK;
@@ -996,6 +1011,10 @@
  * Add a POSIX style lock to a file.
  * We merge adjacent & overlapping locks whenever possible.
  * POSIX locks are sorted by owner task, then by starting address
+ *
+ * Note that if called with an FL_EXISTS argument, the caller may determine
+ * whether or not a lock was successfully freed by testing the return
+ * value for -ENOENT.
  */
 int posix_lock_file(struct file *filp, struct file_lock *fl)
 {
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 3ddda6f..e7ffb4d 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -690,7 +690,9 @@
 			goto out_force;
 		/* This is an open(2) */
 		if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 &&
-				!(server->flags & NFS_MOUNT_NOCTO))
+				!(server->flags & NFS_MOUNT_NOCTO) &&
+				(S_ISREG(inode->i_mode) ||
+				 S_ISDIR(inode->i_mode)))
 			goto out_force;
 	}
 	return nfs_revalidate_inode(server, inode);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 4cdd1b4..fecd3b09 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -67,25 +67,19 @@
 	struct kref		kref;		/* release manager */
 
 	/* I/O parameters */
-	struct list_head	list,		/* nfs_read/write_data structs */
-				rewrite_list;	/* saved nfs_write_data structs */
 	struct nfs_open_context	*ctx;		/* file open context info */
 	struct kiocb *		iocb;		/* controlling i/o request */
 	struct inode *		inode;		/* target file of i/o */
-	unsigned long		user_addr;	/* location of user's buffer */
-	size_t			user_count;	/* total bytes to move */
-	loff_t			pos;		/* starting offset in file */
-	struct page **		pages;		/* pages in our buffer */
-	unsigned int		npages;		/* count of pages */
 
 	/* completion state */
+	atomic_t		io_count;	/* i/os we're waiting for */
 	spinlock_t		lock;		/* protect completion state */
-	int			outstanding;	/* i/os we're waiting for */
 	ssize_t			count,		/* bytes actually processed */
 				error;		/* any reported error */
 	struct completion	completion;	/* wait for i/o completion */
 
 	/* commit state */
+	struct list_head	rewrite_list;	/* saved nfs_write_data structs */
 	struct nfs_write_data *	commit_data;	/* special write_data for commits */
 	int			flags;
 #define NFS_ODIRECT_DO_COMMIT		(1)	/* an unstable reply was received */
@@ -93,8 +87,37 @@
 	struct nfs_writeverf	verf;		/* unstable write verifier */
 };
 
-static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync);
 static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode);
+static const struct rpc_call_ops nfs_write_direct_ops;
+
+static inline void get_dreq(struct nfs_direct_req *dreq)
+{
+	atomic_inc(&dreq->io_count);
+}
+
+static inline int put_dreq(struct nfs_direct_req *dreq)
+{
+	return atomic_dec_and_test(&dreq->io_count);
+}
+
+/*
+ * "size" is never larger than rsize or wsize.
+ */
+static inline int nfs_direct_count_pages(unsigned long user_addr, size_t size)
+{
+	int page_count;
+
+	page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	page_count -= user_addr >> PAGE_SHIFT;
+	BUG_ON(page_count < 0);
+
+	return page_count;
+}
+
+static inline unsigned int nfs_max_pages(unsigned int size)
+{
+	return (size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+}
 
 /**
  * nfs_direct_IO - NFS address space operation for direct I/O
@@ -118,50 +141,21 @@
 	return -EINVAL;
 }
 
-static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
+static void nfs_direct_dirty_pages(struct page **pages, int npages)
 {
 	int i;
 	for (i = 0; i < npages; i++) {
 		struct page *page = pages[i];
-		if (do_dirty && !PageCompound(page))
+		if (!PageCompound(page))
 			set_page_dirty_lock(page);
-		page_cache_release(page);
 	}
-	kfree(pages);
 }
 
-static inline int nfs_get_user_pages(int rw, unsigned long user_addr, size_t size, struct page ***pages)
+static void nfs_direct_release_pages(struct page **pages, int npages)
 {
-	int result = -ENOMEM;
-	unsigned long page_count;
-	size_t array_size;
-
-	page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-	page_count -= user_addr >> PAGE_SHIFT;
-
-	array_size = (page_count * sizeof(struct page *));
-	*pages = kmalloc(array_size, GFP_KERNEL);
-	if (*pages) {
-		down_read(&current->mm->mmap_sem);
-		result = get_user_pages(current, current->mm, user_addr,
-					page_count, (rw == READ), 0,
-					*pages, NULL);
-		up_read(&current->mm->mmap_sem);
-		if (result != page_count) {
-			/*
-			 * If we got fewer pages than expected from
-			 * get_user_pages(), the user buffer runs off the
-			 * end of a mapping; return EFAULT.
-			 */
-			if (result >= 0) {
-				nfs_free_user_pages(*pages, result, 0);
-				result = -EFAULT;
-			} else
-				kfree(*pages);
-			*pages = NULL;
-		}
-	}
-	return result;
+	int i;
+	for (i = 0; i < npages; i++)
+		page_cache_release(pages[i]);
 }
 
 static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
@@ -173,13 +167,13 @@
 		return NULL;
 
 	kref_init(&dreq->kref);
+	kref_get(&dreq->kref);
 	init_completion(&dreq->completion);
-	INIT_LIST_HEAD(&dreq->list);
 	INIT_LIST_HEAD(&dreq->rewrite_list);
 	dreq->iocb = NULL;
 	dreq->ctx = NULL;
 	spin_lock_init(&dreq->lock);
-	dreq->outstanding = 0;
+	atomic_set(&dreq->io_count, 0);
 	dreq->count = 0;
 	dreq->error = 0;
 	dreq->flags = 0;
@@ -220,18 +214,11 @@
 }
 
 /*
- * We must hold a reference to all the pages in this direct read request
- * until the RPCs complete.  This could be long *after* we are woken up in
- * nfs_direct_wait (for instance, if someone hits ^C on a slow server).
- *
- * In addition, synchronous I/O uses a stack-allocated iocb.  Thus we
- * can't trust the iocb is still valid here if this is a synchronous
- * request.  If the waiter is woken prematurely, the iocb is long gone.
+ * Synchronous I/O uses a stack-allocated iocb.  Thus we can't trust
+ * the iocb is still valid here if this is a synchronous request.
  */
 static void nfs_direct_complete(struct nfs_direct_req *dreq)
 {
-	nfs_free_user_pages(dreq->pages, dreq->npages, 1);
-
 	if (dreq->iocb) {
 		long res = (long) dreq->error;
 		if (!res)
@@ -244,48 +231,10 @@
 }
 
 /*
- * Note we also set the number of requests we have in the dreq when we are
- * done.  This prevents races with I/O completion so we will always wait
- * until all requests have been dispatched and completed.
+ * We must hold a reference to all the pages in this direct read request
+ * until the RPCs complete.  This could be long *after* we are woken up in
+ * nfs_direct_wait (for instance, if someone hits ^C on a slow server).
  */
-static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, size_t rsize)
-{
-	struct list_head *list;
-	struct nfs_direct_req *dreq;
-	unsigned int rpages = (rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-
-	dreq = nfs_direct_req_alloc();
-	if (!dreq)
-		return NULL;
-
-	list = &dreq->list;
-	for(;;) {
-		struct nfs_read_data *data = nfs_readdata_alloc(rpages);
-
-		if (unlikely(!data)) {
-			while (!list_empty(list)) {
-				data = list_entry(list->next,
-						  struct nfs_read_data, pages);
-				list_del(&data->pages);
-				nfs_readdata_free(data);
-			}
-			kref_put(&dreq->kref, nfs_direct_req_release);
-			return NULL;
-		}
-
-		INIT_LIST_HEAD(&data->pages);
-		list_add(&data->pages, list);
-
-		data->req = (struct nfs_page *) dreq;
-		dreq->outstanding++;
-		if (nbytes <= rsize)
-			break;
-		nbytes -= rsize;
-	}
-	kref_get(&dreq->kref);
-	return dreq;
-}
-
 static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
 {
 	struct nfs_read_data *data = calldata;
@@ -294,6 +243,9 @@
 	if (nfs_readpage_result(task, data) != 0)
 		return;
 
+	nfs_direct_dirty_pages(data->pagevec, data->npages);
+	nfs_direct_release_pages(data->pagevec, data->npages);
+
 	spin_lock(&dreq->lock);
 
 	if (likely(task->tk_status >= 0))
@@ -301,13 +253,10 @@
 	else
 		dreq->error = task->tk_status;
 
-	if (--dreq->outstanding) {
-		spin_unlock(&dreq->lock);
-		return;
-	}
-
 	spin_unlock(&dreq->lock);
-	nfs_direct_complete(dreq);
+
+	if (put_dreq(dreq))
+		nfs_direct_complete(dreq);
 }
 
 static const struct rpc_call_ops nfs_read_direct_ops = {
@@ -316,41 +265,60 @@
 };
 
 /*
- * For each nfs_read_data struct that was allocated on the list, dispatch
- * an NFS READ operation
+ * For each rsize'd chunk of the user's buffer, dispatch an NFS READ
+ * operation.  If nfs_readdata_alloc() or get_user_pages() fails,
+ * bail and stop sending more reads.  Read length accounting is
+ * handled automatically by nfs_direct_read_result().  Otherwise, if
+ * no requests have been sent, just return an error.
  */
-static void nfs_direct_read_schedule(struct nfs_direct_req *dreq)
+static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos)
 {
 	struct nfs_open_context *ctx = dreq->ctx;
 	struct inode *inode = ctx->dentry->d_inode;
-	struct list_head *list = &dreq->list;
-	struct page **pages = dreq->pages;
-	size_t count = dreq->user_count;
-	loff_t pos = dreq->pos;
 	size_t rsize = NFS_SERVER(inode)->rsize;
-	unsigned int curpage, pgbase;
+	unsigned int rpages = nfs_max_pages(rsize);
+	unsigned int pgbase;
+	int result;
+	ssize_t started = 0;
 
-	curpage = 0;
-	pgbase = dreq->user_addr & ~PAGE_MASK;
+	get_dreq(dreq);
+
+	pgbase = user_addr & ~PAGE_MASK;
 	do {
 		struct nfs_read_data *data;
 		size_t bytes;
 
+		result = -ENOMEM;
+		data = nfs_readdata_alloc(rpages);
+		if (unlikely(!data))
+			break;
+
 		bytes = rsize;
 		if (count < rsize)
 			bytes = count;
 
-		BUG_ON(list_empty(list));
-		data = list_entry(list->next, struct nfs_read_data, pages);
-		list_del_init(&data->pages);
+		data->npages = nfs_direct_count_pages(user_addr, bytes);
+		down_read(&current->mm->mmap_sem);
+		result = get_user_pages(current, current->mm, user_addr,
+					data->npages, 1, 0, data->pagevec, NULL);
+		up_read(&current->mm->mmap_sem);
+		if (unlikely(result < data->npages)) {
+			if (result > 0)
+				nfs_direct_release_pages(data->pagevec, result);
+			nfs_readdata_release(data);
+			break;
+		}
 
+		get_dreq(dreq);
+
+		data->req = (struct nfs_page *) dreq;
 		data->inode = inode;
 		data->cred = ctx->cred;
 		data->args.fh = NFS_FH(inode);
 		data->args.context = ctx;
 		data->args.offset = pos;
 		data->args.pgbase = pgbase;
-		data->args.pages = &pages[curpage];
+		data->args.pages = data->pagevec;
 		data->args.count = bytes;
 		data->res.fattr = &data->fattr;
 		data->res.eof = 0;
@@ -373,33 +341,35 @@
 				bytes,
 				(unsigned long long)data->args.offset);
 
+		started += bytes;
+		user_addr += bytes;
 		pos += bytes;
 		pgbase += bytes;
-		curpage += pgbase >> PAGE_SHIFT;
 		pgbase &= ~PAGE_MASK;
 
 		count -= bytes;
 	} while (count != 0);
-	BUG_ON(!list_empty(list));
+
+	if (put_dreq(dreq))
+		nfs_direct_complete(dreq);
+
+	if (started)
+		return 0;
+	return result < 0 ? (ssize_t) result : -EFAULT;
 }
 
-static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos, struct page **pages, unsigned int nr_pages)
+static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
 {
-	ssize_t result;
+	ssize_t result = 0;
 	sigset_t oldset;
 	struct inode *inode = iocb->ki_filp->f_mapping->host;
 	struct rpc_clnt *clnt = NFS_CLIENT(inode);
 	struct nfs_direct_req *dreq;
 
-	dreq = nfs_direct_read_alloc(count, NFS_SERVER(inode)->rsize);
+	dreq = nfs_direct_req_alloc();
 	if (!dreq)
 		return -ENOMEM;
 
-	dreq->user_addr = user_addr;
-	dreq->user_count = count;
-	dreq->pos = pos;
-	dreq->pages = pages;
-	dreq->npages = nr_pages;
 	dreq->inode = inode;
 	dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data);
 	if (!is_sync_kiocb(iocb))
@@ -407,8 +377,9 @@
 
 	nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count);
 	rpc_clnt_sigmask(clnt, &oldset);
-	nfs_direct_read_schedule(dreq);
-	result = nfs_direct_wait(dreq);
+	result = nfs_direct_read_schedule(dreq, user_addr, count, pos);
+	if (!result)
+		result = nfs_direct_wait(dreq);
 	rpc_clnt_sigunmask(clnt, &oldset);
 
 	return result;
@@ -416,10 +387,10 @@
 
 static void nfs_direct_free_writedata(struct nfs_direct_req *dreq)
 {
-	list_splice_init(&dreq->rewrite_list, &dreq->list);
-	while (!list_empty(&dreq->list)) {
-		struct nfs_write_data *data = list_entry(dreq->list.next, struct nfs_write_data, pages);
+	while (!list_empty(&dreq->rewrite_list)) {
+		struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages);
 		list_del(&data->pages);
+		nfs_direct_release_pages(data->pagevec, data->npages);
 		nfs_writedata_release(data);
 	}
 }
@@ -427,14 +398,51 @@
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
 static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
 {
-	struct list_head *pos;
+	struct inode *inode = dreq->inode;
+	struct list_head *p;
+	struct nfs_write_data *data;
 
-	list_splice_init(&dreq->rewrite_list, &dreq->list);
-	list_for_each(pos, &dreq->list)
-		dreq->outstanding++;
 	dreq->count = 0;
+	get_dreq(dreq);
 
-	nfs_direct_write_schedule(dreq, FLUSH_STABLE);
+	list_for_each(p, &dreq->rewrite_list) {
+		data = list_entry(p, struct nfs_write_data, pages);
+
+		get_dreq(dreq);
+
+		/*
+		 * Reset data->res.
+		 */
+		nfs_fattr_init(&data->fattr);
+		data->res.count = data->args.count;
+		memset(&data->verf, 0, sizeof(data->verf));
+
+		/*
+		 * Reuse data->task; data->args should not have changed
+		 * since the original request was sent.
+		 */
+		rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC,
+				&nfs_write_direct_ops, data);
+		NFS_PROTO(inode)->write_setup(data, FLUSH_STABLE);
+
+		data->task.tk_priority = RPC_PRIORITY_NORMAL;
+		data->task.tk_cookie = (unsigned long) inode;
+
+		/*
+		 * We're called via an RPC callback, so BKL is already held.
+		 */
+		rpc_execute(&data->task);
+
+		dprintk("NFS: %5u rescheduled direct write call (req %s/%Ld, %u bytes @ offset %Lu)\n",
+				data->task.tk_pid,
+				inode->i_sb->s_id,
+				(long long)NFS_FILEID(inode),
+				data->args.count,
+				(unsigned long long)data->args.offset);
+	}
+
+	if (put_dreq(dreq))
+		nfs_direct_write_complete(dreq, inode);
 }
 
 static void nfs_direct_commit_result(struct rpc_task *task, void *calldata)
@@ -471,8 +479,8 @@
 	data->cred = dreq->ctx->cred;
 
 	data->args.fh = NFS_FH(data->inode);
-	data->args.offset = dreq->pos;
-	data->args.count = dreq->user_count;
+	data->args.offset = 0;
+	data->args.count = 0;
 	data->res.count = 0;
 	data->res.fattr = &data->fattr;
 	data->res.verf = &data->verf;
@@ -534,47 +542,6 @@
 }
 #endif
 
-static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize)
-{
-	struct list_head *list;
-	struct nfs_direct_req *dreq;
-	unsigned int wpages = (wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-
-	dreq = nfs_direct_req_alloc();
-	if (!dreq)
-		return NULL;
-
-	list = &dreq->list;
-	for(;;) {
-		struct nfs_write_data *data = nfs_writedata_alloc(wpages);
-
-		if (unlikely(!data)) {
-			while (!list_empty(list)) {
-				data = list_entry(list->next,
-						  struct nfs_write_data, pages);
-				list_del(&data->pages);
-				nfs_writedata_free(data);
-			}
-			kref_put(&dreq->kref, nfs_direct_req_release);
-			return NULL;
-		}
-
-		INIT_LIST_HEAD(&data->pages);
-		list_add(&data->pages, list);
-
-		data->req = (struct nfs_page *) dreq;
-		dreq->outstanding++;
-		if (nbytes <= wsize)
-			break;
-		nbytes -= wsize;
-	}
-
-	nfs_alloc_commit_data(dreq);
-
-	kref_get(&dreq->kref);
-	return dreq;
-}
-
 static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
 {
 	struct nfs_write_data *data = calldata;
@@ -604,8 +571,6 @@
 				}
 		}
 	}
-	/* In case we have to resend */
-	data->args.stable = NFS_FILE_SYNC;
 
 	spin_unlock(&dreq->lock);
 }
@@ -619,14 +584,8 @@
 	struct nfs_write_data *data = calldata;
 	struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
 
-	spin_lock(&dreq->lock);
-	if (--dreq->outstanding) {
-		spin_unlock(&dreq->lock);
-		return;
-	}
-	spin_unlock(&dreq->lock);
-
-	nfs_direct_write_complete(dreq, data->inode);
+	if (put_dreq(dreq))
+		nfs_direct_write_complete(dreq, data->inode);
 }
 
 static const struct rpc_call_ops nfs_write_direct_ops = {
@@ -635,41 +594,62 @@
 };
 
 /*
- * For each nfs_write_data struct that was allocated on the list, dispatch
- * an NFS WRITE operation
+ * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE
+ * operation.  If nfs_writedata_alloc() or get_user_pages() fails,
+ * bail and stop sending more writes.  Write length accounting is
+ * handled automatically by nfs_direct_write_result().  Otherwise, if
+ * no requests have been sent, just return an error.
  */
-static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync)
+static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync)
 {
 	struct nfs_open_context *ctx = dreq->ctx;
 	struct inode *inode = ctx->dentry->d_inode;
-	struct list_head *list = &dreq->list;
-	struct page **pages = dreq->pages;
-	size_t count = dreq->user_count;
-	loff_t pos = dreq->pos;
 	size_t wsize = NFS_SERVER(inode)->wsize;
-	unsigned int curpage, pgbase;
+	unsigned int wpages = nfs_max_pages(wsize);
+	unsigned int pgbase;
+	int result;
+	ssize_t started = 0;
 
-	curpage = 0;
-	pgbase = dreq->user_addr & ~PAGE_MASK;
+	get_dreq(dreq);
+
+	pgbase = user_addr & ~PAGE_MASK;
 	do {
 		struct nfs_write_data *data;
 		size_t bytes;
 
+		result = -ENOMEM;
+		data = nfs_writedata_alloc(wpages);
+		if (unlikely(!data))
+			break;
+
 		bytes = wsize;
 		if (count < wsize)
 			bytes = count;
 
-		BUG_ON(list_empty(list));
-		data = list_entry(list->next, struct nfs_write_data, pages);
+		data->npages = nfs_direct_count_pages(user_addr, bytes);
+		down_read(&current->mm->mmap_sem);
+		result = get_user_pages(current, current->mm, user_addr,
+					data->npages, 0, 0, data->pagevec, NULL);
+		up_read(&current->mm->mmap_sem);
+		if (unlikely(result < data->npages)) {
+			if (result > 0)
+				nfs_direct_release_pages(data->pagevec, result);
+			nfs_writedata_release(data);
+			break;
+		}
+
+		get_dreq(dreq);
+
 		list_move_tail(&data->pages, &dreq->rewrite_list);
 
+		data->req = (struct nfs_page *) dreq;
 		data->inode = inode;
 		data->cred = ctx->cred;
 		data->args.fh = NFS_FH(inode);
 		data->args.context = ctx;
 		data->args.offset = pos;
 		data->args.pgbase = pgbase;
-		data->args.pages = &pages[curpage];
+		data->args.pages = data->pagevec;
 		data->args.count = bytes;
 		data->res.fattr = &data->fattr;
 		data->res.count = bytes;
@@ -693,19 +673,26 @@
 				bytes,
 				(unsigned long long)data->args.offset);
 
+		started += bytes;
+		user_addr += bytes;
 		pos += bytes;
 		pgbase += bytes;
-		curpage += pgbase >> PAGE_SHIFT;
 		pgbase &= ~PAGE_MASK;
 
 		count -= bytes;
 	} while (count != 0);
-	BUG_ON(!list_empty(list));
+
+	if (put_dreq(dreq))
+		nfs_direct_write_complete(dreq, inode);
+
+	if (started)
+		return 0;
+	return result < 0 ? (ssize_t) result : -EFAULT;
 }
 
-static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos, struct page **pages, int nr_pages)
+static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
 {
-	ssize_t result;
+	ssize_t result = 0;
 	sigset_t oldset;
 	struct inode *inode = iocb->ki_filp->f_mapping->host;
 	struct rpc_clnt *clnt = NFS_CLIENT(inode);
@@ -713,17 +700,14 @@
 	size_t wsize = NFS_SERVER(inode)->wsize;
 	int sync = 0;
 
-	dreq = nfs_direct_write_alloc(count, wsize);
+	dreq = nfs_direct_req_alloc();
 	if (!dreq)
 		return -ENOMEM;
+	nfs_alloc_commit_data(dreq);
+
 	if (dreq->commit_data == NULL || count < wsize)
 		sync = FLUSH_STABLE;
 
-	dreq->user_addr = user_addr;
-	dreq->user_count = count;
-	dreq->pos = pos;
-	dreq->pages = pages;
-	dreq->npages = nr_pages;
 	dreq->inode = inode;
 	dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data);
 	if (!is_sync_kiocb(iocb))
@@ -734,8 +718,9 @@
 	nfs_begin_data_update(inode);
 
 	rpc_clnt_sigmask(clnt, &oldset);
-	nfs_direct_write_schedule(dreq, sync);
-	result = nfs_direct_wait(dreq);
+	result = nfs_direct_write_schedule(dreq, user_addr, count, pos, sync);
+	if (!result)
+		result = nfs_direct_wait(dreq);
 	rpc_clnt_sigunmask(clnt, &oldset);
 
 	return result;
@@ -765,8 +750,6 @@
 ssize_t nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t pos)
 {
 	ssize_t retval = -EINVAL;
-	int page_count;
-	struct page **pages;
 	struct file *file = iocb->ki_filp;
 	struct address_space *mapping = file->f_mapping;
 
@@ -788,14 +771,7 @@
 	if (retval)
 		goto out;
 
-	retval = nfs_get_user_pages(READ, (unsigned long) buf,
-						count, &pages);
-	if (retval < 0)
-		goto out;
-	page_count = retval;
-
-	retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos,
-						pages, page_count);
+	retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos);
 	if (retval > 0)
 		iocb->ki_pos = pos + retval;
 
@@ -831,8 +807,6 @@
 ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos)
 {
 	ssize_t retval;
-	int page_count;
-	struct page **pages;
 	struct file *file = iocb->ki_filp;
 	struct address_space *mapping = file->f_mapping;
 
@@ -860,14 +834,7 @@
 	if (retval)
 		goto out;
 
-	retval = nfs_get_user_pages(WRITE, (unsigned long) buf,
-						count, &pages);
-	if (retval < 0)
-		goto out;
-	page_count = retval;
-
-	retval = nfs_direct_write(iocb, (unsigned long) buf, count,
-					pos, pages, page_count);
+	retval = nfs_direct_write(iocb, (unsigned long) buf, count, pos);
 
 	/*
 	 * XXX: nfs_end_data_update() already ensures this file's
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index b4916b0..e6ee97f 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3144,9 +3144,6 @@
 		default:
 			BUG();
 	}
-	if (res < 0)
-		printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
-				__FUNCTION__);
 	return res;
 }
 
@@ -3258,8 +3255,6 @@
 		return ERR_PTR(-ENOMEM);
 	}
 
-	/* Unlock _before_ we do the RPC call */
-	do_vfs_lock(fl->fl_file, fl);
 	return rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data);
 }
 
@@ -3270,30 +3265,28 @@
 	struct rpc_task *task;
 	int status = 0;
 
+	status = nfs4_set_lock_state(state, request);
+	/* Unlock _before_ we do the RPC call */
+	request->fl_flags |= FL_EXISTS;
+	if (do_vfs_lock(request->fl_file, request) == -ENOENT)
+		goto out;
+	if (status != 0)
+		goto out;
 	/* Is this a delegated lock? */
 	if (test_bit(NFS_DELEGATED_STATE, &state->flags))
-		goto out_unlock;
-	/* Is this open_owner holding any locks on the server? */
-	if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
-		goto out_unlock;
-
-	status = nfs4_set_lock_state(state, request);
-	if (status != 0)
-		goto out_unlock;
+		goto out;
 	lsp = request->fl_u.nfs4_fl.owner;
-	status = -ENOMEM;
 	seqid = nfs_alloc_seqid(&lsp->ls_seqid);
+	status = -ENOMEM;
 	if (seqid == NULL)
-		goto out_unlock;
+		goto out;
 	task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid);
 	status = PTR_ERR(task);
 	if (IS_ERR(task))
-		goto out_unlock;
+		goto out;
 	status = nfs4_wait_for_completion_rpc_task(task);
 	rpc_release_task(task);
-	return status;
-out_unlock:
-	do_vfs_lock(request->fl_file, request);
+out:
 	return status;
 }
 
@@ -3461,10 +3454,10 @@
 	struct nfs4_exception exception = { };
 	int err;
 
-	/* Cache the lock if possible... */
-	if (test_bit(NFS_DELEGATED_STATE, &state->flags))
-		return 0;
 	do {
+		/* Cache the lock if possible... */
+		if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
+			return 0;
 		err = _nfs4_do_setlk(state, F_SETLK, request, 1);
 		if (err != -NFS4ERR_DELAY)
 			break;
@@ -3483,6 +3476,8 @@
 	if (err != 0)
 		return err;
 	do {
+		if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
+			return 0;
 		err = _nfs4_do_setlk(state, F_SETLK, request, 0);
 		if (err != -NFS4ERR_DELAY)
 			break;
@@ -3494,29 +3489,42 @@
 static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
 	struct nfs4_client *clp = state->owner->so_client;
+	unsigned char fl_flags = request->fl_flags;
 	int status;
 
 	/* Is this a delegated open? */
-	if (NFS_I(state->inode)->delegation_state != 0) {
-		/* Yes: cache locks! */
-		status = do_vfs_lock(request->fl_file, request);
-		/* ...but avoid races with delegation recall... */
-		if (status < 0 || test_bit(NFS_DELEGATED_STATE, &state->flags))
-			return status;
-	}
-	down_read(&clp->cl_sem);
 	status = nfs4_set_lock_state(state, request);
 	if (status != 0)
 		goto out;
+	request->fl_flags |= FL_ACCESS;
+	status = do_vfs_lock(request->fl_file, request);
+	if (status < 0)
+		goto out;
+	down_read(&clp->cl_sem);
+	if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+		struct nfs_inode *nfsi = NFS_I(state->inode);
+		/* Yes: cache locks! */
+		down_read(&nfsi->rwsem);
+		/* ...but avoid races with delegation recall... */
+		if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+			request->fl_flags = fl_flags & ~FL_SLEEP;
+			status = do_vfs_lock(request->fl_file, request);
+			up_read(&nfsi->rwsem);
+			goto out_unlock;
+		}
+		up_read(&nfsi->rwsem);
+	}
 	status = _nfs4_do_setlk(state, cmd, request, 0);
 	if (status != 0)
-		goto out;
+		goto out_unlock;
 	/* Note: we always want to sleep here! */
-	request->fl_flags |= FL_SLEEP;
+	request->fl_flags = fl_flags | FL_SLEEP;
 	if (do_vfs_lock(request->fl_file, request) < 0)
 		printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
-out:
+out_unlock:
 	up_read(&clp->cl_sem);
+out:
+	request->fl_flags = fl_flags;
 	return status;
 }
 
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index bca5734..86bac6a 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -578,7 +578,7 @@
 	return ret;
 }
 
-static void nfs_cancel_requests(struct list_head *head)
+static void nfs_cancel_dirty_list(struct list_head *head)
 {
 	struct nfs_page *req;
 	while(!list_empty(head)) {
@@ -589,6 +589,19 @@
 	}
 }
 
+static void nfs_cancel_commit_list(struct list_head *head)
+{
+	struct nfs_page *req;
+
+	while(!list_empty(head)) {
+		req = nfs_list_entry(head->next);
+		nfs_list_remove_request(req);
+		nfs_inode_remove_request(req);
+		nfs_clear_page_writeback(req);
+		dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
+	}
+}
+
 /*
  * nfs_scan_dirty - Scan an inode for dirty requests
  * @inode: NFS inode to scan
@@ -1381,6 +1394,7 @@
 		nfs_list_remove_request(req);
 		nfs_mark_request_commit(req);
 		nfs_clear_page_writeback(req);
+		dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
 	}
 	return -ENOMEM;
 }
@@ -1499,7 +1513,7 @@
 		if (pages != 0) {
 			spin_unlock(&nfsi->req_lock);
 			if (how & FLUSH_INVALIDATE)
-				nfs_cancel_requests(&head);
+				nfs_cancel_dirty_list(&head);
 			else
 				ret = nfs_flush_list(inode, &head, pages, how);
 			spin_lock(&nfsi->req_lock);
@@ -1512,7 +1526,7 @@
 			break;
 		if (how & FLUSH_INVALIDATE) {
 			spin_unlock(&nfsi->req_lock);
-			nfs_cancel_requests(&head);
+			nfs_cancel_commit_list(&head);
 			spin_lock(&nfsi->req_lock);
 			continue;
 		}
diff --git a/include/asm-arm/arch-at91rm9200/irqs.h b/include/asm-arm/arch-at91rm9200/irqs.h
index 2dc93b1..f63842c 100644
--- a/include/asm-arm/arch-at91rm9200/irqs.h
+++ b/include/asm-arm/arch-at91rm9200/irqs.h
@@ -39,12 +39,4 @@
  */
 #define	NR_IRQS		(NR_AIC_IRQS + (4 * 32))
 
-
-#ifndef __ASSEMBLY__
-/*
- * Initialize the IRQ controller.
- */
-extern void at91rm9200_init_irq(unsigned int priority[]);
-#endif
-
 #endif
diff --git a/include/asm-sparc64/dma-mapping.h b/include/asm-sparc64/dma-mapping.h
index 0f5b89c..27c46fb 100644
--- a/include/asm-sparc64/dma-mapping.h
+++ b/include/asm-sparc64/dma-mapping.h
@@ -160,6 +160,20 @@
 	BUG();
 }
 
+static inline void
+dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
+			enum dma_data_direction direction)
+{
+	BUG();
+}
+
+static inline void
+dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size,
+			   enum dma_data_direction direction)
+{
+	BUG();
+}
+
 #endif /* PCI */
 
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 134b320..43aef9b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -716,6 +716,7 @@
 #define FL_POSIX	1
 #define FL_FLOCK	2
 #define FL_ACCESS	8	/* not trying to lock, just looking */
+#define FL_EXISTS	16	/* when unlocking, test for existence */
 #define FL_LEASE	32	/* lease held on this file */
 #define FL_CLOSE	64	/* unlock on close */
 #define FL_SLEEP	128	/* A blocking lock */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index f4284bf..6cc497a 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -131,6 +131,7 @@
 	ATA_DFLAG_CFG_MASK	= (1 << 8) - 1,
 
 	ATA_DFLAG_PIO		= (1 << 8), /* device currently in PIO mode */
+	ATA_DFLAG_SUSPENDED	= (1 << 9), /* device suspended */
 	ATA_DFLAG_INIT_MASK	= (1 << 16) - 1,
 
 	ATA_DFLAG_DETACH	= (1 << 16),
@@ -160,22 +161,28 @@
 	ATA_FLAG_HRST_TO_RESUME	= (1 << 11), /* hardreset to resume phy */
 	ATA_FLAG_SKIP_D2H_BSY	= (1 << 12), /* can't wait for the first D2H
 					      * Register FIS clearing BSY */
-
 	ATA_FLAG_DEBUGMSG	= (1 << 13),
-	ATA_FLAG_FLUSH_PORT_TASK = (1 << 14), /* flush port task */
 
-	ATA_FLAG_EH_PENDING	= (1 << 15), /* EH pending */
-	ATA_FLAG_EH_IN_PROGRESS	= (1 << 16), /* EH in progress */
-	ATA_FLAG_FROZEN		= (1 << 17), /* port is frozen */
-	ATA_FLAG_RECOVERED	= (1 << 18), /* recovery action performed */
-	ATA_FLAG_LOADING	= (1 << 19), /* boot/loading probe */
-	ATA_FLAG_UNLOADING	= (1 << 20), /* module is unloading */
-	ATA_FLAG_SCSI_HOTPLUG	= (1 << 21), /* SCSI hotplug scheduled */
+	/* The following flag belongs to ap->pflags but is kept in
+	 * ap->flags because it's referenced in many LLDs and will be
+	 * removed in not-too-distant future.
+	 */
+	ATA_FLAG_DISABLED	= (1 << 23), /* port is disabled, ignore it */
 
-	ATA_FLAG_DISABLED	= (1 << 22), /* port is disabled, ignore it */
-	ATA_FLAG_SUSPENDED	= (1 << 23), /* port is suspended (power) */
+	/* bits 24:31 of ap->flags are reserved for LLD specific flags */
 
-	/* bits 24:31 of ap->flags are reserved for LLDD specific flags */
+	/* struct ata_port pflags */
+	ATA_PFLAG_EH_PENDING	= (1 << 0), /* EH pending */
+	ATA_PFLAG_EH_IN_PROGRESS = (1 << 1), /* EH in progress */
+	ATA_PFLAG_FROZEN	= (1 << 2), /* port is frozen */
+	ATA_PFLAG_RECOVERED	= (1 << 3), /* recovery action performed */
+	ATA_PFLAG_LOADING	= (1 << 4), /* boot/loading probe */
+	ATA_PFLAG_UNLOADING	= (1 << 5), /* module is unloading */
+	ATA_PFLAG_SCSI_HOTPLUG	= (1 << 6), /* SCSI hotplug scheduled */
+
+	ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */
+	ATA_PFLAG_SUSPENDED	= (1 << 17), /* port is suspended (power) */
+	ATA_PFLAG_PM_PENDING	= (1 << 18), /* PM operation pending */
 
 	/* struct ata_queued_cmd flags */
 	ATA_QCFLAG_ACTIVE	= (1 << 0), /* cmd not yet ack'd to scsi lyer */
@@ -248,12 +255,19 @@
 	ATA_EH_REVALIDATE	= (1 << 0),
 	ATA_EH_SOFTRESET	= (1 << 1),
 	ATA_EH_HARDRESET	= (1 << 2),
+	ATA_EH_SUSPEND		= (1 << 3),
+	ATA_EH_RESUME		= (1 << 4),
+	ATA_EH_PM_FREEZE	= (1 << 5),
 
 	ATA_EH_RESET_MASK	= ATA_EH_SOFTRESET | ATA_EH_HARDRESET,
-	ATA_EH_PERDEV_MASK	= ATA_EH_REVALIDATE,
+	ATA_EH_PERDEV_MASK	= ATA_EH_REVALIDATE | ATA_EH_SUSPEND |
+				  ATA_EH_RESUME | ATA_EH_PM_FREEZE,
 
 	/* ata_eh_info->flags */
 	ATA_EHI_HOTPLUGGED	= (1 << 0),  /* could have been hotplugged */
+	ATA_EHI_RESUME_LINK	= (1 << 1),  /* need to resume link */
+	ATA_EHI_NO_AUTOPSY	= (1 << 2),  /* no autopsy */
+	ATA_EHI_QUIET		= (1 << 3),  /* be quiet */
 
 	ATA_EHI_DID_RESET	= (1 << 16), /* already reset this port */
 
@@ -486,6 +500,7 @@
 	const struct ata_port_operations *ops;
 	spinlock_t		*lock;
 	unsigned long		flags;	/* ATA_FLAG_xxx */
+	unsigned int		pflags; /* ATA_PFLAG_xxx */
 	unsigned int		id;	/* unique id req'd by scsi midlyr */
 	unsigned int		port_no; /* unique port #; from zero */
 	unsigned int		hard_port_no;	/* hardware port #; from zero */
@@ -535,6 +550,9 @@
 	struct list_head	eh_done_q;
 	wait_queue_head_t	eh_wait_q;
 
+	pm_message_t		pm_mesg;
+	int			*pm_result;
+
 	void			*private_data;
 
 	u8			sector_buf[ATA_SECT_SIZE]; /* owned by EH */
@@ -589,6 +607,9 @@
 	void (*scr_write) (struct ata_port *ap, unsigned int sc_reg,
 			   u32 val);
 
+	int (*port_suspend) (struct ata_port *ap, pm_message_t mesg);
+	int (*port_resume) (struct ata_port *ap);
+
 	int (*port_start) (struct ata_port *ap);
 	void (*port_stop) (struct ata_port *ap);
 
@@ -622,9 +643,18 @@
 
 #define FIT(v,vmin,vmax)	max_t(short,min_t(short,v,vmax),vmin)
 
-extern const unsigned long sata_deb_timing_boot[];
-extern const unsigned long sata_deb_timing_eh[];
-extern const unsigned long sata_deb_timing_before_fsrst[];
+extern const unsigned long sata_deb_timing_normal[];
+extern const unsigned long sata_deb_timing_hotplug[];
+extern const unsigned long sata_deb_timing_long[];
+
+static inline const unsigned long *
+sata_ehc_deb_timing(struct ata_eh_context *ehc)
+{
+	if (ehc->i.flags & ATA_EHI_HOTPLUGGED)
+		return sata_deb_timing_hotplug;
+	else
+		return sata_deb_timing_normal;
+}
 
 extern void ata_port_probe(struct ata_port *);
 extern void __sata_phy_reset(struct ata_port *ap);
@@ -644,6 +674,8 @@
 extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info,
 			     unsigned int n_ports);
 extern void ata_pci_remove_one (struct pci_dev *pdev);
+extern void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state);
+extern void ata_pci_device_do_resume(struct pci_dev *pdev);
 extern int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state);
 extern int ata_pci_device_resume(struct pci_dev *pdev);
 extern int ata_pci_clear_simplex(struct pci_dev *pdev);
@@ -664,8 +696,9 @@
 extern int ata_port_offline(struct ata_port *ap);
 extern int ata_scsi_device_resume(struct scsi_device *);
 extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t state);
-extern int ata_device_resume(struct ata_device *);
-extern int ata_device_suspend(struct ata_device *, pm_message_t state);
+extern int ata_host_set_suspend(struct ata_host_set *host_set,
+				pm_message_t mesg);
+extern void ata_host_set_resume(struct ata_host_set *host_set);
 extern int ata_ratelimit(void);
 extern unsigned int ata_busy_sleep(struct ata_port *ap,
 				   unsigned long timeout_pat,
@@ -825,19 +858,24 @@
 	(ehi)->desc_len = 0; \
 } while (0)
 
-static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi)
+static inline void __ata_ehi_hotplugged(struct ata_eh_info *ehi)
 {
 	if (ehi->flags & ATA_EHI_HOTPLUGGED)
 		return;
 
-	ehi->flags |= ATA_EHI_HOTPLUGGED;
+	ehi->flags |= ATA_EHI_HOTPLUGGED | ATA_EHI_RESUME_LINK;
 	ehi->hotplug_timestamp = jiffies;
 
-	ehi->err_mask |= AC_ERR_ATA_BUS;
 	ehi->action |= ATA_EH_SOFTRESET;
 	ehi->probe_mask |= (1 << ATA_MAX_DEVICES) - 1;
 }
 
+static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi)
+{
+	__ata_ehi_hotplugged(ehi);
+	ehi->err_mask |= AC_ERR_ATA_BUS;
+}
+
 /*
  * qc helpers
  */
@@ -921,6 +959,11 @@
 	return ata_class_absent(dev->class);
 }
 
+static inline unsigned int ata_dev_ready(const struct ata_device *dev)
+{
+	return ata_dev_enabled(dev) && !(dev->flags & ATA_DFLAG_SUSPENDED);
+}
+
 /*
  * port helpers
  */
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 7c7320f..2d3fb64 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -729,6 +729,7 @@
 	struct list_head	pages;	/* Coalesced read requests */
 	struct nfs_page		*req;	/* multi ops per nfs_page */
 	struct page		**pagevec;
+	unsigned int		npages;	/* active pages in pagevec */
 	struct nfs_readargs args;
 	struct nfs_readres  res;
 #ifdef CONFIG_NFS_V4
@@ -747,6 +748,7 @@
 	struct list_head	pages;		/* Coalesced requests we wish to flush */
 	struct nfs_page		*req;		/* multi ops per nfs_page */
 	struct page		**pagevec;
+	unsigned int		npages;		/* active pages in pagevec */
 	struct nfs_writeargs	args;		/* argument struct */
 	struct nfs_writeres	res;		/* result struct */
 #ifdef CONFIG_NFS_V4
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 685081c..c09396d 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2019,6 +2019,13 @@
 #define PCI_VENDOR_ID_TDI               0x192E
 #define PCI_DEVICE_ID_TDI_EHCI          0x0101
 
+#define PCI_VENDOR_ID_JMICRON		0x197B
+#define PCI_DEVICE_ID_JMICRON_JMB360	0x2360
+#define PCI_DEVICE_ID_JMICRON_JMB361	0x2361
+#define PCI_DEVICE_ID_JMICRON_JMB363	0x2363
+#define PCI_DEVICE_ID_JMICRON_JMB365	0x2365
+#define PCI_DEVICE_ID_JMICRON_JMB366	0x2366
+#define PCI_DEVICE_ID_JMICRON_JMB368	0x2368
 
 #define PCI_VENDOR_ID_TEKRAM		0x1de1
 #define PCI_DEVICE_ID_TEKRAM_DC290	0xdc29
diff --git a/include/net/ieee80211softmac.h b/include/net/ieee80211softmac.h
index 7a483ab..00ad810 100644
--- a/include/net/ieee80211softmac.h
+++ b/include/net/ieee80211softmac.h
@@ -104,6 +104,7 @@
 	 */
 	u8 static_essid:1,
 	   associating:1,
+	   assoc_wait:1,
 	   bssvalid:1,
 	   bssfixed:1;
 
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
index 47ccf15..72d4d4e 100644
--- a/net/ieee80211/ieee80211_rx.c
+++ b/net/ieee80211/ieee80211_rx.c
@@ -368,6 +368,7 @@
 
 	/* Put this code here so that we avoid duplicating it in all
 	 * Rx paths. - Jean II */
+#ifdef CONFIG_WIRELESS_EXT
 #ifdef IW_WIRELESS_SPY		/* defined in iw_handler.h */
 	/* If spy monitoring on */
 	if (ieee->spy_data.spy_number > 0) {
@@ -396,15 +397,16 @@
 		wireless_spy_update(ieee->dev, hdr->addr2, &wstats);
 	}
 #endif				/* IW_WIRELESS_SPY */
+#endif				/* CONFIG_WIRELESS_EXT */
 
 #ifdef NOT_YET
 	hostap_update_rx_stats(local->ap, hdr, rx_stats);
 #endif
 
 	if (ieee->iw_mode == IW_MODE_MONITOR) {
-		ieee80211_monitor_rx(ieee, skb, rx_stats);
 		stats->rx_packets++;
 		stats->rx_bytes += skb->len;
+		ieee80211_monitor_rx(ieee, skb, rx_stats);
 		return 1;
 	}
 
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
index de148ae..bf04213 100644
--- a/net/ieee80211/ieee80211_tx.c
+++ b/net/ieee80211/ieee80211_tx.c
@@ -562,10 +562,13 @@
 	struct net_device_stats *stats = &ieee->stats;
 	struct sk_buff *skb_frag;
 	int priority = -1;
+	int fraglen = total_len;
+	int headroom = ieee->tx_headroom;
+	struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
 
 	spin_lock_irqsave(&ieee->lock, flags);
 
-	if (encrypt_mpdu && !ieee->sec.encrypt)
+	if (encrypt_mpdu && (!ieee->sec.encrypt || !crypt))
 		encrypt_mpdu = 0;
 
 	/* If there is no driver handler to take the TXB, dont' bother
@@ -581,20 +584,24 @@
 		goto success;
 	}
 
-	if (encrypt_mpdu)
+	if (encrypt_mpdu) {
 		frame->frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+		fraglen += crypt->ops->extra_mpdu_prefix_len +
+			   crypt->ops->extra_mpdu_postfix_len;
+		headroom += crypt->ops->extra_mpdu_prefix_len;
+	}
 
 	/* When we allocate the TXB we allocate enough space for the reserve
 	 * and full fragment bytes (bytes_per_frag doesn't include prefix,
 	 * postfix, header, FCS, etc.) */
-	txb = ieee80211_alloc_txb(1, total_len, ieee->tx_headroom, GFP_ATOMIC);
+	txb = ieee80211_alloc_txb(1, fraglen, headroom, GFP_ATOMIC);
 	if (unlikely(!txb)) {
 		printk(KERN_WARNING "%s: Could not allocate TXB\n",
 		       ieee->dev->name);
 		goto failed;
 	}
 	txb->encrypted = 0;
-	txb->payload_size = total_len;
+	txb->payload_size = fraglen;
 
 	skb_frag = txb->fragments[0];
 
diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c
index 5e9a906..44215ce6 100644
--- a/net/ieee80211/softmac/ieee80211softmac_assoc.c
+++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c
@@ -47,9 +47,7 @@
 	
 	dprintk(KERN_INFO PFX "sent association request!\n");
 
-	/* Change the state to associating */
 	spin_lock_irqsave(&mac->lock, flags);
-	mac->associnfo.associating = 1;
 	mac->associated = 0; /* just to make sure */
 
 	/* Set a timer for timeout */
@@ -63,6 +61,7 @@
 ieee80211softmac_assoc_timeout(void *d)
 {
 	struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d;
+	struct ieee80211softmac_network *n;
 	unsigned long flags;
 
 	spin_lock_irqsave(&mac->lock, flags);
@@ -75,11 +74,12 @@
 	mac->associnfo.associating = 0;
 	mac->associnfo.bssvalid = 0;
 	mac->associated = 0;
+
+	n = ieee80211softmac_get_network_by_bssid_locked(mac, mac->associnfo.bssid);
 	spin_unlock_irqrestore(&mac->lock, flags);
 
 	dprintk(KERN_INFO PFX "assoc request timed out!\n");
-	/* FIXME: we need to know the network here. that requires a bit of restructuring */
-	ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, NULL);
+	ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, n);
 }
 
 void
@@ -203,6 +203,10 @@
 	if (mac->associated)
 		ieee80211softmac_send_disassoc_req(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT);
 
+	spin_lock_irqsave(&mac->lock, flags);
+	mac->associnfo.associating = 1;
+	spin_unlock_irqrestore(&mac->lock, flags);
+
 	/* try to find the requested network in our list, if we found one already */
 	if (bssvalid || mac->associnfo.bssfixed)
 		found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);	
@@ -295,19 +299,32 @@
 	memcpy(mac->associnfo.associate_essid.data, found->essid.data, IW_ESSID_MAX_SIZE + 1);
 	
 	/* we found a network! authenticate (if necessary) and associate to it. */
-	if (!found->authenticated) {
+	if (found->authenticating) {
+		dprintk(KERN_INFO PFX "Already requested authentication, waiting...\n");
+		if(!mac->associnfo.assoc_wait) {
+			mac->associnfo.assoc_wait = 1;
+			ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL);
+		}
+		return;
+	}
+	if (!found->authenticated && !found->authenticating) {
 		/* This relies on the fact that _auth_req only queues the work,
 		 * otherwise adding the notification would be racy. */
 		if (!ieee80211softmac_auth_req(mac, found)) {
-			dprintk(KERN_INFO PFX "cannot associate without being authenticated, requested authentication\n");
-			ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL);
+			if(!mac->associnfo.assoc_wait) {
+				dprintk(KERN_INFO PFX "Cannot associate without being authenticated, requested authentication\n");
+				mac->associnfo.assoc_wait = 1;
+				ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL);
+			}
 		} else {
 			printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n");
+			mac->associnfo.assoc_wait = 0;
 			ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found);
 		}
 		return;
 	}
 	/* finally! now we can start associating */
+	mac->associnfo.assoc_wait = 0;
 	ieee80211softmac_assoc(mac, found);
 }
 
diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c
index 90b8484..ebc33ca 100644
--- a/net/ieee80211/softmac/ieee80211softmac_auth.c
+++ b/net/ieee80211/softmac/ieee80211softmac_auth.c
@@ -36,8 +36,9 @@
 	struct ieee80211softmac_auth_queue_item *auth;
 	unsigned long flags;
 	
-	if (net->authenticating)
+	if (net->authenticating || net->authenticated)
 		return 0;
+	net->authenticating = 1;
 
 	/* Add the network if it's not already added */
 	ieee80211softmac_add_network(mac, net);
@@ -92,7 +93,6 @@
 			return;
 		}
 		net->authenticated = 0;
-		net->authenticating = 1;
 		/* add a timeout call so we eventually give up waiting for an auth reply */
 		schedule_delayed_work(&auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT);
 		auth->retry--;
diff --git a/net/ieee80211/softmac/ieee80211softmac_io.c b/net/ieee80211/softmac/ieee80211softmac_io.c
index 0954161..8cc8b20 100644
--- a/net/ieee80211/softmac/ieee80211softmac_io.c
+++ b/net/ieee80211/softmac/ieee80211softmac_io.c
@@ -229,6 +229,9 @@
 		return 0;
 	ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_ASSOC_REQ, net->bssid, net->bssid);
 
+	/* Fill in the capabilities */
+	(*pkt)->capability = ieee80211softmac_capabilities(mac, net);
+
 	/* Fill in Listen Interval (?) */
 	(*pkt)->listen_interval = cpu_to_le16(10);
 	
diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c
index 0e65ff4..75320b6 100644
--- a/net/ieee80211/softmac/ieee80211softmac_wx.c
+++ b/net/ieee80211/softmac/ieee80211softmac_wx.c
@@ -70,12 +70,44 @@
 			      char *extra)
 {
 	struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+	struct ieee80211softmac_network *n;
+	struct ieee80211softmac_auth_queue_item *authptr;
 	int length = 0;
 	unsigned long flags;
-	
+
+	/* Check if we're already associating to this or another network
+	 * If it's another network, cancel and start over with our new network
+	 * If it's our network, ignore the change, we're already doing it!
+	 */
+	if((sm->associnfo.associating || sm->associated) &&
+	   (data->essid.flags && data->essid.length && extra)) {
+		/* Get the associating network */
+		n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid);
+		if(n && n->essid.len == (data->essid.length - 1) &&
+		   !memcmp(n->essid.data, extra, n->essid.len)) {
+			dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n",
+				MAC_ARG(sm->associnfo.bssid));
+			return 0;
+		} else {
+			dprintk(KERN_INFO PFX "Canceling existing associate request!\n");
+			spin_lock_irqsave(&sm->lock,flags);
+			/* Cancel assoc work */
+			cancel_delayed_work(&sm->associnfo.work);
+			/* We don't have to do this, but it's a little cleaner */
+			list_for_each_entry(authptr, &sm->auth_queue, list)
+				cancel_delayed_work(&authptr->work);
+			sm->associnfo.bssvalid = 0;
+			sm->associnfo.bssfixed = 0;
+			spin_unlock_irqrestore(&sm->lock,flags);
+			flush_scheduled_work();
+		}
+	}
+
+
 	spin_lock_irqsave(&sm->lock, flags);
-	
+
 	sm->associnfo.static_essid = 0;
+	sm->associnfo.assoc_wait = 0;
 
 	if (data->essid.flags && data->essid.length && extra /*required?*/) {
 		length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE);
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 49174f0..6ac4510 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -191,7 +191,6 @@
 	do {
 		/* Are any pointers crossing a page boundary? */
 		if (pgto_base == 0) {
-			flush_dcache_page(*pgto);
 			pgto_base = PAGE_CACHE_SIZE;
 			pgto--;
 		}
@@ -211,11 +210,11 @@
 		vto = kmap_atomic(*pgto, KM_USER0);
 		vfrom = kmap_atomic(*pgfrom, KM_USER1);
 		memmove(vto + pgto_base, vfrom + pgfrom_base, copy);
+		flush_dcache_page(*pgto);
 		kunmap_atomic(vfrom, KM_USER1);
 		kunmap_atomic(vto, KM_USER0);
 
 	} while ((len -= copy) != 0);
-	flush_dcache_page(*pgto);
 }
 
 /*