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
 
 
 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
 
--- /dev/null
+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.
+
 
--- /dev/null
+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
+
 
--- /dev/null
+/* 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, ®);
+       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;
+}
 
--- /dev/null
+/* 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 */
 
--- /dev/null
+/* 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 */
 
--- /dev/null
+/* 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;
+}
 
--- /dev/null
+#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 */
 
--- /dev/null
+/* 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;
+
+       /*
+        * 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 */
 
--- /dev/null
+/* 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 */
 
--- /dev/null
+/* 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);
+}
 
--- /dev/null
+/* 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 */
 
--- /dev/null
+/* 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;
+}
 
--- /dev/null
+/* 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 */
 
--- /dev/null
+/* 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;
+}
 
--- /dev/null
+/* 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;
+}
 
--- /dev/null
+/* 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 */
 
--- /dev/null
+/* 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 = ®s->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;
+}
 
--- /dev/null
+/* 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 */
 
--- /dev/null
+/* 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);
+}
 
--- /dev/null
+/* 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 */