Merge git://git.linux-nfs.org/pub/linux/nfs-2.6

* git://git.linux-nfs.org/pub/linux/nfs-2.6:
  NLM,NFSv4: Wait on local locks before we put RPC calls on the wire
  VFS: Add support for the FL_ACCESS flag to flock_lock_file()
  NFSv4: Ensure nfs4_lock_expired() caches delegated locks
  NLM,NFSv4: Don't put UNLOCK requests on the wire unless we hold a lock
  VFS: Allow caller to determine if BSD or posix locks were actually freed
  NFS: Optimise away an excessive GETATTR call when a file is symlinked
  This fixes a panic doing the first READDIR or READDIRPLUS call when:
  NFS: Fix NFS page_state usage
  Revert "Merge branch 'odirect'"
diff --git a/MAINTAINERS b/MAINTAINERS
index 5f76a4f..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/Makefile b/Makefile
index 11a850c..82f76a9 100644
--- a/Makefile
+++ b/Makefile
@@ -528,7 +528,7 @@
 
 ifdef INSTALL_MOD_STRIP
 ifeq ($(INSTALL_MOD_STRIP),1)
-mod_strip_cmd = $STRIP) --strip-debug
+mod_strip_cmd = $(STRIP) --strip-debug
 else
 mod_strip_cmd = $(STRIP) $(INSTALL_MOD_STRIP)
 endif # INSTALL_MOD_STRIP=1
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/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 7d31d7c..9ceceba 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -405,20 +405,22 @@
 	unsigned int loops = 100000;
 	struct mpic *mpic = mpic_from_irq(irq);
 	unsigned int src = mpic_irq_to_hw(irq);
+	unsigned long flags;
 
 	DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src);
 
+	spin_lock_irqsave(&mpic_lock, flags);
 	mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
 		       mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) &
 		       ~MPIC_VECPRI_MASK);
-
 	/* make sure mask gets to controller before we return to user */
 	do {
 		if (!loops--) {
 			printk(KERN_ERR "mpic_enable_irq timeout\n");
 			break;
 		}
-	} while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK);	
+	} while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK);
+	spin_unlock_irqrestore(&mpic_lock, flags);
 }
 
 static void mpic_mask_irq(unsigned int irq)
@@ -426,9 +428,11 @@
 	unsigned int loops = 100000;
 	struct mpic *mpic = mpic_from_irq(irq);
 	unsigned int src = mpic_irq_to_hw(irq);
+	unsigned long flags;
 
 	DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src);
 
+	spin_lock_irqsave(&mpic_lock, flags);
 	mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
 		       mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) |
 		       MPIC_VECPRI_MASK);
@@ -440,6 +444,7 @@
 			break;
 		}
 	} while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK));
+	spin_unlock_irqrestore(&mpic_lock, flags);
 }
 
 static void mpic_end_irq(unsigned int irq)
@@ -624,9 +629,10 @@
 	struct irq_desc *desc = get_irq_desc(virq);
 	struct irq_chip *chip;
 	struct mpic *mpic = h->host_data;
-	unsigned int vecpri = MPIC_VECPRI_SENSE_LEVEL |
+	u32 v, vecpri = MPIC_VECPRI_SENSE_LEVEL |
 		MPIC_VECPRI_POLARITY_NEGATIVE;
 	int level;
+	unsigned long iflags;
 
 	pr_debug("mpic: map virq %d, hwirq 0x%lx, flags: 0x%x\n",
 		 virq, hw, flags);
@@ -668,11 +674,21 @@
 	}
 #endif
 
-	/* Reconfigure irq */
-	vecpri |= MPIC_VECPRI_MASK | hw | (8 << MPIC_VECPRI_PRIORITY_SHIFT);
-	mpic_irq_write(hw, MPIC_IRQ_VECTOR_PRI, vecpri);
+	/* Reconfigure irq. We must preserve the mask bit as we can be called
+	 * while the interrupt is still active (This may change in the future
+	 * but for now, it is the case).
+	 */
+	spin_lock_irqsave(&mpic_lock, iflags);
+	v = mpic_irq_read(hw, MPIC_IRQ_VECTOR_PRI);
+	vecpri = (v &
+		~(MPIC_VECPRI_POLARITY_MASK | MPIC_VECPRI_SENSE_MASK)) |
+		vecpri;
+	if (vecpri != v)
+		mpic_irq_write(hw, MPIC_IRQ_VECTOR_PRI, vecpri);
+	spin_unlock_irqrestore(&mpic_lock, iflags);
 
-	pr_debug("mpic: mapping as IRQ\n");
+	pr_debug("mpic: mapping as IRQ, vecpri = 0x%08x (was 0x%08x)\n",
+		 vecpri, v);
 
 	set_irq_chip_data(virq, mpic);
 	set_irq_chip_and_handler(virq, chip, handle_fasteoi_irq);
@@ -904,8 +920,8 @@
 		
 		/* do senses munging */
 		if (mpic->senses && i < mpic->senses_count)
-			vecpri = mpic_flags_to_vecpri(mpic->senses[i],
-						      &level);
+			vecpri |= mpic_flags_to_vecpri(mpic->senses[i],
+						       &level);
 		else
 			vecpri |= MPIC_VECPRI_SENSE_LEVEL;
 
@@ -955,14 +971,17 @@
 
 void __init mpic_set_serial_int(struct mpic *mpic, int enable)
 {
+	unsigned long flags;
 	u32 v;
 
+	spin_lock_irqsave(&mpic_lock, flags);
 	v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1);
 	if (enable)
 		v |= MPIC_GREG_GLOBAL_CONF_1_SIE;
 	else
 		v &= ~MPIC_GREG_GLOBAL_CONF_1_SIE;
 	mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v);
+	spin_unlock_irqrestore(&mpic_lock, flags);
 }
 
 void mpic_irq_set_priority(unsigned int irq, unsigned int pri)
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..4daafe30 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..5c36e29 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..8459a18 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/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/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-powerpc/cputime.h b/include/asm-powerpc/cputime.h
index a21185d..3108044 100644
--- a/include/asm-powerpc/cputime.h
+++ b/include/asm-powerpc/cputime.h
@@ -43,6 +43,7 @@
 
 #define cputime64_zero			((cputime64_t)0)
 #define cputime64_add(__a, __b)		((__a) + (__b))
+#define cputime64_sub(__a, __b)		((__a) - (__b))
 #define cputime_to_cputime64(__ct)	(__ct)
 
 #ifdef __KERNEL__
@@ -74,6 +75,23 @@
 	return ct;
 }
 
+static inline cputime64_t jiffies64_to_cputime64(const u64 jif)
+{
+	cputime_t ct;
+	u64 sec;
+
+	/* have to be a little careful about overflow */
+	ct = jif % HZ;
+	sec = jif / HZ;
+	if (ct) {
+		ct *= tb_ticks_per_sec;
+		do_div(ct, HZ);
+	}
+	if (sec)
+		ct += (cputime_t) sec * tb_ticks_per_sec;
+	return ct;
+}
+
 static inline u64 cputime64_to_jiffies64(const cputime_t ct)
 {
 	return mulhdu(ct, __cputime_jiffies_factor);
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..44215ce 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);