diff options
Diffstat (limited to 'drivers/net/wireless/zd1211rw')
20 files changed, 6909 insertions, 0 deletions
diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig new file mode 100644 index 000000000000..66ed55bc5460 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/Kconfig @@ -0,0 +1,19 @@ +config ZD1211RW + tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" + depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + select FW_LOADER + ---help--- + This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless + chip, present in many USB-wireless adapters. + + Device firmware is required alongside this driver. You can download the + firmware distribution from http://zd1211.ath.cx/get-firmware + +config ZD1211RW_DEBUG + bool "ZyDAS ZD1211 debugging" + depends on ZD1211RW + ---help--- + ZD1211 debugging messages. Choosing Y will result in additional debug + messages being saved to your kernel logs, which may help debug any + problems. + diff --git a/drivers/net/wireless/zd1211rw/Makefile b/drivers/net/wireless/zd1211rw/Makefile new file mode 100644 index 000000000000..500314fc74d2 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_ZD1211RW) += zd1211rw.o + +zd1211rw-objs := zd_chip.o zd_ieee80211.o \ + zd_mac.o zd_netdev.o \ + zd_rf_al2230.o zd_rf_rf2959.o \ + zd_rf.o zd_usb.o zd_util.o + +ifeq ($(CONFIG_ZD1211RW_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c new file mode 100644 index 000000000000..efc9c4bd826f --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -0,0 +1,1615 @@ +/* zd_chip.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* This file implements all the hardware specific functions for the ZD1211 + * and ZD1211B chips. Support for the ZD1211B was possible after Timothy + * Legge sent me a ZD1211B device. Thank you Tim. -- Uli + */ + +#include <linux/kernel.h> +#include <linux/errno.h> + +#include "zd_def.h" +#include "zd_chip.h" +#include "zd_ieee80211.h" +#include "zd_mac.h" +#include "zd_rf.h" +#include "zd_util.h" + +void zd_chip_init(struct zd_chip *chip, + struct net_device *netdev, + struct usb_interface *intf) +{ + memset(chip, 0, sizeof(*chip)); + mutex_init(&chip->mutex); + zd_usb_init(&chip->usb, netdev, intf); + zd_rf_init(&chip->rf); +} + +void zd_chip_clear(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_clear(&chip->usb); + zd_rf_clear(&chip->rf); + mutex_unlock(&chip->mutex); + mutex_destroy(&chip->mutex); + memset(chip, 0, sizeof(*chip)); +} + +static int scnprint_mac_oui(const u8 *addr, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%02x-%02x-%02x", + addr[0], addr[1], addr[2]); +} + +/* Prints an identifier line, which will support debugging. */ +static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size) +{ + int i = 0; + + i = scnprintf(buffer, size, "zd1211%s chip ", + chip->is_zd1211b ? "b" : ""); + i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += scnprint_mac_oui(chip->e2p_mac, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c", chip->pa_type, + chip->patch_cck_gain ? 'g' : '-', + chip->patch_cr157 ? '7' : '-', + chip->patch_6m_band_edge ? '6' : '-'); + return i; +} + +static void print_id(struct zd_chip *chip) +{ + char buffer[80]; + + scnprint_id(chip, buffer, sizeof(buffer)); + buffer[sizeof(buffer)-1] = 0; + dev_info(zd_chip_dev(chip), "%s\n", buffer); +} + +/* Read a variable number of 32-bit values. Parameter count is not allowed to + * exceed USB_MAX_IOREAD32_COUNT. + */ +int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr, + unsigned int count) +{ + int r; + int i; + zd_addr_t *a16 = (zd_addr_t *)NULL; + u16 *v16; + unsigned int count16; + + if (count > USB_MAX_IOREAD32_COUNT) + return -EINVAL; + + /* Allocate a single memory block for values and addresses. */ + count16 = 2*count; + a16 = (zd_addr_t *)kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)), + GFP_NOFS); + if (!a16) { + dev_dbg_f(zd_chip_dev(chip), + "error ENOMEM in allocation of a16\n"); + r = -ENOMEM; + goto out; + } + v16 = (u16 *)(a16 + count16); + + for (i = 0; i < count; i++) { + int j = 2*i; + /* We read the high word always first. */ + a16[j] = zd_inc_word(addr[i]); + a16[j+1] = addr[i]; + } + + r = zd_ioread16v_locked(chip, v16, a16, count16); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error: zd_ioread16v_locked. Error number %d\n", r); + goto out; + } + + for (i = 0; i < count; i++) { + int j = 2*i; + values[i] = (v16[j] << 16) | v16[j+1]; + } + +out: + kfree((void *)a16); + return r; +} + +int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int i, j, r; + struct zd_ioreq16 *ioreqs16; + unsigned int count16; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + + if (count == 0) + return 0; + if (count > USB_MAX_IOWRITE32_COUNT) + return -EINVAL; + + /* Allocate a single memory block for values and addresses. */ + count16 = 2*count; + ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_NOFS); + if (!ioreqs16) { + r = -ENOMEM; + dev_dbg_f(zd_chip_dev(chip), + "error %d in ioreqs16 allocation\n", r); + goto out; + } + + for (i = 0; i < count; i++) { + j = 2*i; + /* We write the high word always first. */ + ioreqs16[j].value = ioreqs[i].value >> 16; + ioreqs16[j].addr = zd_inc_word(ioreqs[i].addr); + ioreqs16[j+1].value = ioreqs[i].value; + ioreqs16[j+1].addr = ioreqs[i].addr; + } + + r = zd_usb_iowrite16v(&chip->usb, ioreqs16, count16); +#ifdef DEBUG + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error %d in zd_usb_write16v\n", r); + } +#endif /* DEBUG */ +out: + kfree(ioreqs16); + return r; +} + +int zd_iowrite16a_locked(struct zd_chip *chip, + const struct zd_ioreq16 *ioreqs, unsigned int count) +{ + int r; + unsigned int i, j, t, max; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + for (i = 0; i < count; i += j + t) { + t = 0; + max = count-i; + if (max > USB_MAX_IOWRITE16_COUNT) + max = USB_MAX_IOWRITE16_COUNT; + for (j = 0; j < max; j++) { + if (!ioreqs[i+j].addr) { + t = 1; + break; + } + } + + r = zd_usb_iowrite16v(&chip->usb, &ioreqs[i], j); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error zd_usb_iowrite16v. Error number %d\n", + r); + return r; + } + } + + return 0; +} + +/* Writes a variable number of 32 bit registers. The functions will split + * that in several USB requests. A split can be forced by inserting an IO + * request with an zero address field. + */ +int zd_iowrite32a_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, unsigned int count) +{ + int r; + unsigned int i, j, t, max; + + for (i = 0; i < count; i += j + t) { + t = 0; + max = count-i; + if (max > USB_MAX_IOWRITE32_COUNT) + max = USB_MAX_IOWRITE32_COUNT; + for (j = 0; j < max; j++) { + if (!ioreqs[i+j].addr) { + t = 1; + break; + } + } + + r = _zd_iowrite32v_locked(chip, &ioreqs[i], j); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error _zd_iowrite32v_locked." + " Error number %d\n", r); + return r; + } + } + + return 0; +} + +int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_ioread16_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_ioread32_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_iowrite16_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, + u32 *values, unsigned int count) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_ioread32v_locked(chip, values, addresses, count); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, ioreqs, count); + mutex_unlock(&chip->mutex); + return r; +} + +static int read_pod(struct zd_chip *chip, u8 *rf_type) +{ + int r; + u32 value; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &value, E2P_POD); + if (r) + goto error; + dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value); + + /* FIXME: AL2230 handling (Bit 7 in POD) */ + *rf_type = value & 0x0f; + chip->pa_type = (value >> 16) & 0x0f; + chip->patch_cck_gain = (value >> 8) & 0x1; + chip->patch_cr157 = (value >> 13) & 0x1; + chip->patch_6m_band_edge = (value >> 21) & 0x1; + + dev_dbg_f(zd_chip_dev(chip), + "RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d " + "patch 6M %d\n", + zd_rf_name(*rf_type), *rf_type, + chip->pa_type, chip->patch_cck_gain, + chip->patch_cr157, chip->patch_6m_band_edge); + return 0; +error: + *rf_type = 0; + chip->pa_type = 0; + chip->patch_cck_gain = 0; + chip->patch_cr157 = 0; + chip->patch_6m_band_edge = 0; + return r; +} + +static int _read_mac_addr(struct zd_chip *chip, u8 *mac_addr, + const zd_addr_t *addr) +{ + int r; + u32 parts[2]; + + r = zd_ioread32v_locked(chip, parts, (const zd_addr_t *)addr, 2); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error: couldn't read e2p macs. Error number %d\n", r); + return r; + } + + mac_addr[0] = parts[0]; + mac_addr[1] = parts[0] >> 8; + mac_addr[2] = parts[0] >> 16; + mac_addr[3] = parts[0] >> 24; + mac_addr[4] = parts[1]; + mac_addr[5] = parts[1] >> 8; + + return 0; +} + +static int read_e2p_mac_addr(struct zd_chip *chip) +{ + static const zd_addr_t addr[2] = { E2P_MAC_ADDR_P1, E2P_MAC_ADDR_P2 }; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return _read_mac_addr(chip, chip->e2p_mac, (const zd_addr_t *)addr); +} + +/* MAC address: if custom mac addresses are to to be used CR_MAC_ADDR_P1 and + * CR_MAC_ADDR_P2 must be overwritten + */ +void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + mutex_lock(&chip->mutex); + memcpy(mac_addr, chip->e2p_mac, ETH_ALEN); + mutex_unlock(&chip->mutex); +} + +static int read_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + static const zd_addr_t addr[2] = { CR_MAC_ADDR_P1, CR_MAC_ADDR_P2 }; + return _read_mac_addr(chip, mac_addr, (const zd_addr_t *)addr); +} + +int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + mutex_lock(&chip->mutex); + r = read_mac_addr(chip, mac_addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr) +{ + int r; + struct zd_ioreq32 reqs[2] = { + [0] = { .addr = CR_MAC_ADDR_P1 }, + [1] = { .addr = CR_MAC_ADDR_P2 }, + }; + + reqs[0].value = (mac_addr[3] << 24) + | (mac_addr[2] << 16) + | (mac_addr[1] << 8) + | mac_addr[0]; + reqs[1].value = (mac_addr[5] << 8) + | mac_addr[4]; + + dev_dbg_f(zd_chip_dev(chip), + "mac addr " MAC_FMT "\n", MAC_ARG(mac_addr)); + + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); +#ifdef DEBUG + { + u8 tmp[ETH_ALEN]; + read_mac_addr(chip, tmp); + } +#endif /* DEBUG */ + mutex_unlock(&chip->mutex); + return r; +} + +int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain) +{ + int r; + u32 value; + + mutex_lock(&chip->mutex); + r = zd_ioread32_locked(chip, &value, E2P_SUBID); + mutex_unlock(&chip->mutex); + if (r) + return r; + + *regdomain = value >> 16; + dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain); + + return 0; +} + +static int read_values(struct zd_chip *chip, u8 *values, size_t count, + zd_addr_t e2p_addr, u32 guard) +{ + int r; + int i; + u32 v; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + for (i = 0;;) { + r = zd_ioread32_locked(chip, &v, e2p_addr+i/2); + if (r) + return r; + v -= guard; + if (i+4 < count) { + values[i++] = v; + values[i++] = v >> 8; + values[i++] = v >> 16; + values[i++] = v >> 24; + continue; + } + for (;i < count; i++) + values[i] = v >> (8*(i%3)); + return 0; + } +} + +static int read_pwr_cal_values(struct zd_chip *chip) +{ + return read_values(chip, chip->pwr_cal_values, + E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1, + 0); +} + +static int read_pwr_int_values(struct zd_chip *chip) +{ + return read_values(chip, chip->pwr_int_values, + E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1, + E2P_PWR_INT_GUARD); +} + +static int read_ofdm_cal_values(struct zd_chip *chip) +{ + int r; + int i; + static const zd_addr_t addresses[] = { + E2P_36M_CAL_VALUE1, + E2P_48M_CAL_VALUE1, + E2P_54M_CAL_VALUE1, + }; + + for (i = 0; i < 3; i++) { + r = read_values(chip, chip->ofdm_cal_values[i], + E2P_CHANNEL_COUNT, addresses[i], 0); + if (r) + return r; + } + return 0; +} + +static int read_cal_int_tables(struct zd_chip *chip) +{ + int r; + + r = read_pwr_cal_values(chip); + if (r) + return r; + r = read_pwr_int_values(chip); + if (r) + return r; + r = read_ofdm_cal_values(chip); + if (r) + return r; + return 0; +} + +/* phy means physical registers */ +int zd_chip_lock_phy_regs(struct zd_chip *chip) +{ + int r; + u32 tmp; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &tmp, CR_REG1); + if (r) { + dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r); + return r; + } + + dev_dbg_f(zd_chip_dev(chip), + "CR_REG1: 0x%02x -> 0x%02x\n", tmp, tmp & ~UNLOCK_PHY_REGS); + tmp &= ~UNLOCK_PHY_REGS; + + r = zd_iowrite32_locked(chip, tmp, CR_REG1); + if (r) + dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); + return r; +} + +int zd_chip_unlock_phy_regs(struct zd_chip *chip) +{ + int r; + u32 tmp; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &tmp, CR_REG1); + if (r) { + dev_err(zd_chip_dev(chip), + "error ioread32(CR_REG1): %d\n", r); + return r; + } + + dev_dbg_f(zd_chip_dev(chip), + "CR_REG1: 0x%02x -> 0x%02x\n", tmp, tmp | UNLOCK_PHY_REGS); + tmp |= UNLOCK_PHY_REGS; + + r = zd_iowrite32_locked(chip, tmp, CR_REG1); + if (r) + dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); + return r; +} + +/* CR157 can be optionally patched by the EEPROM */ +static int patch_cr157(struct zd_chip *chip) +{ + int r; + u32 value; + + if (!chip->patch_cr157) + return 0; + + r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); + if (r) + return r; + + dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8); + return zd_iowrite32_locked(chip, value >> 8, CR157); +} + +/* + * 6M band edge can be optionally overwritten for certain RF's + * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge + * bit (for AL2230, AL2230S) + */ +static int patch_6m_band_edge(struct zd_chip *chip, int channel) +{ + struct zd_ioreq16 ioreqs[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR47, 0x1e }, + }; + + if (!chip->patch_6m_band_edge || !chip->rf.patch_6m_band_edge) + return 0; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1 || channel == 11) + ioreqs[0].value = 0x12; + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211_hw_reset_phy(struct zd_chip *chip) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR0, 0x0a }, { CR1, 0x06 }, { CR2, 0x26 }, + { CR3, 0x38 }, { CR4, 0x80 }, { CR9, 0xa0 }, + { CR10, 0x81 }, { CR11, 0x00 }, { CR12, 0x7f }, + { CR13, 0x8c }, { CR14, 0x80 }, { CR15, 0x3d }, + { CR16, 0x20 }, { CR17, 0x1e }, { CR18, 0x0a }, + { CR19, 0x48 }, { CR20, 0x0c }, { CR21, 0x0c }, + { CR22, 0x23 }, { CR23, 0x90 }, { CR24, 0x14 }, + { CR25, 0x40 }, { CR26, 0x10 }, { CR27, 0x19 }, + { CR28, 0x7f }, { CR29, 0x80 }, { CR30, 0x4b }, + { CR31, 0x60 }, { CR32, 0x43 }, { CR33, 0x08 }, + { CR34, 0x06 }, { CR35, 0x0a }, { CR36, 0x00 }, + { CR37, 0x00 }, { CR38, 0x38 }, { CR39, 0x0c }, + { CR40, 0x84 }, { CR41, 0x2a }, { CR42, 0x80 }, + { CR43, 0x10 }, { CR44, 0x12 }, { CR46, 0xff }, + { CR47, 0x1E }, { CR48, 0x26 }, { CR49, 0x5b }, + { CR64, 0xd0 }, { CR65, 0x04 }, { CR66, 0x58 }, + { CR67, 0xc9 }, { CR68, 0x88 }, { CR69, 0x41 }, + { CR70, 0x23 }, { CR71, 0x10 }, { CR72, 0xff }, + { CR73, 0x32 }, { CR74, 0x30 }, { CR75, 0x65 }, + { CR76, 0x41 }, { CR77, 0x1b }, { CR78, 0x30 }, + { CR79, 0x68 }, { CR80, 0x64 }, { CR81, 0x64 }, + { CR82, 0x00 }, { CR83, 0x00 }, { CR84, 0x00 }, + { CR85, 0x02 }, { CR86, 0x00 }, { CR87, 0x00 }, + { CR88, 0xff }, { CR89, 0xfc }, { CR90, 0x00 }, + { CR91, 0x00 }, { CR92, 0x00 }, { CR93, 0x08 }, + { CR94, 0x00 }, { CR95, 0x00 }, { CR96, 0xff }, + { CR97, 0xe7 }, { CR98, 0x00 }, { CR99, 0x00 }, + { CR100, 0x00 }, { CR101, 0xae }, { CR102, 0x02 }, + { CR103, 0x00 }, { CR104, 0x03 }, { CR105, 0x65 }, + { CR106, 0x04 }, { CR107, 0x00 }, { CR108, 0x0a }, + { CR109, 0xaa }, { CR110, 0xaa }, { CR111, 0x25 }, + { CR112, 0x25 }, { CR113, 0x00 }, { CR119, 0x1e }, + { CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 }, + { }, + { CR5, 0x00 }, { CR6, 0x00 }, { CR7, 0x00 }, + { CR8, 0x00 }, { CR9, 0x20 }, { CR12, 0xf0 }, + { CR20, 0x0e }, { CR21, 0x0e }, { CR27, 0x10 }, + { CR44, 0x33 }, { CR47, 0x1E }, { CR83, 0x24 }, + { CR84, 0x04 }, { CR85, 0x00 }, { CR86, 0x0C }, + { CR87, 0x12 }, { CR88, 0x0C }, { CR89, 0x00 }, + { CR90, 0x10 }, { CR91, 0x08 }, { CR93, 0x00 }, + { CR94, 0x01 }, { CR95, 0x00 }, { CR96, 0x50 }, + { CR97, 0x37 }, { CR98, 0x35 }, { CR101, 0x13 }, + { CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 }, + { CR105, 0x12 }, { CR109, 0x27 }, { CR110, 0x27 }, + { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, + { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, + { CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f }, + { CR123, 0x27 }, { CR125, 0xaa }, { CR127, 0x03 }, + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR131, 0x0C }, { CR136, 0xdf }, { CR137, 0x40 }, + { CR138, 0xa0 }, { CR139, 0xb0 }, { CR140, 0x99 }, + { CR141, 0x82 }, { CR142, 0x54 }, { CR143, 0x1c }, + { CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x4c }, + { CR149, 0x50 }, { CR150, 0x0e }, { CR151, 0x18 }, + { CR160, 0xfe }, { CR161, 0xee }, { CR162, 0xaa }, + { CR163, 0xfa }, { CR164, 0xfa }, { CR165, 0xea }, + { CR166, 0xbe }, { CR167, 0xbe }, { CR168, 0x6a }, + { CR169, 0xba }, { CR170, 0xba }, { CR171, 0xba }, + /* Note: CR204 must lead the CR203 */ + { CR204, 0x7d }, + { }, + { CR203, 0x30 }, + }; + + int r, t; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + goto unlock; + + r = patch_cr157(chip); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + return r; +} + +static int zd1211b_hw_reset_phy(struct zd_chip *chip) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR0, 0x14 }, { CR1, 0x06 }, { CR2, 0x26 }, + { CR3, 0x38 }, { CR4, 0x80 }, { CR9, 0xe0 }, + { CR10, 0x81 }, + /* power control { { CR11, 1 << 6 }, */ + { CR11, 0x00 }, + { CR12, 0xf0 }, { CR13, 0x8c }, { CR14, 0x80 }, + { CR15, 0x3d }, { CR16, 0x20 }, { CR17, 0x1e }, + { CR18, 0x0a }, { CR19, 0x48 }, + { CR20, 0x10 }, /* Org:0x0E, ComTrend:RalLink AP */ + { CR21, 0x0e }, { CR22, 0x23 }, { CR23, 0x90 }, + { CR24, 0x14 }, { CR25, 0x40 }, { CR26, 0x10 }, + { CR27, 0x10 }, { CR28, 0x7f }, { CR29, 0x80 }, + { CR30, 0x49 }, /* jointly decoder, no ASIC */ + { CR31, 0x60 }, { CR32, 0x43 }, { CR33, 0x08 }, + { CR34, 0x06 }, { CR35, 0x0a }, { CR36, 0x00 }, + { CR37, 0x00 }, { CR38, 0x38 }, { CR39, 0x0c }, + { CR40, 0x84 }, { CR41, 0x2a }, { CR42, 0x80 }, + { CR43, 0x10 }, { CR44, 0x33 }, { CR46, 0xff }, + { CR47, 0x1E }, { CR48, 0x26 }, { CR49, 0x5b }, + { CR64, 0xd0 }, { CR65, 0x04 }, { CR66, 0x58 }, + { CR67, 0xc9 }, { CR68, 0x88 }, { CR69, 0x41 }, + { CR70, 0x23 }, { CR71, 0x10 }, { CR72, 0xff }, + { CR73, 0x32 }, { CR74, 0x30 }, { CR75, 0x65 }, + { CR76, 0x41 }, { CR77, 0x1b }, { CR78, 0x30 }, + { CR79, 0xf0 }, { CR80, 0x64 }, { CR81, 0x64 }, + { CR82, 0x00 }, { CR83, 0x24 }, { CR84, 0x04 }, + { CR85, 0x00 }, { CR86, 0x0c }, { CR87, 0x12 }, + { CR88, 0x0c }, { CR89, 0x00 }, { CR90, 0x58 }, + { CR91, 0x04 }, { CR92, 0x00 }, { CR93, 0x00 }, + { CR94, 0x01 }, + { CR95, 0x20 }, /* ZD1211B */ + { CR96, 0x50 }, { CR97, 0x37 }, { CR98, 0x35 }, + { CR99, 0x00 }, { CR100, 0x01 }, { CR101, 0x13 }, + { CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 }, + { CR105, 0x12 }, { CR106, 0x04 }, { CR107, 0x00 }, + { CR108, 0x0a }, { CR109, 0x27 }, { CR110, 0x27 }, + { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, + { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, + { CR117, 0xfc }, { CR118, 0xfa }, { CR119, 0x1e }, + { CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 }, + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR131, 0x0c }, { CR136, 0xdf }, { CR137, 0xa0 }, + { CR138, 0xa8 }, { CR139, 0xb4 }, { CR140, 0x98 }, + { CR141, 0x82 }, { CR142, 0x53 }, { CR143, 0x1c }, + { CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x40 }, + { CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */ + { CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */ + { CR151, 0x18 }, { CR159, 0x70 }, { CR160, 0xfe }, + { CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa }, + { CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe }, + { CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba }, + { CR170, 0xba }, { CR171, 0xba }, + /* Note: CR204 must lead the CR203 */ + { CR204, 0x7d }, + {}, + { CR203, 0x30 }, + }; + + int r, t; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + goto unlock; + + r = patch_cr157(chip); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + return r; +} + +static int hw_reset_phy(struct zd_chip *chip) +{ + return chip->is_zd1211b ? zd1211b_hw_reset_phy(chip) : + zd1211_hw_reset_phy(chip); +} + +static int zd1211_hw_init_hmac(struct zd_chip *chip) +{ + static const struct zd_ioreq32 ioreqs[] = { + { CR_ACK_TIMEOUT_EXT, 0x20 }, + { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, + { CR_ZD1211_RETRY_MAX, 0x2 }, + { CR_SNIFFER_ON, 0 }, + { CR_RX_FILTER, AP_RX_FILTER }, + { CR_GROUP_HASH_P1, 0x00 }, + { CR_GROUP_HASH_P2, 0x80000000 }, + { CR_REG1, 0xa4 }, + { CR_ADDA_PWR_DWN, 0x7f }, + { CR_BCN_PLCP_CFG, 0x00f00401 }, + { CR_PHY_DELAY, 0x00 }, + { CR_ACK_TIMEOUT_EXT, 0x80 }, + { CR_ADDA_PWR_DWN, 0x00 }, + { CR_ACK_TIME_80211, 0x100 }, + { CR_IFS_VALUE, 0x547c032 }, + { CR_RX_PE_DELAY, 0x70 }, + { CR_PS_CTRL, 0x10000000 }, + { CR_RTS_CTS_RATE, 0x02030203 }, + { CR_RX_THRESHOLD, 0x000c0640 }, + { CR_AFTER_PNP, 0x1 }, + { CR_WEP_PROTECT, 0x114 }, + }; + + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +#ifdef DEBUG + if (r) { + dev_err(zd_chip_dev(chip), + "error in zd_iowrite32a_locked. Error number %d\n", r); + } +#endif /* DEBUG */ + return r; +} + +static int zd1211b_hw_init_hmac(struct zd_chip *chip) +{ + static const struct zd_ioreq32 ioreqs[] = { + { CR_ACK_TIMEOUT_EXT, 0x20 }, + { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, + { CR_ZD1211B_RETRY_MAX, 0x02020202 }, + { CR_ZD1211B_TX_PWR_CTL4, 0x007f003f }, + { CR_ZD1211B_TX_PWR_CTL3, 0x007f003f }, + { CR_ZD1211B_TX_PWR_CTL2, 0x003f001f }, + { CR_ZD1211B_TX_PWR_CTL1, 0x001f000f }, + { CR_ZD1211B_AIFS_CTL1, 0x00280028 }, + { CR_ZD1211B_AIFS_CTL2, 0x008C003C }, + { CR_ZD1211B_TXOP, 0x01800824 }, + { CR_SNIFFER_ON, 0 }, + { CR_RX_FILTER, AP_RX_FILTER }, + { CR_GROUP_HASH_P1, 0x00 }, + { CR_GROUP_HASH_P2, 0x80000000 }, + { CR_REG1, 0xa4 }, + { CR_ADDA_PWR_DWN, 0x7f }, + { CR_BCN_PLCP_CFG, 0x00f00401 }, + { CR_PHY_DELAY, 0x00 }, + { CR_ACK_TIMEOUT_EXT, 0x80 }, + { CR_ADDA_PWR_DWN, 0x00 }, + { CR_ACK_TIME_80211, 0x100 }, + { CR_IFS_VALUE, 0x547c032 }, + { CR_RX_PE_DELAY, 0x70 }, + { CR_PS_CTRL, 0x10000000 }, + { CR_RTS_CTS_RATE, 0x02030203 }, + { CR_RX_THRESHOLD, 0x000c0640 }, + { CR_AFTER_PNP, 0x1 }, + { CR_WEP_PROTECT, 0x114 }, + }; + + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error in zd_iowrite32a_locked. Error number %d\n", r); + } + return r; +} + +static int hw_init_hmac(struct zd_chip *chip) +{ + return chip->is_zd1211b ? + zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip); +} + +struct aw_pt_bi { + u32 atim_wnd_period; + u32 pre_tbtt; + u32 beacon_interval; +}; + +static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) +{ + int r; + static const zd_addr_t aw_pt_bi_addr[] = + { CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL }; + u32 values[3]; + + r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, + ARRAY_SIZE(aw_pt_bi_addr)); + if (r) { + memset(s, 0, sizeof(*s)); + return r; + } + + s->atim_wnd_period = values[0]; + s->pre_tbtt = values[1]; + s->beacon_interval = values[2]; + dev_dbg_f(zd_chip_dev(chip), "aw %u pt %u bi %u\n", + s->atim_wnd_period, s->pre_tbtt, s->beacon_interval); + return 0; +} + +static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) +{ + struct zd_ioreq32 reqs[3]; + + if (s->beacon_interval <= 5) + s->beacon_interval = 5; + if (s->pre_tbtt < 4 || s->pre_tbtt >= s->beacon_interval) + s->pre_tbtt = s->beacon_interval - 1; + if (s->atim_wnd_period >= s->pre_tbtt) + s->atim_wnd_period = s->pre_tbtt - 1; + + reqs[0].addr = CR_ATIM_WND_PERIOD; + reqs[0].value = s->atim_wnd_period; + reqs[1].addr = CR_PRE_TBTT; + reqs[1].value = s->pre_tbtt; + reqs[2].addr = CR_BCN_INTERVAL; + reqs[2].value = s->beacon_interval; + + dev_dbg_f(zd_chip_dev(chip), + "aw %u pt %u bi %u\n", s->atim_wnd_period, s->pre_tbtt, + s->beacon_interval); + return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); +} + + +static int set_beacon_interval(struct zd_chip *chip, u32 interval) +{ + int r; + struct aw_pt_bi s; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = get_aw_pt_bi(chip, &s); + if (r) + return r; + s.beacon_interval = interval; + return set_aw_pt_bi(chip, &s); +} + +int zd_set_beacon_interval(struct zd_chip *chip, u32 interval) +{ + int r; + + mutex_lock(&chip->mutex); + r = set_beacon_interval(chip, interval); + mutex_unlock(&chip->mutex); + return r; +} + +static int hw_init(struct zd_chip *chip) +{ + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = hw_reset_phy(chip); + if (r) + return r; + + r = hw_init_hmac(chip); + if (r) + return r; + r = set_beacon_interval(chip, 100); + if (r) + return r; + return 0; +} + +#ifdef DEBUG +static int dump_cr(struct zd_chip *chip, const zd_addr_t addr, + const char *addr_string) +{ + int r; + u32 value; + + r = zd_ioread32_locked(chip, &value, addr); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error reading %s. Error number %d\n", addr_string, r); + return r; + } + + dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n", + addr_string, (unsigned int)value); + return 0; +} + +static int test_init(struct zd_chip *chip) +{ + int r; + + r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP"); + if (r) + return r; + r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN"); + if (r) + return r; + return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT"); +} + +static void dump_fw_registers(struct zd_chip *chip) +{ + static const zd_addr_t addr[4] = { + FW_FIRMWARE_VER, FW_USB_SPEED, FW_FIX_TX_RATE, + FW_LINK_STATUS + }; + + int r; + u16 values[4]; + + r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr, + ARRAY_SIZE(addr)); + if (r) { + dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n", + r); + return; + } + + dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]); + dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]); + dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]); + dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]); +} +#endif /* DEBUG */ + +static int print_fw_version(struct zd_chip *chip) +{ + int r; + u16 version; + + r = zd_ioread16_locked(chip, &version, FW_FIRMWARE_VER); + if (r) + return r; + + dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version); + return 0; +} + +static int set_mandatory_rates(struct zd_chip *chip, enum ieee80211_std std) +{ + u32 rates; + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + /* This sets the mandatory rates, which only depend from the standard + * that the device is supporting. Until further notice we should try + * to support 802.11g also for full speed USB. + */ + switch (std) { + case IEEE80211B: + rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M; + break; + case IEEE80211G: + rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M| + CR_RATE_6M|CR_RATE_12M|CR_RATE_24M; + break; + default: + return -EINVAL; + } + return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL); +} + +int zd_chip_enable_hwint(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT); + mutex_unlock(&chip->mutex); + return r; +} + +static int disable_hwint(struct zd_chip *chip) +{ + return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT); +} + +int zd_chip_disable_hwint(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = disable_hwint(chip); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_init_hw(struct zd_chip *chip, u8 device_type) +{ + int r; + u8 rf_type; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + mutex_lock(&chip->mutex); + chip->is_zd1211b = (device_type == DEVICE_ZD1211B) != 0; + +#ifdef DEBUG + r = test_init(chip); + if (r) + goto out; +#endif + r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP); + if (r) + goto out; + + r = zd_usb_init_hw(&chip->usb); + if (r) + goto out; + + /* GPI is always disabled, also in the other driver. + */ + r = zd_iowrite32_locked(chip, 0, CR_GPI_EN); + if (r) + goto out; + r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX); + if (r) + goto out; + /* Currently we support IEEE 802.11g for full and high speed USB. + * It might be discussed, whether we should suppport pure b mode for + * full speed USB. + */ + r = set_mandatory_rates(chip, IEEE80211G); + if (r) + goto out; + /* Disabling interrupts is certainly a smart thing here. + */ + r = disable_hwint(chip); + if (r) + goto out; + r = read_pod(chip, &rf_type); + if (r) + goto out; + r = hw_init(chip); + if (r) + goto out; + r = zd_rf_init_hw(&chip->rf, rf_type); + if (r) + goto out; + + r = print_fw_version(chip); + if (r) + goto out; + +#ifdef DEBUG + dump_fw_registers(chip); + r = test_init(chip); + if (r) + goto out; +#endif /* DEBUG */ + + r = read_e2p_mac_addr(chip); + if (r) + goto out; + + r = read_cal_int_tables(chip); + if (r) + goto out; + + print_id(chip); +out: + mutex_unlock(&chip->mutex); + return r; +} + +static int update_pwr_int(struct zd_chip *chip, u8 channel) +{ + u8 value = chip->pwr_int_values[channel - 1]; + dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_int %#04x\n", + channel, value); + return zd_iowrite32_locked(chip, value, CR31); +} + +static int update_pwr_cal(struct zd_chip *chip, u8 channel) +{ + u8 value = chip->pwr_cal_values[channel-1]; + dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_cal %#04x\n", + channel, value); + return zd_iowrite32_locked(chip, value, CR68); +} + +static int update_ofdm_cal(struct zd_chip *chip, u8 channel) +{ + struct zd_ioreq32 ioreqs[3]; + + ioreqs[0].addr = CR67; + ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1]; + ioreqs[1].addr = CR66; + ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1]; + ioreqs[2].addr = CR65; + ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1]; + + dev_dbg_f(zd_chip_dev(chip), + "channel %d ofdm_cal 36M %#04x 48M %#04x 54M %#04x\n", + channel, ioreqs[0].value, ioreqs[1].value, ioreqs[2].value); + return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int update_channel_integration_and_calibration(struct zd_chip *chip, + u8 channel) +{ + int r; + + r = update_pwr_int(chip, channel); + if (r) + return r; + if (chip->is_zd1211b) { + static const struct zd_ioreq32 ioreqs[] = { + { CR69, 0x28 }, + {}, + { CR69, 0x2a }, + }; + + r = update_ofdm_cal(chip, channel); + if (r) + return r; + r = update_pwr_cal(chip, channel); + if (r) + return r; + r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + } + + return 0; +} + +/* The CCK baseband gain can be optionally patched by the EEPROM */ +static int patch_cck_gain(struct zd_chip *chip) +{ + int r; + u32 value; + + if (!chip->patch_cck_gain) + return 0; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); + if (r) + return r; + dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff); + return zd_iowrite32_locked(chip, value & 0xff, CR47); +} + +int zd_chip_set_channel(struct zd_chip *chip, u8 channel) +{ + int r, t; + + mutex_lock(&chip->mutex); + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + r = zd_rf_set_channel(&chip->rf, channel); + if (r) + goto unlock; + r = update_channel_integration_and_calibration(chip, channel); + if (r) + goto unlock; + r = patch_cck_gain(chip); + if (r) + goto unlock; + r = patch_6m_band_edge(chip, channel); + if (r) + goto unlock; + r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + mutex_unlock(&chip->mutex); + return r; +} + +u8 zd_chip_get_channel(struct zd_chip *chip) +{ + u8 channel; + + mutex_lock(&chip->mutex); + channel = chip->rf.channel; + mutex_unlock(&chip->mutex); + return channel; +} + +static u16 led_mask(int led) +{ + switch (led) { + case 1: + return LED1; + case 2: + return LED2; + default: + return 0; + } +} + +static int read_led_reg(struct zd_chip *chip, u16 *status) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_ioread16_locked(chip, status, CR_LED); +} + +static int write_led_reg(struct zd_chip *chip, u16 status) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite16_locked(chip, status, CR_LED); +} + +int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status) +{ + int r, ret; + u16 mask = led_mask(led); + u16 reg; + + if (!mask) + return -EINVAL; + mutex_lock(&chip->mutex); + r = read_led_reg(chip, ®); + if (r) + return r; + switch (status) { + case LED_STATUS: + return (reg & mask) ? LED_ON : LED_OFF; + case LED_OFF: + reg &= ~mask; + ret = LED_OFF; + break; + case LED_FLIP: + reg ^= mask; + ret = (reg&mask) ? LED_ON : LED_OFF; + break; + case LED_ON: + reg |= mask; + ret = LED_ON; + break; + default: + return -EINVAL; + } + r = write_led_reg(chip, reg); + if (r) { + ret = r; + goto out; + } +out: + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_led_flip(struct zd_chip *chip, int led, + const unsigned int *phases_msecs, unsigned int count) +{ + int i, r; + enum led_status status; + + r = zd_chip_led_status(chip, led, LED_STATUS); + if (r) + return r; + status = r; + for (i = 0; i < count; i++) { + r = zd_chip_led_status(chip, led, LED_FLIP); + if (r < 0) + goto out; + msleep(phases_msecs[i]); + } + +out: + zd_chip_led_status(chip, led, status); + return r; +} + +int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates) +{ + int r; + + if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G)) + return -EINVAL; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL); + mutex_unlock(&chip->mutex); + return r; +} + +static int ofdm_qual_db(u8 status_quality, u8 rate, unsigned int size) +{ + static const u16 constants[] = { + 715, 655, 585, 540, 470, 410, 360, 315, + 270, 235, 205, 175, 150, 125, 105, 85, + 65, 50, 40, 25, 15 + }; + + int i; + u32 x; + + /* It seems that their quality parameter is somehow per signal + * and is now transferred per bit. + */ + switch (rate) { + case ZD_OFDM_RATE_6M: + case ZD_OFDM_RATE_12M: + case ZD_OFDM_RATE_24M: + size *= 2; + break; + case ZD_OFDM_RATE_9M: + case ZD_OFDM_RATE_18M: + case ZD_OFDM_RATE_36M: + case ZD_OFDM_RATE_54M: + size *= 4; + size /= 3; + break; + case ZD_OFDM_RATE_48M: + size *= 3; + size /= 2; + break; + default: + return -EINVAL; + } + + x = (10000 * status_quality)/size; + for (i = 0; i < ARRAY_SIZE(constants); i++) { + if (x > constants[i]) + break; + } + + return i; +} + +static unsigned int log10times100(unsigned int x) +{ + static const u8 log10[] = { + 0, + 0, 30, 47, 60, 69, 77, 84, 90, 95, 100, + 104, 107, 111, 114, 117, 120, 123, 125, 127, 130, + 132, 134, 136, 138, 139, 141, 143, 144, 146, 147, + 149, 150, 151, 153, 154, 155, 156, 157, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, + 170, 171, 172, 173, 174, 174, 175, 176, 177, 177, + 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, + 185, 185, 186, 186, 187, 188, 188, 189, 189, 190, + 190, 191, 191, 192, 192, 193, 193, 194, 194, 195, + 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, + 200, 200, 201, 201, 202, 202, 202, 203, 203, 204, + 204, 204, 205, 205, 206, 206, 206, 207, 207, 207, + 208, 208, 208, 209, 209, 210, 210, 210, 211, 211, + 211, 212, 212, 212, 213, 213, 213, 213, 214, 214, + 214, 215, 215, 215, 216, 216, 216, 217, 217, 217, + 217, 218, 218, 218, 219, 219, 219, 219, 220, 220, + 220, 220, 221, 221, 221, 222, 222, 222, 222, 223, + 223, 223, 223, 224, 224, 224, 224, + }; + + return x < ARRAY_SIZE(log10) ? log10[x] : 225; +} + +enum { + MAX_CCK_EVM_DB = 45, +}; + +static int cck_evm_db(u8 status_quality) +{ + return (20 * log10times100(status_quality)) / 100; +} + +static int cck_snr_db(u8 status_quality) +{ + int r = MAX_CCK_EVM_DB - cck_evm_db(status_quality); + ZD_ASSERT(r >= 0); + return r; +} + +static int rx_qual_db(const void *rx_frame, unsigned int size, + const struct rx_status *status) +{ + return (status->frame_status&ZD_RX_OFDM) ? + ofdm_qual_db(status->signal_quality_ofdm, + zd_ofdm_plcp_header_rate(rx_frame), + size) : + cck_snr_db(status->signal_quality_cck); +} + +u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size, + const struct rx_status *status) +{ + int r = rx_qual_db(rx_frame, size, status); + if (r < 0) + r = 0; + r = (r * 100) / 14; + if (r > 100) + r = 100; + return r; +} + +u8 zd_rx_strength_percent(u8 rssi) +{ + int r = (rssi*100) / 30; + if (r > 100) + r = 100; + return (u8) r; +} + +u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status) +{ + static const u16 ofdm_rates[] = { + [ZD_OFDM_RATE_6M] = 60, + [ZD_OFDM_RATE_9M] = 90, + [ZD_OFDM_RATE_12M] = 120, + [ZD_OFDM_RATE_18M] = 180, + [ZD_OFDM_RATE_24M] = 240, + [ZD_OFDM_RATE_36M] = 360, + [ZD_OFDM_RATE_48M] = 480, + [ZD_OFDM_RATE_54M] = 540, + }; + u16 rate; + if (status->frame_status & ZD_RX_OFDM) { + u8 ofdm_rate = zd_ofdm_plcp_header_rate(rx_frame); + rate = ofdm_rates[ofdm_rate & 0xf]; + } else { + u8 cck_rate = zd_cck_plcp_header_rate(rx_frame); + switch (cck_rate) { + case ZD_CCK_SIGNAL_1M: + rate = 10; + break; + case ZD_CCK_SIGNAL_2M: + rate = 20; + break; + case ZD_CCK_SIGNAL_5M5: + rate = 55; + break; + case ZD_CCK_SIGNAL_11M: + rate = 110; + break; + default: + rate = 0; + } + } + + return rate; +} + +int zd_chip_switch_radio_on(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_switch_radio_on(&chip->rf); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_switch_radio_off(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_switch_radio_off(&chip->rf); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_enable_int(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_usb_enable_int(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_int(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_disable_int(&chip->usb); + mutex_unlock(&chip->mutex); +} + +int zd_chip_enable_rx(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_usb_enable_rx(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_rx(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_disable_rx(&chip->usb); + mutex_unlock(&chip->mutex); +} + +int zd_rfwritev_locked(struct zd_chip *chip, + const u32* values, unsigned int count, u8 bits) +{ + int r; + unsigned int i; + + for (i = 0; i < count; i++) { + r = zd_rfwrite_locked(chip, values[i], bits); + if (r) + return r; + } + + return 0; +} diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h new file mode 100644 index 000000000000..805121093ab5 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -0,0 +1,825 @@ +/* zd_chip.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_CHIP_H +#define _ZD_CHIP_H + +#include "zd_types.h" +#include "zd_rf.h" +#include "zd_usb.h" + +/* Header for the Media Access Controller (MAC) and the Baseband Processor + * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and + * adds a processor for handling the USB protocol. + */ + +/* 8-bit hardware registers */ +#define CR0 CTL_REG(0x0000) +#define CR1 CTL_REG(0x0004) +#define CR2 CTL_REG(0x0008) +#define CR3 CTL_REG(0x000C) + +#define CR5 CTL_REG(0x0010) +/* bit 5: if set short preamble used + * bit 6: filter band - Japan channel 14 on, else off + */ +#define CR6 CTL_REG(0x0014) +#define CR7 CTL_REG(0x0018) +#define CR8 CTL_REG(0x001C) + +#define CR4 CTL_REG(0x0020) + +#define CR9 CTL_REG(0x0024) +/* bit 2: antenna switch (together with CR10) */ +#define CR10 CTL_REG(0x0028) +/* bit 1: antenna switch (together with CR9) + * RF2959 controls with CR11 radion on and off + */ +#define CR11 CTL_REG(0x002C) +/* bit 6: TX power control for OFDM + * RF2959 controls with CR10 radio on and off + */ +#define CR12 CTL_REG(0x0030) +#define CR13 CTL_REG(0x0034) +#define CR14 CTL_REG(0x0038) +#define CR15 CTL_REG(0x003C) +#define CR16 CTL_REG(0x0040) +#define CR17 CTL_REG(0x0044) +#define CR18 CTL_REG(0x0048) +#define CR19 CTL_REG(0x004C) +#define CR20 CTL_REG(0x0050) +#define CR21 CTL_REG(0x0054) +#define CR22 CTL_REG(0x0058) +#define CR23 CTL_REG(0x005C) +#define CR24 CTL_REG(0x0060) /* CCA threshold */ +#define CR25 CTL_REG(0x0064) +#define CR26 CTL_REG(0x0068) +#define CR27 CTL_REG(0x006C) +#define CR28 CTL_REG(0x0070) +#define CR29 CTL_REG(0x0074) +#define CR30 CTL_REG(0x0078) +#define CR31 CTL_REG(0x007C) /* TX power control for RF in CCK mode */ +#define CR32 CTL_REG(0x0080) +#define CR33 CTL_REG(0x0084) +#define CR34 CTL_REG(0x0088) +#define CR35 CTL_REG(0x008C) +#define CR36 CTL_REG(0x0090) +#define CR37 CTL_REG(0x0094) +#define CR38 CTL_REG(0x0098) +#define CR39 CTL_REG(0x009C) +#define CR40 CTL_REG(0x00A0) +#define CR41 CTL_REG(0x00A4) +#define CR42 CTL_REG(0x00A8) +#define CR43 CTL_REG(0x00AC) +#define CR44 CTL_REG(0x00B0) +#define CR45 CTL_REG(0x00B4) +#define CR46 CTL_REG(0x00B8) +#define CR47 CTL_REG(0x00BC) /* CCK baseband gain + * (patch value might be in EEPROM) + */ +#define CR48 CTL_REG(0x00C0) +#define CR49 CTL_REG(0x00C4) +#define CR50 CTL_REG(0x00C8) +#define CR51 CTL_REG(0x00CC) /* TX power control for RF in 6-36M modes */ +#define CR52 CTL_REG(0x00D0) /* TX power control for RF in 48M mode */ +#define CR53 CTL_REG(0x00D4) /* TX power control for RF in 54M mode */ +#define CR54 CTL_REG(0x00D8) +#define CR55 CTL_REG(0x00DC) +#define CR56 CTL_REG(0x00E0) +#define CR57 CTL_REG(0x00E4) +#define CR58 CTL_REG(0x00E8) +#define CR59 CTL_REG(0x00EC) +#define CR60 CTL_REG(0x00F0) +#define CR61 CTL_REG(0x00F4) +#define CR62 CTL_REG(0x00F8) +#define CR63 CTL_REG(0x00FC) +#define CR64 CTL_REG(0x0100) +#define CR65 CTL_REG(0x0104) /* OFDM 54M calibration */ +#define CR66 CTL_REG(0x0108) /* OFDM 48M calibration */ +#define CR67 CTL_REG(0x010C) /* OFDM 36M calibration */ +#define CR68 CTL_REG(0x0110) /* CCK calibration */ +#define CR69 CTL_REG(0x0114) +#define CR70 CTL_REG(0x0118) +#define CR71 CTL_REG(0x011C) +#define CR72 CTL_REG(0x0120) +#define CR73 CTL_REG(0x0124) +#define CR74 CTL_REG(0x0128) +#define CR75 CTL_REG(0x012C) +#define CR76 CTL_REG(0x0130) +#define CR77 CTL_REG(0x0134) +#define CR78 CTL_REG(0x0138) +#define CR79 CTL_REG(0x013C) +#define CR80 CTL_REG(0x0140) +#define CR81 CTL_REG(0x0144) +#define CR82 CTL_REG(0x0148) +#define CR83 CTL_REG(0x014C) +#define CR84 CTL_REG(0x0150) +#define CR85 CTL_REG(0x0154) +#define CR86 CTL_REG(0x0158) +#define CR87 CTL_REG(0x015C) +#define CR88 CTL_REG(0x0160) +#define CR89 CTL_REG(0x0164) +#define CR90 CTL_REG(0x0168) +#define CR91 CTL_REG(0x016C) +#define CR92 CTL_REG(0x0170) +#define CR93 CTL_REG(0x0174) +#define CR94 CTL_REG(0x0178) +#define CR95 CTL_REG(0x017C) +#define CR96 CTL_REG(0x0180) +#define CR97 CTL_REG(0x0184) +#define CR98 CTL_REG(0x0188) +#define CR99 CTL_REG(0x018C) +#define CR100 CTL_REG(0x0190) +#define CR101 CTL_REG(0x0194) +#define CR102 CTL_REG(0x0198) +#define CR103 CTL_REG(0x019C) +#define CR104 CTL_REG(0x01A0) +#define CR105 CTL_REG(0x01A4) +#define CR106 CTL_REG(0x01A8) +#define CR107 CTL_REG(0x01AC) +#define CR108 CTL_REG(0x01B0) +#define CR109 CTL_REG(0x01B4) +#define CR110 CTL_REG(0x01B8) +#define CR111 CTL_REG(0x01BC) +#define CR112 CTL_REG(0x01C0) +#define CR113 CTL_REG(0x01C4) +#define CR114 CTL_REG(0x01C8) +#define CR115 CTL_REG(0x01CC) +#define CR116 CTL_REG(0x01D0) +#define CR117 CTL_REG(0x01D4) +#define CR118 CTL_REG(0x01D8) +#define CR119 CTL_REG(0x01DC) +#define CR120 CTL_REG(0x01E0) +#define CR121 CTL_REG(0x01E4) +#define CR122 CTL_REG(0x01E8) +#define CR123 CTL_REG(0x01EC) +#define CR124 CTL_REG(0x01F0) +#define CR125 CTL_REG(0x01F4) +#define CR126 CTL_REG(0x01F8) +#define CR127 CTL_REG(0x01FC) +#define CR128 CTL_REG(0x0200) +#define CR129 CTL_REG(0x0204) +#define CR130 CTL_REG(0x0208) +#define CR131 CTL_REG(0x020C) +#define CR132 CTL_REG(0x0210) +#define CR133 CTL_REG(0x0214) +#define CR134 CTL_REG(0x0218) +#define CR135 CTL_REG(0x021C) +#define CR136 CTL_REG(0x0220) +#define CR137 CTL_REG(0x0224) +#define CR138 CTL_REG(0x0228) +#define CR139 CTL_REG(0x022C) +#define CR140 CTL_REG(0x0230) +#define CR141 CTL_REG(0x0234) +#define CR142 CTL_REG(0x0238) +#define CR143 CTL_REG(0x023C) +#define CR144 CTL_REG(0x0240) +#define CR145 CTL_REG(0x0244) +#define CR146 CTL_REG(0x0248) +#define CR147 CTL_REG(0x024C) +#define CR148 CTL_REG(0x0250) +#define CR149 CTL_REG(0x0254) +#define CR150 CTL_REG(0x0258) +#define CR151 CTL_REG(0x025C) +#define CR152 CTL_REG(0x0260) +#define CR153 CTL_REG(0x0264) +#define CR154 CTL_REG(0x0268) +#define CR155 CTL_REG(0x026C) +#define CR156 CTL_REG(0x0270) +#define CR157 CTL_REG(0x0274) +#define CR158 CTL_REG(0x0278) +#define CR159 CTL_REG(0x027C) +#define CR160 CTL_REG(0x0280) +#define CR161 CTL_REG(0x0284) +#define CR162 CTL_REG(0x0288) +#define CR163 CTL_REG(0x028C) +#define CR164 CTL_REG(0x0290) +#define CR165 CTL_REG(0x0294) +#define CR166 CTL_REG(0x0298) +#define CR167 CTL_REG(0x029C) +#define CR168 CTL_REG(0x02A0) +#define CR169 CTL_REG(0x02A4) +#define CR170 CTL_REG(0x02A8) +#define CR171 CTL_REG(0x02AC) +#define CR172 CTL_REG(0x02B0) +#define CR173 CTL_REG(0x02B4) +#define CR174 CTL_REG(0x02B8) +#define CR175 CTL_REG(0x02BC) +#define CR176 CTL_REG(0x02C0) +#define CR177 CTL_REG(0x02C4) +#define CR178 CTL_REG(0x02C8) +#define CR179 CTL_REG(0x02CC) +#define CR180 CTL_REG(0x02D0) +#define CR181 CTL_REG(0x02D4) +#define CR182 CTL_REG(0x02D8) +#define CR183 CTL_REG(0x02DC) +#define CR184 CTL_REG(0x02E0) +#define CR185 CTL_REG(0x02E4) +#define CR186 CTL_REG(0x02E8) +#define CR187 CTL_REG(0x02EC) +#define CR188 CTL_REG(0x02F0) +#define CR189 CTL_REG(0x02F4) +#define CR190 CTL_REG(0x02F8) +#define CR191 CTL_REG(0x02FC) +#define CR192 CTL_REG(0x0300) +#define CR193 CTL_REG(0x0304) +#define CR194 CTL_REG(0x0308) +#define CR195 CTL_REG(0x030C) +#define CR196 CTL_REG(0x0310) +#define CR197 CTL_REG(0x0314) +#define CR198 CTL_REG(0x0318) +#define CR199 CTL_REG(0x031C) +#define CR200 CTL_REG(0x0320) +#define CR201 CTL_REG(0x0324) +#define CR202 CTL_REG(0x0328) +#define CR203 CTL_REG(0x032C) /* I2C bus template value & flash control */ +#define CR204 CTL_REG(0x0330) +#define CR205 CTL_REG(0x0334) +#define CR206 CTL_REG(0x0338) +#define CR207 CTL_REG(0x033C) +#define CR208 CTL_REG(0x0340) +#define CR209 CTL_REG(0x0344) +#define CR210 CTL_REG(0x0348) +#define CR211 CTL_REG(0x034C) +#define CR212 CTL_REG(0x0350) +#define CR213 CTL_REG(0x0354) +#define CR214 CTL_REG(0x0358) +#define CR215 CTL_REG(0x035C) +#define CR216 CTL_REG(0x0360) +#define CR217 CTL_REG(0x0364) +#define CR218 CTL_REG(0x0368) +#define CR219 CTL_REG(0x036C) +#define CR220 CTL_REG(0x0370) +#define CR221 CTL_REG(0x0374) +#define CR222 CTL_REG(0x0378) +#define CR223 CTL_REG(0x037C) +#define CR224 CTL_REG(0x0380) +#define CR225 CTL_REG(0x0384) +#define CR226 CTL_REG(0x0388) +#define CR227 CTL_REG(0x038C) +#define CR228 CTL_REG(0x0390) +#define CR229 CTL_REG(0x0394) +#define CR230 CTL_REG(0x0398) +#define CR231 CTL_REG(0x039C) +#define CR232 CTL_REG(0x03A0) +#define CR233 CTL_REG(0x03A4) +#define CR234 CTL_REG(0x03A8) +#define CR235 CTL_REG(0x03AC) +#define CR236 CTL_REG(0x03B0) + +#define CR240 CTL_REG(0x03C0) +/* bit 7: host-controlled RF register writes + * CR241-CR245: for hardware controlled writing of RF bits, not needed for + * USB + */ +#define CR241 CTL_REG(0x03C4) +#define CR242 CTL_REG(0x03C8) +#define CR243 CTL_REG(0x03CC) +#define CR244 CTL_REG(0x03D0) +#define CR245 CTL_REG(0x03D4) + +#define CR251 CTL_REG(0x03EC) /* only used for activation and deactivation of + * Airoha RFs AL2230 and AL7230B + */ +#define CR252 CTL_REG(0x03F0) +#define CR253 CTL_REG(0x03F4) +#define CR254 CTL_REG(0x03F8) +#define CR255 CTL_REG(0x03FC) + +#define CR_MAX_PHY_REG 255 + +/* Taken from the ZYDAS driver, not all of them are relevant for the ZSD1211 + * driver. + */ + +#define CR_RF_IF_CLK CTL_REG(0x0400) +#define CR_RF_IF_DATA CTL_REG(0x0404) +#define CR_PE1_PE2 CTL_REG(0x0408) +#define CR_PE2_DLY CTL_REG(0x040C) +#define CR_LE1 CTL_REG(0x0410) +#define CR_LE2 CTL_REG(0x0414) +/* Seems to enable/disable GPI (General Purpose IO?) */ +#define CR_GPI_EN CTL_REG(0x0418) +#define CR_RADIO_PD CTL_REG(0x042C) +#define CR_RF2948_PD CTL_REG(0x042C) +#define CR_ENABLE_PS_MANUAL_AGC CTL_REG(0x043C) +#define CR_CONFIG_PHILIPS CTL_REG(0x0440) +#define CR_SA2400_SER_AP CTL_REG(0x0444) +#define CR_I2C_WRITE CTL_REG(0x0444) +#define CR_SA2400_SER_RP CTL_REG(0x0448) +#define CR_RADIO_PE CTL_REG(0x0458) +#define CR_RST_BUS_MASTER CTL_REG(0x045C) +#define CR_RFCFG CTL_REG(0x0464) +#define CR_HSTSCHG CTL_REG(0x046C) +#define CR_PHY_ON CTL_REG(0x0474) +#define CR_RX_DELAY CTL_REG(0x0478) +#define CR_RX_PE_DELAY CTL_REG(0x047C) +#define CR_GPIO_1 CTL_REG(0x0490) +#define CR_GPIO_2 CTL_REG(0x0494) +#define CR_EncryBufMux CTL_REG(0x04A8) +#define CR_PS_CTRL CTL_REG(0x0500) +#define CR_ADDA_PWR_DWN CTL_REG(0x0504) +#define CR_ADDA_MBIAS_WARMTIME CTL_REG(0x0508) +#define CR_MAC_PS_STATE CTL_REG(0x050C) + +#define CR_INTERRUPT CTL_REG(0x0510) +#define INT_TX_COMPLETE 0x00000001 +#define INT_RX_COMPLETE 0x00000002 +#define INT_RETRY_FAIL 0x00000004 +#define INT_WAKEUP 0x00000008 +#define INT_DTIM_NOTIFY 0x00000020 +#define INT_CFG_NEXT_BCN 0x00000040 +#define INT_BUS_ABORT 0x00000080 +#define INT_TX_FIFO_READY 0x00000100 +#define INT_UART 0x00000200 +#define INT_TX_COMPLETE_EN 0x00010000 +#define INT_RX_COMPLETE_EN 0x00020000 +#define INT_RETRY_FAIL_EN 0x00040000 +#define INT_WAKEUP_EN 0x00080000 +#define INT_DTIM_NOTIFY_EN 0x00200000 +#define INT_CFG_NEXT_BCN_EN 0x00400000 +#define INT_BUS_ABORT_EN 0x00800000 +#define INT_TX_FIFO_READY_EN 0x01000000 +#define INT_UART_EN 0x02000000 + +#define CR_TSF_LOW_PART CTL_REG(0x0514) +#define CR_TSF_HIGH_PART CTL_REG(0x0518) + +/* Following three values are in time units (1024us) + * Following condition must be met: + * atim < tbtt < bcn + */ +#define CR_ATIM_WND_PERIOD CTL_REG(0x051C) +#define CR_BCN_INTERVAL CTL_REG(0x0520) +#define CR_PRE_TBTT CTL_REG(0x0524) +/* in units of TU(1024us) */ + +/* for UART support */ +#define CR_UART_RBR_THR_DLL CTL_REG(0x0540) +#define CR_UART_DLM_IER CTL_REG(0x0544) +#define CR_UART_IIR_FCR CTL_REG(0x0548) +#define CR_UART_LCR CTL_REG(0x054c) +#define CR_UART_MCR CTL_REG(0x0550) +#define CR_UART_LSR CTL_REG(0x0554) +#define CR_UART_MSR CTL_REG(0x0558) +#define CR_UART_ECR CTL_REG(0x055c) +#define CR_UART_STATUS CTL_REG(0x0560) + +#define CR_PCI_TX_ADDR_P1 CTL_REG(0x0600) +#define CR_PCI_TX_AddR_P2 CTL_REG(0x0604) +#define CR_PCI_RX_AddR_P1 CTL_REG(0x0608) +#define CR_PCI_RX_AddR_P2 CTL_REG(0x060C) + +/* must be overwritten if custom MAC address will be used */ +#define CR_MAC_ADDR_P1 CTL_REG(0x0610) +#define CR_MAC_ADDR_P2 CTL_REG(0x0614) +#define CR_BSSID_P1 CTL_REG(0x0618) +#define CR_BSSID_P2 CTL_REG(0x061C) +#define CR_BCN_PLCP_CFG CTL_REG(0x0620) +#define CR_GROUP_HASH_P1 CTL_REG(0x0624) +#define CR_GROUP_HASH_P2 CTL_REG(0x0628) +#define CR_RX_TIMEOUT CTL_REG(0x062C) + +/* Basic rates supported by the BSS. When producing ACK or CTS messages, the + * device will use a rate in this table that is less than or equal to the rate + * of the incoming frame which prompted the response */ +#define CR_BASIC_RATE_TBL CTL_REG(0x0630) +#define CR_RATE_1M 0x0001 /* 802.11b */ +#define CR_RATE_2M 0x0002 /* 802.11b */ +#define CR_RATE_5_5M 0x0004 /* 802.11b */ +#define CR_RATE_11M 0x0008 /* 802.11b */ +#define CR_RATE_6M 0x0100 /* 802.11g */ +#define CR_RATE_9M 0x0200 /* 802.11g */ +#define CR_RATE_12M 0x0400 /* 802.11g */ +#define CR_RATE_18M 0x0800 /* 802.11g */ +#define CR_RATE_24M 0x1000 /* 802.11g */ +#define CR_RATE_36M 0x2000 /* 802.11g */ +#define CR_RATE_48M 0x4000 /* 802.11g */ +#define CR_RATE_54M 0x8000 /* 802.11g */ +#define CR_RATES_80211G 0xff00 +#define CR_RATES_80211B 0x000f + +/* Mandatory rates required in the BSS. When producing ACK or CTS messages, if + * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will + * look for a rate in this table that is less than or equal to the rate of + * the incoming frame. */ +#define CR_MANDATORY_RATE_TBL CTL_REG(0x0634) +#define CR_RTS_CTS_RATE CTL_REG(0x0638) + +#define CR_WEP_PROTECT CTL_REG(0x063C) +#define CR_RX_THRESHOLD CTL_REG(0x0640) + +/* register for controlling the LEDS */ +#define CR_LED CTL_REG(0x0644) +/* masks for controlling LEDs */ +#define LED1 0x0100 +#define LED2 0x0200 + +/* Seems to indicate that the configuration is over. + */ +#define CR_AFTER_PNP CTL_REG(0x0648) +#define CR_ACK_TIME_80211 CTL_REG(0x0658) + +#define CR_RX_OFFSET CTL_REG(0x065c) + +#define CR_PHY_DELAY CTL_REG(0x066C) +#define CR_BCN_FIFO CTL_REG(0x0670) +#define CR_SNIFFER_ON CTL_REG(0x0674) + +#define CR_ENCRYPTION_TYPE CTL_REG(0x0678) +#define NO_WEP 0 +#define WEP64 1 +#define WEP128 5 +#define WEP256 6 +#define ENC_SNIFFER 8 + +#define CR_ZD1211_RETRY_MAX CTL_REG(0x067C) + +#define CR_REG1 CTL_REG(0x0680) +/* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical + * registers, so one could argue it is a LOCK bit. But calling it + * LOCK_PHY_REGS makes it confusing. + */ +#define UNLOCK_PHY_REGS 0x0080 + +#define CR_DEVICE_STATE CTL_REG(0x0684) +#define CR_UNDERRUN_CNT CTL_REG(0x0688) + +#define CR_RX_FILTER CTL_REG(0x068c) +#define RX_FILTER_ASSOC_RESPONSE 0x0002 +#define RX_FILTER_PROBE_RESPONSE 0x0020 +#define RX_FILTER_BEACON 0x0100 +#define RX_FILTER_AUTH 0x0800 +/* Sniff modus sets filter to 0xfffff */ + +#define CR_ACK_TIMEOUT_EXT CTL_REG(0x0690) +#define CR_BCN_FIFO_SEMAPHORE CTL_REG(0x0694) +#define CR_IFS_VALUE CTL_REG(0x0698) +#define CR_RX_TIME_OUT CTL_REG(0x069C) +#define CR_TOTAL_RX_FRM CTL_REG(0x06A0) +#define CR_CRC32_CNT CTL_REG(0x06A4) +#define CR_CRC16_CNT CTL_REG(0x06A8) +#define CR_DECRYPTION_ERR_UNI CTL_REG(0x06AC) +#define CR_RX_FIFO_OVERRUN CTL_REG(0x06B0) + +#define CR_DECRYPTION_ERR_MUL CTL_REG(0x06BC) + +#define CR_NAV_CNT CTL_REG(0x06C4) +#define CR_NAV_CCA CTL_REG(0x06C8) +#define CR_RETRY_CNT CTL_REG(0x06CC) + +#define CR_READ_TCB_ADDR CTL_REG(0x06E8) +#define CR_READ_RFD_ADDR CTL_REG(0x06EC) +#define CR_CWMIN_CWMAX CTL_REG(0x06F0) +#define CR_TOTAL_TX_FRM CTL_REG(0x06F4) + +/* CAM: Continuous Access Mode (power management) */ +#define CR_CAM_MODE CTL_REG(0x0700) +#define CR_CAM_ROLL_TB_LOW CTL_REG(0x0704) +#define CR_CAM_ROLL_TB_HIGH CTL_REG(0x0708) +#define CR_CAM_ADDRESS CTL_REG(0x070C) +#define CR_CAM_DATA CTL_REG(0x0710) + +#define CR_ROMDIR CTL_REG(0x0714) + +#define CR_DECRY_ERR_FLG_LOW CTL_REG(0x0714) +#define CR_DECRY_ERR_FLG_HIGH CTL_REG(0x0718) + +#define CR_WEPKEY0 CTL_REG(0x0720) +#define CR_WEPKEY1 CTL_REG(0x0724) +#define CR_WEPKEY2 CTL_REG(0x0728) +#define CR_WEPKEY3 CTL_REG(0x072C) +#define CR_WEPKEY4 CTL_REG(0x0730) +#define CR_WEPKEY5 CTL_REG(0x0734) +#define CR_WEPKEY6 CTL_REG(0x0738) +#define CR_WEPKEY7 CTL_REG(0x073C) +#define CR_WEPKEY8 CTL_REG(0x0740) +#define CR_WEPKEY9 CTL_REG(0x0744) +#define CR_WEPKEY10 CTL_REG(0x0748) +#define CR_WEPKEY11 CTL_REG(0x074C) +#define CR_WEPKEY12 CTL_REG(0x0750) +#define CR_WEPKEY13 CTL_REG(0x0754) +#define CR_WEPKEY14 CTL_REG(0x0758) +#define CR_WEPKEY15 CTL_REG(0x075c) +#define CR_TKIP_MODE CTL_REG(0x0760) + +#define CR_EEPROM_PROTECT0 CTL_REG(0x0758) +#define CR_EEPROM_PROTECT1 CTL_REG(0x075C) + +#define CR_DBG_FIFO_RD CTL_REG(0x0800) +#define CR_DBG_SELECT CTL_REG(0x0804) +#define CR_FIFO_Length CTL_REG(0x0808) + + +#define CR_RSSI_MGC CTL_REG(0x0810) + +#define CR_PON CTL_REG(0x0818) +#define CR_RX_ON CTL_REG(0x081C) +#define CR_TX_ON CTL_REG(0x0820) +#define CR_CHIP_EN CTL_REG(0x0824) +#define CR_LO_SW CTL_REG(0x0828) +#define CR_TXRX_SW CTL_REG(0x082C) +#define CR_S_MD CTL_REG(0x0830) + +#define CR_USB_DEBUG_PORT CTL_REG(0x0888) + +#define CR_ZD1211B_TX_PWR_CTL1 CTL_REG(0x0b00) +#define CR_ZD1211B_TX_PWR_CTL2 CTL_REG(0x0b04) +#define CR_ZD1211B_TX_PWR_CTL3 CTL_REG(0x0b08) +#define CR_ZD1211B_TX_PWR_CTL4 CTL_REG(0x0b0c) +#define CR_ZD1211B_AIFS_CTL1 CTL_REG(0x0b10) +#define CR_ZD1211B_AIFS_CTL2 CTL_REG(0x0b14) +#define CR_ZD1211B_TXOP CTL_REG(0x0b20) +#define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28) + +#define AP_RX_FILTER 0x0400feff +#define STA_RX_FILTER 0x0000ffff + +#define CWIN_SIZE 0x007f043f + + +#define HWINT_ENABLED 0x004f0000 +#define HWINT_DISABLED 0 + +#define E2P_PWR_INT_GUARD 8 +#define E2P_CHANNEL_COUNT 14 + +/* If you compare this addresses with the ZYDAS orignal driver, please notify + * that we use word mapping for the EEPROM. + */ + +/* + * Upper 16 bit contains the regulatory domain. + */ +#define E2P_SUBID E2P_REG(0x00) +#define E2P_POD E2P_REG(0x02) +#define E2P_MAC_ADDR_P1 E2P_REG(0x04) +#define E2P_MAC_ADDR_P2 E2P_REG(0x06) +#define E2P_PWR_CAL_VALUE1 E2P_REG(0x08) +#define E2P_PWR_CAL_VALUE2 E2P_REG(0x0a) +#define E2P_PWR_CAL_VALUE3 E2P_REG(0x0c) +#define E2P_PWR_CAL_VALUE4 E2P_REG(0x0e) +#define E2P_PWR_INT_VALUE1 E2P_REG(0x10) +#define E2P_PWR_INT_VALUE2 E2P_REG(0x12) +#define E2P_PWR_INT_VALUE3 E2P_REG(0x14) +#define E2P_PWR_INT_VALUE4 E2P_REG(0x16) + +/* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30) + * also only 11 channels. */ +#define E2P_ALLOWED_CHANNEL E2P_REG(0x18) + +#define E2P_PHY_REG E2P_REG(0x1a) +#define E2P_DEVICE_VER E2P_REG(0x20) +#define E2P_36M_CAL_VALUE1 E2P_REG(0x28) +#define E2P_36M_CAL_VALUE2 E2P_REG(0x2a) +#define E2P_36M_CAL_VALUE3 E2P_REG(0x2c) +#define E2P_36M_CAL_VALUE4 E2P_REG(0x2e) +#define E2P_11A_INT_VALUE1 E2P_REG(0x30) +#define E2P_11A_INT_VALUE2 E2P_REG(0x32) +#define E2P_11A_INT_VALUE3 E2P_REG(0x34) +#define E2P_11A_INT_VALUE4 E2P_REG(0x36) +#define E2P_48M_CAL_VALUE1 E2P_REG(0x38) +#define E2P_48M_CAL_VALUE2 E2P_REG(0x3a) +#define E2P_48M_CAL_VALUE3 E2P_REG(0x3c) +#define E2P_48M_CAL_VALUE4 E2P_REG(0x3e) +#define E2P_48M_INT_VALUE1 E2P_REG(0x40) +#define E2P_48M_INT_VALUE2 E2P_REG(0x42) +#define E2P_48M_INT_VALUE3 E2P_REG(0x44) +#define E2P_48M_INT_VALUE4 E2P_REG(0x46) +#define E2P_54M_CAL_VALUE1 E2P_REG(0x48) /* ??? */ +#define E2P_54M_CAL_VALUE2 E2P_REG(0x4a) +#define E2P_54M_CAL_VALUE3 E2P_REG(0x4c) +#define E2P_54M_CAL_VALUE4 E2P_REG(0x4e) +#define E2P_54M_INT_VALUE1 E2P_REG(0x50) +#define E2P_54M_INT_VALUE2 E2P_REG(0x52) +#define E2P_54M_INT_VALUE3 E2P_REG(0x54) +#define E2P_54M_INT_VALUE4 E2P_REG(0x56) + +/* All 16 bit values */ +#define FW_FIRMWARE_VER FW_REG(0) +/* non-zero if USB high speed connection */ +#define FW_USB_SPEED FW_REG(1) +#define FW_FIX_TX_RATE FW_REG(2) +/* Seems to be able to control LEDs over the firmware */ +#define FW_LINK_STATUS FW_REG(3) +#define FW_SOFT_RESET FW_REG(4) +#define FW_FLASH_CHK FW_REG(5) + +enum { + CR_BASE_OFFSET = 0x9000, + FW_START_OFFSET = 0xee00, + FW_BASE_ADDR_OFFSET = FW_START_OFFSET + 0x1d, + EEPROM_START_OFFSET = 0xf800, + EEPROM_SIZE = 0x800, /* words */ + LOAD_CODE_SIZE = 0xe, /* words */ + LOAD_VECT_SIZE = 0x10000 - 0xfff7, /* words */ + EEPROM_REGS_OFFSET = LOAD_CODE_SIZE + LOAD_VECT_SIZE, + E2P_BASE_OFFSET = EEPROM_START_OFFSET + + EEPROM_REGS_OFFSET, +}; + +#define FW_REG_TABLE_ADDR USB_ADDR(FW_START_OFFSET + 0x1d) + +enum { + /* indices for ofdm_cal_values */ + OFDM_36M_INDEX = 0, + OFDM_48M_INDEX = 1, + OFDM_54M_INDEX = 2, +}; + +struct zd_chip { + struct zd_usb usb; + struct zd_rf rf; + struct mutex mutex; + u8 e2p_mac[ETH_ALEN]; + /* EepSetPoint in the vendor driver */ + u8 pwr_cal_values[E2P_CHANNEL_COUNT]; + /* integration values in the vendor driver */ + u8 pwr_int_values[E2P_CHANNEL_COUNT]; + /* SetPointOFDM in the vendor driver */ + u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT]; + u8 pa_type:4, patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1, + is_zd1211b:1; +}; + +static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb) +{ + return container_of(usb, struct zd_chip, usb); +} + +static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf) +{ + return container_of(rf, struct zd_chip, rf); +} + +#define zd_chip_dev(chip) (&(chip)->usb.intf->dev) + +void zd_chip_init(struct zd_chip *chip, + struct net_device *netdev, + struct usb_interface *intf); +void zd_chip_clear(struct zd_chip *chip); +int zd_chip_init_hw(struct zd_chip *chip, u8 device_type); +int zd_chip_reset(struct zd_chip *chip); + +static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values, + const zd_addr_t *addresses, + unsigned int count) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_ioread16v(&chip->usb, values, addresses, count); +} + +static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value, + const zd_addr_t addr) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_ioread16(&chip->usb, value, addr); +} + +int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, + const zd_addr_t *addresses, unsigned int count); + +static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value, + const zd_addr_t addr) +{ + return zd_ioread32v_locked(chip, value, (const zd_addr_t *)&addr, 1); +} + +static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value, + zd_addr_t addr) +{ + struct zd_ioreq16 ioreq; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + ioreq.addr = addr; + ioreq.value = value; + + return zd_usb_iowrite16v(&chip->usb, &ioreq, 1); +} + +int zd_iowrite16a_locked(struct zd_chip *chip, + const struct zd_ioreq16 *ioreqs, unsigned int count); + +int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count); + +static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value, + zd_addr_t addr) +{ + struct zd_ioreq32 ioreq; + + ioreq.addr = addr; + ioreq.value = value; + + return _zd_iowrite32v_locked(chip, &ioreq, 1); +} + +int zd_iowrite32a_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, unsigned int count); + +static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_rfwrite(&chip->usb, value, bits); +} + +int zd_rfwritev_locked(struct zd_chip *chip, + const u32* values, unsigned int count, u8 bits); + +/* Locking functions for reading and writing registers. + * The different parameters are intentional. + */ +int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value); +int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value); +int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value); +int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value); +int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, + u32 *values, unsigned int count); +int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count); + +int zd_chip_set_channel(struct zd_chip *chip, u8 channel); +static inline u8 _zd_chip_get_channel(struct zd_chip *chip) +{ + return chip->rf.channel; +} +u8 zd_chip_get_channel(struct zd_chip *chip); +int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain); +void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr); +int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr); +int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr); +int zd_chip_switch_radio_on(struct zd_chip *chip); +int zd_chip_switch_radio_off(struct zd_chip *chip); +int zd_chip_enable_int(struct zd_chip *chip); +void zd_chip_disable_int(struct zd_chip *chip); +int zd_chip_enable_rx(struct zd_chip *chip); +void zd_chip_disable_rx(struct zd_chip *chip); +int zd_chip_enable_hwint(struct zd_chip *chip); +int zd_chip_disable_hwint(struct zd_chip *chip); + +static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type) +{ + return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type); +} + +static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type) +{ + return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type); +} + +static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates) +{ + return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates); +} + +int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates); + +static inline int zd_chip_set_rx_filter(struct zd_chip *chip, u32 filter) +{ + return zd_iowrite32(chip, CR_RX_FILTER, filter); +} + +int zd_chip_lock_phy_regs(struct zd_chip *chip); +int zd_chip_unlock_phy_regs(struct zd_chip *chip); + +enum led_status { + LED_OFF = 0, + LED_ON = 1, + LED_FLIP = 2, + LED_STATUS = 3, +}; + +int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status); +int zd_chip_led_flip(struct zd_chip *chip, int led, + const unsigned int *phases_msecs, unsigned int count); + +int zd_set_beacon_interval(struct zd_chip *chip, u32 interval); + +static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval) +{ + return zd_ioread32(chip, CR_BCN_INTERVAL, interval); +} + +struct rx_status; + +u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size, + const struct rx_status *status); +u8 zd_rx_strength_percent(u8 rssi); + +u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status); + +#endif /* _ZD_CHIP_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_def.h b/drivers/net/wireless/zd1211rw/zd_def.h new file mode 100644 index 000000000000..465906812fc4 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_def.h @@ -0,0 +1,48 @@ +/* zd_def.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_DEF_H +#define _ZD_DEF_H + +#include <linux/kernel.h> +#include <linux/stringify.h> +#include <linux/device.h> +#include <linux/kernel.h> + +#define dev_printk_f(level, dev, fmt, args...) \ + dev_printk(level, dev, "%s() " fmt, __func__, ##args) + +#ifdef DEBUG +# define dev_dbg_f(dev, fmt, args...) \ + dev_printk_f(KERN_DEBUG, dev, fmt, ## args) +#else +# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0) +#endif /* DEBUG */ + +#ifdef DEBUG +# define ZD_ASSERT(x) \ +do { \ + if (!(x)) { \ + pr_debug("%s:%d ASSERT %s VIOLATED!\n", \ + __FILE__, __LINE__, __stringify(x)); \ + } \ +} while (0) +#else +# define ZD_ASSERT(x) do { } while (0) +#endif + +#endif /* _ZD_DEF_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_ieee80211.c b/drivers/net/wireless/zd1211rw/zd_ieee80211.c new file mode 100644 index 000000000000..66905f7b61ff --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_ieee80211.c @@ -0,0 +1,191 @@ +/* zd_ieee80211.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * A lot of this code is generic and should be moved into the upper layers + * at some point. + */ + +#include <linux/errno.h> +#include <linux/wireless.h> +#include <linux/kernel.h> +#include <net/ieee80211.h> + +#include "zd_def.h" +#include "zd_ieee80211.h" +#include "zd_mac.h" + +static const struct channel_range channel_ranges[] = { + [0] = { 0, 0}, + [ZD_REGDOMAIN_FCC] = { 1, 12}, + [ZD_REGDOMAIN_IC] = { 1, 12}, + [ZD_REGDOMAIN_ETSI] = { 1, 14}, + [ZD_REGDOMAIN_JAPAN] = { 1, 14}, + [ZD_REGDOMAIN_SPAIN] = { 1, 14}, + [ZD_REGDOMAIN_FRANCE] = { 1, 14}, + [ZD_REGDOMAIN_JAPAN_ADD] = {14, 15}, +}; + +const struct channel_range *zd_channel_range(u8 regdomain) +{ + if (regdomain >= ARRAY_SIZE(channel_ranges)) + regdomain = 0; + return &channel_ranges[regdomain]; +} + +int zd_regdomain_supports_channel(u8 regdomain, u8 channel) +{ + const struct channel_range *range = zd_channel_range(regdomain); + return range->start <= channel && channel < range->end; +} + +int zd_regdomain_supported(u8 regdomain) +{ + const struct channel_range *range = zd_channel_range(regdomain); + return range->start != 0; +} + +/* Stores channel frequencies in MHz. */ +static const u16 channel_frequencies[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, 2472, 2484, +}; + +#define NUM_CHANNELS ARRAY_SIZE(channel_frequencies) + +static int compute_freq(struct iw_freq *freq, u32 mhz, u32 hz) +{ + u32 factor; + + freq->e = 0; + if (mhz >= 1000000000U) { + pr_debug("zd1211 mhz %u to large\n", mhz); + freq->m = 0; + return -EINVAL; + } + + factor = 1000; + while (mhz >= factor) { + + freq->e += 1; + factor *= 10; + } + + factor /= 1000U; + freq->m = mhz * (1000000U/factor) + hz/factor; + + return 0; +} + +int zd_channel_to_freq(struct iw_freq *freq, u8 channel) +{ + if (channel > NUM_CHANNELS) { + freq->m = 0; + freq->e = 0; + return -EINVAL; + } + if (!channel) { + freq->m = 0; + freq->e = 0; + return -EINVAL; + } + return compute_freq(freq, channel_frequencies[channel-1], 0); +} + +static int freq_to_mhz(const struct iw_freq *freq) +{ + u32 factor; + int e; + + /* Such high frequencies are not supported. */ + if (freq->e > 6) + return -EINVAL; + + factor = 1; + for (e = freq->e; e > 0; --e) { + factor *= 10; + } + factor = 1000000U / factor; + + if (freq->m % factor) { + return -EINVAL; + } + + return freq->m / factor; +} + +int zd_find_channel(u8 *channel, const struct iw_freq *freq) +{ + int i, r; + u32 mhz; + + if (!(freq->flags & IW_FREQ_FIXED)) + return 0; + + if (freq->m < 1000) { + if (freq->m > NUM_CHANNELS || freq->m == 0) + return -EINVAL; + *channel = freq->m; + return 1; + } + + r = freq_to_mhz(freq); + if (r < 0) + return r; + mhz = r; + + for (i = 0; i < NUM_CHANNELS; i++) { + if (mhz == channel_frequencies[i]) { + *channel = i+1; + return 1; + } + } + + return -EINVAL; +} + +int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain) +{ + struct ieee80211_geo geo; + const struct channel_range *range; + int i; + u8 channel; + + dev_dbg(zd_mac_dev(zd_netdev_mac(ieee->dev)), + "regdomain %#04x\n", regdomain); + + range = zd_channel_range(regdomain); + if (range->start == 0) { + dev_err(zd_mac_dev(zd_netdev_mac(ieee->dev)), + "zd1211 regdomain %#04x not supported\n", + regdomain); + return -EINVAL; + } + + memset(&geo, 0, sizeof(geo)); + + for (i = 0, channel = range->start; channel < range->end; channel++) { + struct ieee80211_channel *chan = &geo.bg[i++]; + chan->freq = channel_frequencies[channel - 1]; + chan->channel = channel; + } + + geo.bg_channels = i; + memcpy(geo.name, "XX ", 4); + ieee80211_set_geo(ieee, &geo); + return 0; +} diff --git a/drivers/net/wireless/zd1211rw/zd_ieee80211.h b/drivers/net/wireless/zd1211rw/zd_ieee80211.h new file mode 100644 index 000000000000..36329890dfec --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_ieee80211.h @@ -0,0 +1,85 @@ +#ifndef _ZD_IEEE80211_H +#define _ZD_IEEE80211_H + +#include <net/ieee80211.h> +#include "zd_types.h" + +/* Additional definitions from the standards. + */ + +#define ZD_REGDOMAIN_FCC 0x10 +#define ZD_REGDOMAIN_IC 0x20 +#define ZD_REGDOMAIN_ETSI 0x30 +#define ZD_REGDOMAIN_SPAIN 0x31 +#define ZD_REGDOMAIN_FRANCE 0x32 +#define ZD_REGDOMAIN_JAPAN_ADD 0x40 +#define ZD_REGDOMAIN_JAPAN 0x41 + +enum { + MIN_CHANNEL24 = 1, + MAX_CHANNEL24 = 14, +}; + +struct channel_range { + u8 start; + u8 end; /* exclusive (channel must be less than end) */ +}; + +struct iw_freq; + +int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain); + +const struct channel_range *zd_channel_range(u8 regdomain); +int zd_regdomain_supports_channel(u8 regdomain, u8 channel); +int zd_regdomain_supported(u8 regdomain); + +/* for 2.4 GHz band */ +int zd_channel_to_freq(struct iw_freq *freq, u8 channel); +int zd_find_channel(u8 *channel, const struct iw_freq *freq); + +#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80 + +struct ofdm_plcp_header { + u8 prefix[3]; + __le16 service; +} __attribute__((packed)); + +static inline u8 zd_ofdm_plcp_header_rate( + const struct ofdm_plcp_header *header) +{ + return header->prefix[0] & 0xf; +} + +#define ZD_OFDM_RATE_6M 0xb +#define ZD_OFDM_RATE_9M 0xf +#define ZD_OFDM_RATE_12M 0xa +#define ZD_OFDM_RATE_18M 0xe +#define ZD_OFDM_RATE_24M 0x9 +#define ZD_OFDM_RATE_36M 0xd +#define ZD_OFDM_RATE_48M 0x8 +#define ZD_OFDM_RATE_54M 0xc + +struct cck_plcp_header { + u8 signal; + u8 service; + __le16 length; + __le16 crc16; +} __attribute__((packed)); + +static inline u8 zd_cck_plcp_header_rate(const struct cck_plcp_header *header) +{ + return header->signal; +} + +#define ZD_CCK_SIGNAL_1M 0x0a +#define ZD_CCK_SIGNAL_2M 0x14 +#define ZD_CCK_SIGNAL_5M5 0x37 +#define ZD_CCK_SIGNAL_11M 0x6e + +enum ieee80211_std { + IEEE80211B = 0x01, + IEEE80211A = 0x02, + IEEE80211G = 0x04, +}; + +#endif /* _ZD_IEEE80211_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c new file mode 100644 index 000000000000..bbe067ec7de1 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -0,0 +1,1055 @@ +/* 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 */ diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h new file mode 100644 index 000000000000..71e382c589ee --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_mac.h @@ -0,0 +1,190 @@ +/* zd_mac.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_MAC_H +#define _ZD_MAC_H + +#include <linux/wireless.h> +#include <linux/kernel.h> +#include <net/ieee80211.h> +#include <net/ieee80211softmac.h> + +#include "zd_chip.h" +#include "zd_netdev.h" + +struct zd_ctrlset { + u8 modulation; + __le16 tx_length; + u8 control; + /* stores only the difference to tx_length on ZD1211B */ + __le16 packet_length; + __le16 current_length; + u8 service; + __le16 next_frame_length; +} __attribute__((packed)); + +#define ZD_CS_RESERVED_SIZE 25 + +/* zd_crtlset field modulation */ +#define ZD_CS_RATE_MASK 0x0f +#define ZD_CS_TYPE_MASK 0x10 +#define ZD_CS_RATE(modulation) ((modulation) & ZD_CS_RATE_MASK) +#define ZD_CS_TYPE(modulation) ((modulation) & ZD_CS_TYPE_MASK) + +#define ZD_CS_CCK 0x00 +#define ZD_CS_OFDM 0x10 + +#define ZD_CS_CCK_RATE_1M 0x00 +#define ZD_CS_CCK_RATE_2M 0x01 +#define ZD_CS_CCK_RATE_5_5M 0x02 +#define ZD_CS_CCK_RATE_11M 0x03 +/* The rates for OFDM are encoded as in the PLCP header. Use ZD_OFDM_RATE_*. + */ + +/* bit 5 is preamble (when in CCK mode), or a/g selection (when in OFDM mode) */ +#define ZD_CS_CCK_PREA_LONG 0x00 +#define ZD_CS_CCK_PREA_SHORT 0x20 +#define ZD_CS_OFDM_MODE_11G 0x00 +#define ZD_CS_OFDM_MODE_11A 0x20 + +/* zd_ctrlset control field */ +#define ZD_CS_NEED_RANDOM_BACKOFF 0x01 +#define ZD_CS_MULTICAST 0x02 + +#define ZD_CS_FRAME_TYPE_MASK 0x0c +#define ZD_CS_DATA_FRAME 0x00 +#define ZD_CS_PS_POLL_FRAME 0x04 +#define ZD_CS_MANAGEMENT_FRAME 0x08 +#define ZD_CS_NO_SEQUENCE_CTL_FRAME 0x0c + +#define ZD_CS_WAKE_DESTINATION 0x10 +#define ZD_CS_RTS 0x20 +#define ZD_CS_ENCRYPT 0x40 +#define ZD_CS_SELF_CTS 0x80 + +/* Incoming frames are prepended by a PLCP header */ +#define ZD_PLCP_HEADER_SIZE 5 + +struct rx_length_info { + __le16 length[3]; + __le16 tag; +} __attribute__((packed)); + +#define RX_LENGTH_INFO_TAG 0x697e + +struct rx_status { + /* rssi */ + u8 signal_strength; + u8 signal_quality_cck; + u8 signal_quality_ofdm; + u8 decryption_type; + u8 frame_status; +} __attribute__((packed)); + +/* rx_status field decryption_type */ +#define ZD_RX_NO_WEP 0 +#define ZD_RX_WEP64 1 +#define ZD_RX_TKIP 2 +#define ZD_RX_AES 4 +#define ZD_RX_WEP128 5 +#define ZD_RX_WEP256 6 + +/* rx_status field frame_status */ +#define ZD_RX_FRAME_MODULATION_MASK 0x01 +#define ZD_RX_CCK 0x00 +#define ZD_RX_OFDM 0x01 + +#define ZD_RX_TIMEOUT_ERROR 0x02 +#define ZD_RX_FIFO_OVERRUN_ERROR 0x04 +#define ZD_RX_DECRYPTION_ERROR 0x08 +#define ZD_RX_CRC32_ERROR 0x10 +#define ZD_RX_NO_ADDR1_MATCH_ERROR 0x20 +#define ZD_RX_CRC16_ERROR 0x40 +#define ZD_RX_ERROR 0x80 + +enum mac_flags { + MAC_FIXED_CHANNEL = 0x01, +}; + +struct zd_mac { + struct net_device *netdev; + struct zd_chip chip; + spinlock_t lock; + /* Unlocked reading possible */ + struct iw_statistics iw_stats; + u8 qual_average; + u8 rssi_average; + u8 regdomain; + u8 default_regdomain; + u8 requested_channel; +}; + +static inline struct ieee80211_device *zd_mac_to_ieee80211(struct zd_mac *mac) +{ + return zd_netdev_ieee80211(mac->netdev); +} + +static inline struct zd_mac *zd_netdev_mac(struct net_device *netdev) +{ + return ieee80211softmac_priv(netdev); +} + +static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip) +{ + return container_of(chip, struct zd_mac, chip); +} + +static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb) +{ + return zd_chip_to_mac(zd_usb_to_chip(usb)); +} + +#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip)) + +int zd_mac_init(struct zd_mac *mac, + struct net_device *netdev, + struct usb_interface *intf); +void zd_mac_clear(struct zd_mac *mac); + +int zd_mac_init_hw(struct zd_mac *mac, u8 device_type); + +int zd_mac_open(struct net_device *netdev); +int zd_mac_stop(struct net_device *netdev); +int zd_mac_set_mac_address(struct net_device *dev, void *p); + +int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length); + +int zd_mac_set_regdomain(struct zd_mac *zd_mac, u8 regdomain); +u8 zd_mac_get_regdomain(struct zd_mac *zd_mac); + +int zd_mac_request_channel(struct zd_mac *mac, u8 channel); +int zd_mac_get_channel(struct zd_mac *mac, u8 *channel, u8 *flags); + +int zd_mac_set_mode(struct zd_mac *mac, u32 mode); +int zd_mac_get_mode(struct zd_mac *mac, u32 *mode); + +int zd_mac_get_range(struct zd_mac *mac, struct iw_range *range); + +struct iw_statistics *zd_mac_get_wireless_stats(struct net_device *ndev); + +#ifdef DEBUG +void zd_dump_rx_status(const struct rx_status *status); +#else +#define zd_dump_rx_status(status) +#endif /* DEBUG */ + +#endif /* _ZD_MAC_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.c b/drivers/net/wireless/zd1211rw/zd_netdev.c new file mode 100644 index 000000000000..9df232c2c863 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_netdev.c @@ -0,0 +1,267 @@ +/* zd_netdev.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <net/ieee80211.h> +#include <net/ieee80211softmac.h> +#include <net/ieee80211softmac_wx.h> +#include <net/iw_handler.h> + +#include "zd_def.h" +#include "zd_netdev.h" +#include "zd_mac.h" +#include "zd_ieee80211.h" + +/* Region 0 means reset regdomain to default. */ +static int zd_set_regdomain(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + const u8 *regdomain = (u8 *)req; + return zd_mac_set_regdomain(zd_netdev_mac(netdev), *regdomain); +} + +static int zd_get_regdomain(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + u8 *regdomain = (u8 *)req; + if (!regdomain) + return -EINVAL; + *regdomain = zd_mac_get_regdomain(zd_netdev_mac(netdev)); + return 0; +} + +static const struct iw_priv_args zd_priv_args[] = { + { + .cmd = ZD_PRIV_SET_REGDOMAIN, + .set_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + .name = "set_regdomain", + }, + { + .cmd = ZD_PRIV_GET_REGDOMAIN, + .get_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + .name = "get_regdomain", + }, +}; + +#define PRIV_OFFSET(x) [(x)-SIOCIWFIRSTPRIV] + +static const iw_handler zd_priv_handler[] = { + PRIV_OFFSET(ZD_PRIV_SET_REGDOMAIN) = zd_set_regdomain, + PRIV_OFFSET(ZD_PRIV_GET_REGDOMAIN) = zd_get_regdomain, +}; + +static int iw_get_name(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + /* FIXME: check whether 802.11a will also supported, add also + * zd1211B, if we support it. + */ + strlcpy(req->name, "802.11g zd1211", IFNAMSIZ); + return 0; +} + +static int iw_set_freq(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + int r; + struct zd_mac *mac = zd_netdev_mac(netdev); + struct iw_freq *freq = &req->freq; + u8 channel; + + r = zd_find_channel(&channel, freq); + if (r < 0) + return r; + r = zd_mac_request_channel(mac, channel); + return r; +} + +static int iw_get_freq(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + int r; + struct zd_mac *mac = zd_netdev_mac(netdev); + struct iw_freq *freq = &req->freq; + u8 channel; + u8 flags; + + r = zd_mac_get_channel(mac, &channel, &flags); + if (r) + return r; + + freq->flags = (flags & MAC_FIXED_CHANNEL) ? + IW_FREQ_FIXED : IW_FREQ_AUTO; + dev_dbg_f(zd_mac_dev(mac), "channel %s\n", + (flags & MAC_FIXED_CHANNEL) ? "fixed" : "auto"); + return zd_channel_to_freq(freq, channel); +} + +static int iw_set_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + return zd_mac_set_mode(zd_netdev_mac(netdev), req->mode); +} + +static int iw_get_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + return zd_mac_get_mode(zd_netdev_mac(netdev), &req->mode); +} + +static int iw_get_range(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n"); + req->data.length = sizeof(*range); + return zd_mac_get_range(zd_netdev_mac(netdev), range); +} + +static int iw_set_encode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + return ieee80211_wx_set_encode(zd_netdev_ieee80211(netdev), info, + data, extra); +} + +static int iw_get_encode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + return ieee80211_wx_get_encode(zd_netdev_ieee80211(netdev), info, + data, extra); +} + +static int iw_set_encodeext(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + return ieee80211_wx_set_encodeext(zd_netdev_ieee80211(netdev), info, + data, extra); +} + +static int iw_get_encodeext(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + return ieee80211_wx_get_encodeext(zd_netdev_ieee80211(netdev), info, + data, extra); +} + +#define WX(x) [(x)-SIOCIWFIRST] + +static const iw_handler zd_standard_iw_handlers[] = { + WX(SIOCGIWNAME) = iw_get_name, + WX(SIOCSIWFREQ) = iw_set_freq, + WX(SIOCGIWFREQ) = iw_get_freq, + WX(SIOCSIWMODE) = iw_set_mode, + WX(SIOCGIWMODE) = iw_get_mode, + WX(SIOCGIWRANGE) = iw_get_range, + WX(SIOCSIWENCODE) = iw_set_encode, + WX(SIOCGIWENCODE) = iw_get_encode, + WX(SIOCSIWENCODEEXT) = iw_set_encodeext, + WX(SIOCGIWENCODEEXT) = iw_get_encodeext, + WX(SIOCSIWAUTH) = ieee80211_wx_set_auth, + WX(SIOCGIWAUTH) = ieee80211_wx_get_auth, + WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan, + WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results, + WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid, + WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid, + WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap, + WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap, + WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate, + WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate, + WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie, + WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie, + WX(SIOCSIWMLME) = ieee80211softmac_wx_set_mlme, +}; + +static const struct iw_handler_def iw_handler_def = { + .standard = zd_standard_iw_handlers, + .num_standard = ARRAY_SIZE(zd_standard_iw_handlers), + .private = zd_priv_handler, + .num_private = ARRAY_SIZE(zd_priv_handler), + .private_args = zd_priv_args, + .num_private_args = ARRAY_SIZE(zd_priv_args), + .get_wireless_stats = zd_mac_get_wireless_stats, +}; + +struct net_device *zd_netdev_alloc(struct usb_interface *intf) +{ + int r; + struct net_device *netdev; + struct zd_mac *mac; + + netdev = alloc_ieee80211softmac(sizeof(struct zd_mac)); + if (!netdev) { + dev_dbg_f(&intf->dev, "out of memory\n"); + return NULL; + } + + mac = zd_netdev_mac(netdev); + r = zd_mac_init(mac, netdev, intf); + if (r) { + usb_set_intfdata(intf, NULL); + free_ieee80211(netdev); + return NULL; + } + + SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &intf->dev); + + dev_dbg_f(&intf->dev, "netdev->flags %#06hx\n", netdev->flags); + dev_dbg_f(&intf->dev, "netdev->features %#010lx\n", netdev->features); + + netdev->open = zd_mac_open; + netdev->stop = zd_mac_stop; + /* netdev->get_stats = */ + /* netdev->set_multicast_list = */ + netdev->set_mac_address = zd_mac_set_mac_address; + netdev->wireless_handlers = &iw_handler_def; + /* netdev->ethtool_ops = */ + + return netdev; +} + +void zd_netdev_free(struct net_device *netdev) +{ + if (!netdev) + return; + + zd_mac_clear(zd_netdev_mac(netdev)); + free_ieee80211(netdev); +} + +void zd_netdev_disconnect(struct net_device *netdev) +{ + unregister_netdev(netdev); +} diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.h b/drivers/net/wireless/zd1211rw/zd_netdev.h new file mode 100644 index 000000000000..374a957073c1 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_netdev.h @@ -0,0 +1,45 @@ +/* zd_netdev.h: Header for net device related functions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_NETDEV_H +#define _ZD_NETDEV_H + +#include <linux/usb.h> +#include <linux/netdevice.h> +#include <net/ieee80211.h> + +#define ZD_PRIV_SET_REGDOMAIN (SIOCIWFIRSTPRIV) +#define ZD_PRIV_GET_REGDOMAIN (SIOCIWFIRSTPRIV+1) + +static inline struct ieee80211_device *zd_netdev_ieee80211( + struct net_device *ndev) +{ + return netdev_priv(ndev); +} + +static inline struct net_device *zd_ieee80211_to_netdev( + struct ieee80211_device *ieee) +{ + return ieee->dev; +} + +struct net_device *zd_netdev_alloc(struct usb_interface *intf); +void zd_netdev_free(struct net_device *netdev); + +void zd_netdev_disconnect(struct net_device *netdev); + +#endif /* _ZD_NETDEV_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c new file mode 100644 index 000000000000..d3770d2c61bc --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_rf.c @@ -0,0 +1,151 @@ +/* zd_rf.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/errno.h> +#include <linux/string.h> + +#include "zd_def.h" +#include "zd_rf.h" +#include "zd_ieee80211.h" +#include "zd_chip.h" + +static const char *rfs[] = { + [0] = "unknown RF0", + [1] = "unknown RF1", + [UW2451_RF] = "UW2451_RF", + [UCHIP_RF] = "UCHIP_RF", + [AL2230_RF] = "AL2230_RF", + [AL7230B_RF] = "AL7230B_RF", + [THETA_RF] = "THETA_RF", + [AL2210_RF] = "AL2210_RF", + [MAXIM_NEW_RF] = "MAXIM_NEW_RF", + [UW2453_RF] = "UW2453_RF", + [AL2230S_RF] = "AL2230S_RF", + [RALINK_RF] = "RALINK_RF", + [INTERSIL_RF] = "INTERSIL_RF", + [RF2959_RF] = "RF2959_RF", + [MAXIM_NEW2_RF] = "MAXIM_NEW2_RF", + [PHILIPS_RF] = "PHILIPS_RF", +}; + +const char *zd_rf_name(u8 type) +{ + if (type & 0xf0) + type = 0; + return rfs[type]; +} + +void zd_rf_init(struct zd_rf *rf) +{ + memset(rf, 0, sizeof(*rf)); +} + +void zd_rf_clear(struct zd_rf *rf) +{ + memset(rf, 0, sizeof(*rf)); +} + +int zd_rf_init_hw(struct zd_rf *rf, u8 type) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + switch (type) { + case RF2959_RF: + r = zd_rf_init_rf2959(rf); + if (r) + return r; + break; + case AL2230_RF: + r = zd_rf_init_al2230(rf); + if (r) + return r; + break; + default: + dev_err(zd_chip_dev(chip), + "RF %s %#x is not supported\n", zd_rf_name(type), type); + rf->type = 0; + return -ENODEV; + } + + rf->type = type; + + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->init_hw(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%s", zd_rf_name(rf->type)); +} + +int zd_rf_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + + ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex)); + if (channel < MIN_CHANNEL24) + return -EINVAL; + if (channel > MAX_CHANNEL24) + return -EINVAL; + dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel); + + r = rf->set_channel(rf, channel); + if (r >= 0) + rf->channel = channel; + return r; +} + +int zd_switch_radio_on(struct zd_rf *rf) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->switch_radio_on(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_switch_radio_off(struct zd_rf *rf) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + /* TODO: move phy regs handling to zd_chip */ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->switch_radio_off(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h new file mode 100644 index 000000000000..ea30f693fcc8 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_rf.h @@ -0,0 +1,82 @@ +/* zd_rf.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_RF_H +#define _ZD_RF_H + +#include "zd_types.h" + +#define UW2451_RF 0x2 +#define UCHIP_RF 0x3 +#define AL2230_RF 0x4 +#define AL7230B_RF 0x5 /* a,b,g */ +#define THETA_RF 0x6 +#define AL2210_RF 0x7 +#define MAXIM_NEW_RF 0x8 +#define UW2453_RF 0x9 +#define AL2230S_RF 0xa +#define RALINK_RF 0xb +#define INTERSIL_RF 0xc +#define RF2959_RF 0xd +#define MAXIM_NEW2_RF 0xe +#define PHILIPS_RF 0xf + +#define RF_CHANNEL(ch) [(ch)-1] + +/* Provides functions of the RF transceiver. */ + +enum { + RF_REG_BITS = 6, + RF_VALUE_BITS = 18, + RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS, +}; + +struct zd_rf { + u8 type; + + u8 channel; + /* + * Whether this RF should patch the 6M band edge + * (assuming E2P_POD agrees) + */ + u8 patch_6m_band_edge:1; + + /* RF-specific functions */ + int (*init_hw)(struct zd_rf *rf); + int (*set_channel)(struct zd_rf *rf, u8 channel); + int (*switch_radio_on)(struct zd_rf *rf); + int (*switch_radio_off)(struct zd_rf *rf); +}; + +const char *zd_rf_name(u8 type); +void zd_rf_init(struct zd_rf *rf); +void zd_rf_clear(struct zd_rf *rf); +int zd_rf_init_hw(struct zd_rf *rf, u8 type); + +int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size); + +int zd_rf_set_channel(struct zd_rf *rf, u8 channel); + +int zd_switch_radio_on(struct zd_rf *rf); +int zd_switch_radio_off(struct zd_rf *rf); + +/* Functions for individual RF chips */ + +int zd_rf_init_rf2959(struct zd_rf *rf); +int zd_rf_init_al2230(struct zd_rf *rf); + +#endif /* _ZD_RF_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c new file mode 100644 index 000000000000..0948b25f660d --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c @@ -0,0 +1,308 @@ +/* zd_rf_al2230.c: Functions for the AL2230 RF controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +static const u32 al2230_table[][3] = { + RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, }, + RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, }, + RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, }, + RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, }, + RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, }, + RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, }, + RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, }, + RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, }, + RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, }, + RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, }, +}; + +static int zd1211_al2230_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { CR15, 0x20 }, { CR23, 0x40 }, { CR24, 0x20 }, + { CR26, 0x11 }, { CR28, 0x3e }, { CR29, 0x00 }, + { CR44, 0x33 }, { CR106, 0x2a }, { CR107, 0x1a }, + { CR109, 0x09 }, { CR110, 0x27 }, { CR111, 0x2b }, + { CR112, 0x2b }, { CR119, 0x0a }, { CR10, 0x89 }, + /* for newest (3rd cut) AL2300 */ + { CR17, 0x28 }, + { CR26, 0x93 }, { CR34, 0x30 }, + /* for newest (3rd cut) AL2300 */ + { CR35, 0x3e }, + { CR41, 0x24 }, { CR44, 0x32 }, + /* for newest (3rd cut) AL2300 */ + { CR46, 0x96 }, + { CR47, 0x1e }, { CR79, 0x58 }, { CR80, 0x30 }, + { CR81, 0x30 }, { CR87, 0x0a }, { CR89, 0x04 }, + { CR92, 0x0a }, { CR99, 0x28 }, { CR100, 0x00 }, + { CR101, 0x13 }, { CR102, 0x27 }, { CR106, 0x24 }, + { CR107, 0x2a }, { CR109, 0x09 }, { CR110, 0x13 }, + { CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 }, + { CR114, 0x27 }, + /* for newest (3rd cut) AL2300 */ + { CR115, 0x24 }, + { CR116, 0x24 }, { CR117, 0xf4 }, { CR118, 0xfc }, + { CR119, 0x10 }, { CR120, 0x4f }, { CR121, 0x77 }, + { CR122, 0xe0 }, { CR137, 0x88 }, { CR252, 0xff }, + { CR253, 0xff }, + + /* These following happen separately in the vendor driver */ + { }, + + /* shdnb(PLL_ON)=0 */ + { CR251, 0x2f }, + /* shdnb(PLL_ON)=1 */ + { CR251, 0x3f }, + { CR138, 0x28 }, { CR203, 0x06 }, + }; + + static const u32 rv[] = { + /* Channel 1 */ + 0x03f790, + 0x033331, + 0x00000d, + + 0x0b3331, + 0x03b812, + 0x00fff3, + 0x000da4, + 0x0f4dc5, /* fix freq shift, 0x04edc5 */ + 0x0805b6, + 0x011687, + 0x000688, + 0x0403b9, /* external control TX power (CR31) */ + 0x00dbba, + 0x00099b, + 0x0bdffc, + 0x00000d, + 0x00500f, + + /* These writes happen separately in the vendor driver */ + 0x00d00f, + 0x004c0f, + 0x00540f, + 0x00700f, + 0x00500f, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); + if (r) + return r; + + return 0; +} + +static int zd1211b_al2230_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs1[] = { + { CR10, 0x89 }, { CR15, 0x20 }, + { CR17, 0x2B }, /* for newest(3rd cut) AL2230 */ + { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x93 }, + { CR28, 0x3e }, { CR29, 0x00 }, + { CR33, 0x28 }, /* 5621 */ + { CR34, 0x30 }, + { CR35, 0x3e }, /* for newest(3rd cut) AL2230 */ + { CR41, 0x24 }, { CR44, 0x32 }, + { CR46, 0x99 }, /* for newest(3rd cut) AL2230 */ + { CR47, 0x1e }, + + /* ZD1211B 05.06.10 */ + { CR48, 0x00 }, { CR49, 0x00 }, { CR51, 0x01 }, + { CR52, 0x80 }, { CR53, 0x7e }, { CR65, 0x00 }, + { CR66, 0x00 }, { CR67, 0x00 }, { CR68, 0x00 }, + { CR69, 0x28 }, + + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR87, 0x0a }, { CR89, 0x04 }, + { CR91, 0x00 }, /* 5621 */ + { CR92, 0x0a }, + { CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { CR99, 0x00 }, /* 5621 */ + { CR101, 0x13 }, { CR102, 0x27 }, + { CR106, 0x24 }, /* for newest(3rd cut) AL2230 */ + { CR107, 0x2a }, + { CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { CR110, 0x1f }, /* 4804, for 1212 new algorithm */ + { CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 }, + { CR114, 0x27 }, + { CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) AL2230 */ + { CR116, 0x24 }, + { CR117, 0xfa }, /* for 1211b */ + { CR118, 0xfa }, /* for 1211b */ + { CR119, 0x10 }, + { CR120, 0x4f }, + { CR121, 0x6c }, /* for 1211b */ + { CR122, 0xfc }, /* E0->FC at 4902 */ + { CR123, 0x57 }, /* 5623 */ + { CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { CR126, 0x6c }, /* 5614 */ + { CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { CR137, 0x50 }, /* 5614 */ + { CR138, 0xa8 }, + { CR144, 0xac }, /* 5621 */ + { CR150, 0x0d }, { CR252, 0x00 }, { CR253, 0x00 }, + }; + + static const u32 rv1[] = { + /* channel 1 */ + 0x03f790, + 0x033331, + 0x00000d, + + 0x0b3331, + 0x03b812, + 0x00fff3, + 0x0005a4, + 0x0f4dc5, /* fix freq shift 0x044dc5 */ + 0x0805b6, + 0x0146c7, + 0x000688, + 0x0403b9, /* External control TX power (CR31) */ + 0x00dbba, + 0x00099b, + 0x0bdffc, + 0x00000d, + 0x00580f, + }; + + static const struct zd_ioreq16 ioreqs2[] = { + { CR47, 0x1e }, { CR_RFCFG, 0x03 }, + }; + + static const u32 rv2[] = { + 0x00880f, + 0x00080f, + }; + + static const struct zd_ioreq16 ioreqs3[] = { + { CR_RFCFG, 0x00 }, { CR47, 0x1e }, { CR251, 0x7f }, + }; + + static const u32 rv3[] = { + 0x00d80f, + 0x00780f, + 0x00580f, + }; + + static const struct zd_ioreq16 ioreqs4[] = { + { CR138, 0x28 }, { CR203, 0x06 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); + if (r) + return r; + r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + if (r) + return r; + r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); + if (r) + return r; + r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); + if (r) + return r; + return zd_iowrite16a_locked(chip, ioreqs4, ARRAY_SIZE(ioreqs4)); +} + +static int al2230_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = al2230_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR138, 0x28 }, + { CR203, 0x06 }, + }; + + r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS); + if (r) + return r; + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211_al2230_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x3f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int al2230_switch_radio_off(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x04 }, + { CR251, 0x2f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_al2230(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + rf->set_channel = al2230_set_channel; + rf->switch_radio_off = al2230_switch_radio_off; + if (chip->is_zd1211b) { + rf->init_hw = zd1211b_al2230_init_hw; + rf->switch_radio_on = zd1211b_al2230_switch_radio_on; + } else { + rf->init_hw = zd1211_al2230_init_hw; + rf->switch_radio_on = zd1211_al2230_switch_radio_on; + } + rf->patch_6m_band_edge = 1; + return 0; +} diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c new file mode 100644 index 000000000000..58247271cc24 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c @@ -0,0 +1,279 @@ +/* zd_rf_rfmd.c: Functions for the RFMD RF controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +static u32 rf2959_table[][2] = { + RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, + RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, + RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, + RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 }, + RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 }, + RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 }, + RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 }, + RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 }, + RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 }, + RF_CHANNEL(10) = { 0x181a09, 0x1e6666 }, + RF_CHANNEL(11) = { 0x181a19, 0x1e6666 }, + RF_CHANNEL(12) = { 0x181a29, 0x1e6666 }, + RF_CHANNEL(13) = { 0x181a39, 0x1e6666 }, + RF_CHANNEL(14) = { 0x181a60, 0x1c0000 }, +}; + +#if 0 +static int bits(u32 rw, int from, int to) +{ + rw &= ~(0xffffffffU << (to+1)); + rw >>= from; + return rw; +} + +static int bit(u32 rw, int bit) +{ + return bits(rw, bit, bit); +} + +static void dump_regwrite(u32 rw) +{ + int reg = bits(rw, 18, 22); + int rw_flag = bits(rw, 23, 23); + PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag); + + switch (reg) { + case 0: + PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d" + " if_vco_reg_en %d if_vga_en %d", + bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1), + bit(rw, 0)); + break; + case 1: + PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d" + " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d" + " ifloopc %d dac1 %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), + bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), + bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3)); + break; + case 2: + PDEBUG("reg2 IFPLL2 n1 %d num1 %d", + bits(rw, 6, 17), bits(rw, 0, 5)); + break; + case 3: + PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17)); + break; + case 4: + PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d", + bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); + break; + case 5: + PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d" + " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d" + " dac %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), + bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), + bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3)); + break; + case 6: + PDEBUG("reg6 RFPLL2 n %d num %d", + bits(rw, 6, 17), bits(rw, 0, 5)); + break; + case 7: + PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17)); + break; + case 8: + PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d", + bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); + break; + case 9: + PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d", + bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7), + bits(rw, 0, 2)); + break; + case 10: + PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d" + " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d" + " intbiasen %d tybypass %d", + bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14), + bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2), + bit(rw, 1), bit(rw, 0)); + break; + case 11: + PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d" + " tx_delay %d", + bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8), + bits(rw, 0, 2)); + break; + case 12: + PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d", + bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5)); + break; + case 13: + PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d" + " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d" + " rf_biasvco %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), + bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4), + bits(rw, 0, 2)); + break; + case 14: + PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d" + " tx_acal %d tx_pcal %d", + bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8), + bits(rw, 0, 3)); + break; + } +} +#endif /* 0 */ + +static int rf2959_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { CR2, 0x1E }, { CR9, 0x20 }, { CR10, 0x89 }, + { CR11, 0x00 }, { CR15, 0xD0 }, { CR17, 0x68 }, + { CR19, 0x4a }, { CR20, 0x0c }, { CR21, 0x0E }, + { CR23, 0x48 }, + /* normal size for cca threshold */ + { CR24, 0x14 }, + /* { CR24, 0x20 }, */ + { CR26, 0x90 }, { CR27, 0x30 }, { CR29, 0x20 }, + { CR31, 0xb2 }, { CR32, 0x43 }, { CR33, 0x28 }, + { CR38, 0x30 }, { CR34, 0x0f }, { CR35, 0xF0 }, + { CR41, 0x2a }, { CR46, 0x7F }, { CR47, 0x1E }, + { CR51, 0xc5 }, { CR52, 0xc5 }, { CR53, 0xc5 }, + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR82, 0x00 }, { CR83, 0x24 }, { CR84, 0x04 }, + { CR85, 0x00 }, { CR86, 0x10 }, { CR87, 0x2A }, + { CR88, 0x10 }, { CR89, 0x24 }, { CR90, 0x18 }, + /* { CR91, 0x18 }, */ + /* should solve continous CTS frame problems */ + { CR91, 0x00 }, + { CR92, 0x0a }, { CR93, 0x00 }, { CR94, 0x01 }, + { CR95, 0x00 }, { CR96, 0x40 }, { CR97, 0x37 }, + { CR98, 0x05 }, { CR99, 0x28 }, { CR100, 0x00 }, + { CR101, 0x13 }, { CR102, 0x27 }, { CR103, 0x27 }, + { CR104, 0x18 }, { CR105, 0x12 }, + /* normal size */ + { CR106, 0x1a }, + /* { CR106, 0x22 }, */ + { CR107, 0x24 }, { CR108, 0x0a }, { CR109, 0x13 }, + { CR110, 0x2F }, { CR111, 0x27 }, { CR112, 0x27 }, + { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x40 }, + { CR116, 0x40 }, { CR117, 0xF0 }, { CR118, 0xF0 }, + { CR119, 0x16 }, + /* no TX continuation */ + { CR122, 0x00 }, + /* { CR122, 0xff }, */ + { CR127, 0x03 }, { CR131, 0x08 }, { CR138, 0x28 }, + { CR148, 0x44 }, { CR150, 0x10 }, { CR169, 0xBB }, + { CR170, 0xBB }, + }; + + static const u32 rv[] = { + 0x000007, /* REG0(CFG1) */ + 0x07dd43, /* REG1(IFPLL1) */ + 0x080959, /* REG2(IFPLL2) */ + 0x0e6666, + 0x116a57, /* REG4 */ + 0x17dd43, /* REG5 */ + 0x1819f9, /* REG6 */ + 0x1e6666, + 0x214554, + 0x25e7fa, + 0x27fffa, + /* The Zydas driver somehow forgets to set this value. It's + * only set for Japan. We are using internal power control + * for now. + */ + 0x294128, /* internal power */ + /* 0x28252c, */ /* External control TX power */ + /* CR31_CCK, CR51_6-36M, CR52_48M, CR53_54M */ + 0x2c0000, + 0x300000, + 0x340000, /* REG13(0xD) */ + 0x381e0f, /* REG14(0xE) */ + /* Bogus, RF2959's data sheet doesn't know register 27, which is + * actually referenced here. The commented 0x11 is 17. + */ + 0x6c180f, /* REG27(0x11) */ + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); +} + +static int rf2959_set_channel(struct zd_rf *rf, u8 channel) +{ + int i, r; + u32 *rv = rf2959_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + for (i = 0; i < 2; i++) { + r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS); + if (r) + return r; + } + return 0; +} + +static int rf2959_switch_radio_on(struct zd_rf *rf) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR10, 0x89 }, + { CR11, 0x00 }, + }; + struct zd_chip *chip = zd_rf_to_chip(rf); + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int rf2959_switch_radio_off(struct zd_rf *rf) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR10, 0x15 }, + { CR11, 0x81 }, + }; + struct zd_chip *chip = zd_rf_to_chip(rf); + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_rf2959(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + if (chip->is_zd1211b) { + dev_err(zd_chip_dev(chip), + "RF2959 is currently not supported for ZD1211B" + " devices\n"); + return -ENODEV; + } + rf->init_hw = rf2959_init_hw; + rf->set_channel = rf2959_set_channel; + rf->switch_radio_on = rf2959_switch_radio_on; + rf->switch_radio_off = rf2959_switch_radio_off; + return 0; +} diff --git a/drivers/net/wireless/zd1211rw/zd_types.h b/drivers/net/wireless/zd1211rw/zd_types.h new file mode 100644 index 000000000000..0155a1584ed3 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_types.h @@ -0,0 +1,71 @@ +/* zd_types.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_TYPES_H +#define _ZD_TYPES_H + +#include <linux/types.h> + +/* We have three register spaces mapped into the overall USB address space of + * 64K words (16-bit values). There is the control register space of + * double-word registers, the eeprom register space and the firmware register + * space. The control register space is byte mapped, the others are word + * mapped. + * + * For that reason, we are using byte offsets for control registers and word + * offsets for everything else. + */ + +typedef u32 __nocast zd_addr_t; + +enum { + ADDR_BASE_MASK = 0xff000000, + ADDR_OFFSET_MASK = 0x0000ffff, + ADDR_ZERO_MASK = 0x00ff0000, + NULL_BASE = 0x00000000, + USB_BASE = 0x01000000, + CR_BASE = 0x02000000, + CR_MAX_OFFSET = 0x0b30, + E2P_BASE = 0x03000000, + E2P_MAX_OFFSET = 0x007e, + FW_BASE = 0x04000000, + FW_MAX_OFFSET = 0x0005, +}; + +#define ZD_ADDR_BASE(addr) ((u32)(addr) & ADDR_BASE_MASK) +#define ZD_OFFSET(addr) ((u32)(addr) & ADDR_OFFSET_MASK) + +#define ZD_ADDR(base, offset) \ + ((zd_addr_t)(((base) & ADDR_BASE_MASK) | ((offset) & ADDR_OFFSET_MASK))) + +#define ZD_NULL_ADDR ((zd_addr_t)0) +#define USB_REG(offset) ZD_ADDR(USB_BASE, offset) /* word addressing */ +#define CTL_REG(offset) ZD_ADDR(CR_BASE, offset) /* byte addressing */ +#define E2P_REG(offset) ZD_ADDR(E2P_BASE, offset) /* word addressing */ +#define FW_REG(offset) ZD_ADDR(FW_BASE, offset) /* word addressing */ + +static inline zd_addr_t zd_inc_word(zd_addr_t addr) +{ + u32 base = ZD_ADDR_BASE(addr); + u32 offset = ZD_OFFSET(addr); + + offset += base == CR_BASE ? 2 : 1; + + return base | offset; +} + +#endif /* _ZD_TYPES_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c new file mode 100644 index 000000000000..ce1cb2c6aa8d --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -0,0 +1,1316 @@ +/* zd_usb.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <asm/unaligned.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/firmware.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/skbuff.h> +#include <linux/usb.h> +#include <net/ieee80211.h> + +#include "zd_def.h" +#include "zd_netdev.h" +#include "zd_mac.h" +#include "zd_usb.h" +#include "zd_util.h" + +static struct usb_device_id usb_ids[] = { + /* ZD1211 */ + { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, + /* ZD1211B */ + { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, + {} +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip."); +MODULE_AUTHOR("Ulrich Kunitz"); +MODULE_AUTHOR("Daniel Drake"); +MODULE_VERSION("1.0"); +MODULE_DEVICE_TABLE(usb, usb_ids); + +#define FW_ZD1211_PREFIX "zd1211/zd1211_" +#define FW_ZD1211B_PREFIX "zd1211/zd1211b_" + +/* register address handling */ + +#ifdef DEBUG +static int check_addr(struct zd_usb *usb, zd_addr_t addr) +{ + u32 base = ZD_ADDR_BASE(addr); + u32 offset = ZD_OFFSET(addr); + + if ((u32)addr & ADDR_ZERO_MASK) + goto invalid_address; + switch (base) { + case USB_BASE: + break; + case CR_BASE: + if (offset > CR_MAX_OFFSET) { + dev_dbg(zd_usb_dev(usb), + "CR offset %#010x larger than" + " CR_MAX_OFFSET %#10x\n", + offset, CR_MAX_OFFSET); + goto invalid_address; + } + if (offset & 1) { + dev_dbg(zd_usb_dev(usb), + "CR offset %#010x is not a multiple of 2\n", + offset); + goto invalid_address; + } + break; + case E2P_BASE: + if (offset > E2P_MAX_OFFSET) { + dev_dbg(zd_usb_dev(usb), + "E2P offset %#010x larger than" + " E2P_MAX_OFFSET %#010x\n", + offset, E2P_MAX_OFFSET); + goto invalid_address; + } + break; + case FW_BASE: + if (!usb->fw_base_offset) { + dev_dbg(zd_usb_dev(usb), + "ERROR: fw base offset has not been set\n"); + return -EAGAIN; + } + if (offset > FW_MAX_OFFSET) { + dev_dbg(zd_usb_dev(usb), + "FW offset %#10x is larger than" + " FW_MAX_OFFSET %#010x\n", + offset, FW_MAX_OFFSET); + goto invalid_address; + } + break; + default: + dev_dbg(zd_usb_dev(usb), + "address has unsupported base %#010x\n", addr); + goto invalid_address; + } + + return 0; +invalid_address: + dev_dbg(zd_usb_dev(usb), + "ERROR: invalid address: %#010x\n", addr); + return -EINVAL; +} +#endif /* DEBUG */ + +static u16 usb_addr(struct zd_usb *usb, zd_addr_t addr) +{ + u32 base; + u16 offset; + + base = ZD_ADDR_BASE(addr); + offset = ZD_OFFSET(addr); + + ZD_ASSERT(check_addr(usb, addr) == 0); + + switch (base) { + case CR_BASE: + offset += CR_BASE_OFFSET; + break; + case E2P_BASE: + offset += E2P_BASE_OFFSET; + break; + case FW_BASE: + offset += usb->fw_base_offset; + break; + } + + return offset; +} + +/* USB device initialization */ + +static int request_fw_file( + const struct firmware **fw, const char *name, struct device *device) +{ + int r; + + dev_dbg_f(device, "fw name %s\n", name); + + r = request_firmware(fw, name, device); + if (r) + dev_err(device, + "Could not load firmware file %s. Error number %d\n", + name, r); + return r; +} + +static inline u16 get_bcdDevice(const struct usb_device *udev) +{ + return le16_to_cpu(udev->descriptor.bcdDevice); +} + +enum upload_code_flags { + REBOOT = 1, +}; + +/* Ensures that MAX_TRANSFER_SIZE is even. */ +#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1) + +static int upload_code(struct usb_device *udev, + const u8 *data, size_t size, u16 code_offset, int flags) +{ + u8 *p; + int r; + + /* USB request blocks need "kmalloced" buffers. + */ + p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL); + if (!p) { + dev_err(&udev->dev, "out of memory\n"); + r = -ENOMEM; + goto error; + } + + size &= ~1; + while (size > 0) { + size_t transfer_size = size <= MAX_TRANSFER_SIZE ? + size : MAX_TRANSFER_SIZE; + + dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size); + + memcpy(p, data, transfer_size); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_FIRMWARE_DOWNLOAD, + USB_DIR_OUT | USB_TYPE_VENDOR, + code_offset, 0, p, transfer_size, 1000 /* ms */); + if (r < 0) { + dev_err(&udev->dev, + "USB control request for firmware upload" + " failed. Error number %d\n", r); + goto error; + } + transfer_size = r & ~1; + + size -= transfer_size; + data += transfer_size; + code_offset += transfer_size/sizeof(u16); + } + + if (flags & REBOOT) { + u8 ret; + + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_FIRMWARE_CONFIRM, + USB_DIR_IN | USB_TYPE_VENDOR, + 0, 0, &ret, sizeof(ret), 5000 /* ms */); + if (r != sizeof(ret)) { + dev_err(&udev->dev, + "control request firmeware confirmation failed." + " Return value %d\n", r); + if (r >= 0) + r = -ENODEV; + goto error; + } + if (ret & 0x80) { + dev_err(&udev->dev, + "Internal error while downloading." + " Firmware confirm return value %#04x\n", + (unsigned int)ret); + r = -ENODEV; + goto error; + } + dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n", + (unsigned int)ret); + } + + r = 0; +error: + kfree(p); + return r; +} + +static u16 get_word(const void *data, u16 offset) +{ + const __le16 *p = data; + return le16_to_cpu(p[offset]); +} + +static char *get_fw_name(char *buffer, size_t size, u8 device_type, + const char* postfix) +{ + scnprintf(buffer, size, "%s%s", + device_type == DEVICE_ZD1211B ? + FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX, + postfix); + return buffer; +} + +static int upload_firmware(struct usb_device *udev, u8 device_type) +{ + int r; + u16 fw_bcdDevice; + u16 bcdDevice; + const struct firmware *ub_fw = NULL; + const struct firmware *uph_fw = NULL; + char fw_name[128]; + + bcdDevice = get_bcdDevice(udev); + + r = request_fw_file(&ub_fw, + get_fw_name(fw_name, sizeof(fw_name), device_type, "ub"), + &udev->dev); + if (r) + goto error; + + fw_bcdDevice = get_word(ub_fw->data, EEPROM_REGS_OFFSET); + + /* FIXME: do we have any reason to perform the kludge that the vendor + * driver does when there is a version mismatch? (their driver uploads + * different firmwares and stuff) + */ + if (fw_bcdDevice != bcdDevice) { + dev_info(&udev->dev, + "firmware device id %#06x and actual device id " + "%#06x differ, continuing anyway\n", + fw_bcdDevice, bcdDevice); + } else { + dev_dbg_f(&udev->dev, + "firmware device id %#06x is equal to the " + "actual device id\n", fw_bcdDevice); + } + + + r = request_fw_file(&uph_fw, + get_fw_name(fw_name, sizeof(fw_name), device_type, "uphr"), + &udev->dev); + if (r) + goto error; + + r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START_OFFSET, + REBOOT); + if (r) { + dev_err(&udev->dev, + "Could not upload firmware code uph. Error number %d\n", + r); + } + + /* FALL-THROUGH */ +error: + release_firmware(ub_fw); + release_firmware(uph_fw); + return r; +} + +static void disable_read_regs_int(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + ZD_ASSERT(in_interrupt()); + spin_lock(&intr->lock); + intr->read_regs_enabled = 0; + spin_unlock(&intr->lock); +} + +#define urb_dev(urb) (&(urb)->dev->dev) + +static inline void handle_regs_int(struct urb *urb) +{ + struct zd_usb *usb = urb->context; + struct zd_usb_interrupt *intr = &usb->intr; + int len; + + ZD_ASSERT(in_interrupt()); + spin_lock(&intr->lock); + + if (intr->read_regs_enabled) { + intr->read_regs.length = len = urb->actual_length; + + if (len > sizeof(intr->read_regs.buffer)) + len = sizeof(intr->read_regs.buffer); + memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); + intr->read_regs_enabled = 0; + complete(&intr->read_regs.completion); + goto out; + } + + dev_dbg_f(urb_dev(urb), "regs interrupt ignored\n"); +out: + spin_unlock(&intr->lock); +} + +static inline void handle_retry_failed_int(struct urb *urb) +{ + dev_dbg_f(urb_dev(urb), "retry failed interrupt\n"); +} + + +static void int_urb_complete(struct urb *urb, struct pt_regs *pt_regs) +{ + int r; + struct usb_int_header *hdr; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + goto kfree; + case -EPIPE: + usb_clear_halt(urb->dev, EP_INT_IN); + /* FALL-THROUGH */ + default: + goto resubmit; + } + + if (urb->actual_length < sizeof(hdr)) { + dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb); + goto resubmit; + } + + hdr = urb->transfer_buffer; + if (hdr->type != USB_INT_TYPE) { + dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb); + goto resubmit; + } + + switch (hdr->id) { + case USB_INT_ID_REGS: + handle_regs_int(urb); + break; + case USB_INT_ID_RETRY_FAILED: + handle_retry_failed_int(urb); + break; + default: + dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb, + (unsigned int)hdr->id); + goto resubmit; + } + +resubmit: + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + dev_dbg_f(urb_dev(urb), "resubmit urb %p\n", urb); + goto kfree; + } + return; +kfree: + kfree(urb->transfer_buffer); +} + +static inline int int_urb_interval(struct usb_device *udev) +{ + switch (udev->speed) { + case USB_SPEED_HIGH: + return 4; + case USB_SPEED_LOW: + return 10; + case USB_SPEED_FULL: + default: + return 1; + } +} + +static inline int usb_int_enabled(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + + spin_lock_irqsave(&intr->lock, flags); + urb = intr->urb; + spin_unlock_irqrestore(&intr->lock, flags); + return urb != NULL; +} + +int zd_usb_enable_int(struct zd_usb *usb) +{ + int r; + struct usb_device *udev; + struct zd_usb_interrupt *intr = &usb->intr; + void *transfer_buffer = NULL; + struct urb *urb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + urb = usb_alloc_urb(0, GFP_NOFS); + if (!urb) { + r = -ENOMEM; + goto out; + } + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&intr->lock); + if (intr->urb) { + spin_unlock_irq(&intr->lock); + r = 0; + goto error_free_urb; + } + intr->urb = urb; + spin_unlock_irq(&intr->lock); + + /* TODO: make it a DMA buffer */ + r = -ENOMEM; + transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_NOFS); + if (!transfer_buffer) { + dev_dbg_f(zd_usb_dev(usb), + "couldn't allocate transfer_buffer\n"); + goto error_set_urb_null; + } + + udev = zd_usb_to_usbdev(usb); + usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN), + transfer_buffer, USB_MAX_EP_INT_BUFFER, + int_urb_complete, usb, + intr->interval); + + dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); + r = usb_submit_urb(urb, GFP_NOFS); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "Couldn't submit urb. Error number %d\n", r); + goto error; + } + + return 0; +error: + kfree(transfer_buffer); +error_set_urb_null: + spin_lock_irq(&intr->lock); + intr->urb = NULL; + spin_unlock_irq(&intr->lock); +error_free_urb: + usb_free_urb(urb); +out: + return r; +} + +void zd_usb_disable_int(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + + spin_lock_irqsave(&intr->lock, flags); + urb = intr->urb; + if (!urb) { + spin_unlock_irqrestore(&intr->lock, flags); + return; + } + intr->urb = NULL; + spin_unlock_irqrestore(&intr->lock, flags); + + usb_kill_urb(urb); + dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb); + usb_free_urb(urb); +} + +static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, + unsigned int length) +{ + int i; + struct zd_mac *mac = zd_usb_to_mac(usb); + const struct rx_length_info *length_info; + + if (length < sizeof(struct rx_length_info)) { + /* It's not a complete packet anyhow. */ + return; + } + length_info = (struct rx_length_info *) + (buffer + length - sizeof(struct rx_length_info)); + + /* It might be that three frames are merged into a single URB + * transaction. We have to check for the length info tag. + * + * While testing we discovered that length_info might be unaligned, + * because if USB transactions are merged, the last packet will not + * be padded. Unaligned access might also happen if the length_info + * structure is not present. + */ + if (get_unaligned(&length_info->tag) == RX_LENGTH_INFO_TAG) { + unsigned int l, k, n; + for (i = 0, l = 0;; i++) { + k = le16_to_cpu(get_unaligned( + &length_info->length[i])); + n = l+k; + if (n > length) + return; + zd_mac_rx(mac, buffer+l, k); + if (i >= 2) + return; + l = (n+3) & ~3; + } + } else { + zd_mac_rx(mac, buffer, length); + } +} + +static void rx_urb_complete(struct urb *urb, struct pt_regs *pt_regs) +{ + struct zd_usb *usb; + struct zd_usb_rx *rx; + const u8 *buffer; + unsigned int length; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + return; + case -EPIPE: + usb_clear_halt(urb->dev, EP_DATA_IN); + /* FALL-THROUGH */ + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } + + buffer = urb->transfer_buffer; + length = urb->actual_length; + usb = urb->context; + rx = &usb->rx; + + if (length%rx->usb_packet_size > rx->usb_packet_size-4) { + /* If there is an old first fragment, we don't care. */ + dev_dbg_f(urb_dev(urb), "*** first fragment ***\n"); + ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment)); + spin_lock(&rx->lock); + memcpy(rx->fragment, buffer, length); + rx->fragment_length = length; + spin_unlock(&rx->lock); + goto resubmit; + } + + spin_lock(&rx->lock); + if (rx->fragment_length > 0) { + /* We are on a second fragment, we believe */ + ZD_ASSERT(length + rx->fragment_length <= + ARRAY_SIZE(rx->fragment)); + dev_dbg_f(urb_dev(urb), "*** second fragment ***\n"); + memcpy(rx->fragment+rx->fragment_length, buffer, length); + handle_rx_packet(usb, rx->fragment, + rx->fragment_length + length); + rx->fragment_length = 0; + spin_unlock(&rx->lock); + } else { + spin_unlock(&rx->lock); + handle_rx_packet(usb, buffer, length); + } + +resubmit: + usb_submit_urb(urb, GFP_ATOMIC); +} + +struct urb *alloc_urb(struct zd_usb *usb) +{ + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + void *buffer; + + urb = usb_alloc_urb(0, GFP_NOFS); + if (!urb) + return NULL; + buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_NOFS, + &urb->transfer_dma); + if (!buffer) { + usb_free_urb(urb); + return NULL; + } + + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN), + buffer, USB_MAX_RX_SIZE, + rx_urb_complete, usb); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return urb; +} + +void free_urb(struct urb *urb) +{ + if (!urb) + return; + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); +} + +int zd_usb_enable_rx(struct zd_usb *usb) +{ + int i, r; + struct zd_usb_rx *rx = &usb->rx; + struct urb **urbs; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + r = -ENOMEM; + urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_NOFS); + if (!urbs) + goto error; + for (i = 0; i < URBS_COUNT; i++) { + urbs[i] = alloc_urb(usb); + if (!urbs[i]) + goto error; + } + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&rx->lock); + if (rx->urbs) { + spin_unlock_irq(&rx->lock); + r = 0; + goto error; + } + rx->urbs = urbs; + rx->urbs_count = URBS_COUNT; + spin_unlock_irq(&rx->lock); + + for (i = 0; i < URBS_COUNT; i++) { + r = usb_submit_urb(urbs[i], GFP_NOFS); + if (r) + goto error_submit; + } + + return 0; +error_submit: + for (i = 0; i < URBS_COUNT; i++) { + usb_kill_urb(urbs[i]); + } + spin_lock_irq(&rx->lock); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irq(&rx->lock); +error: + if (urbs) { + for (i = 0; i < URBS_COUNT; i++) + free_urb(urbs[i]); + } + return r; +} + +void zd_usb_disable_rx(struct zd_usb *usb) +{ + int i; + unsigned long flags; + struct urb **urbs; + unsigned int count; + struct zd_usb_rx *rx = &usb->rx; + + spin_lock_irqsave(&rx->lock, flags); + urbs = rx->urbs; + count = rx->urbs_count; + spin_unlock_irqrestore(&rx->lock, flags); + if (!urbs) + return; + + for (i = 0; i < count; i++) { + usb_kill_urb(urbs[i]); + free_urb(urbs[i]); + } + kfree(urbs); + + spin_lock_irqsave(&rx->lock, flags); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irqrestore(&rx->lock, flags); +} + +static void tx_urb_complete(struct urb *urb, struct pt_regs *pt_regs) +{ + int r; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + break; + case -EPIPE: + usb_clear_halt(urb->dev, EP_DATA_OUT); + /* FALL-THROUGH */ + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } +free_urb: + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + return; +resubmit: + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r); + goto free_urb; + } +} + +/* Puts the frame on the USB endpoint. It doesn't wait for + * completion. The frame must contain the control set. + */ +int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length) +{ + int r; + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + void *buffer; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + r = -ENOMEM; + goto out; + } + + buffer = usb_buffer_alloc(zd_usb_to_usbdev(usb), length, GFP_ATOMIC, + &urb->transfer_dma); + if (!buffer) { + r = -ENOMEM; + goto error_free_urb; + } + memcpy(buffer, frame, length); + + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), + buffer, length, tx_urb_complete, NULL); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + goto error; + return 0; +error: + usb_buffer_free(zd_usb_to_usbdev(usb), length, buffer, + urb->transfer_dma); +error_free_urb: + usb_free_urb(urb); +out: + return r; +} + +static inline void init_usb_interrupt(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock_init(&intr->lock); + intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); + init_completion(&intr->read_regs.completion); + intr->read_regs.cr_int_addr = cpu_to_le16(usb_addr(usb, CR_INTERRUPT)); +} + +static inline void init_usb_rx(struct zd_usb *usb) +{ + struct zd_usb_rx *rx = &usb->rx; + spin_lock_init(&rx->lock); + if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) { + rx->usb_packet_size = 512; + } else { + rx->usb_packet_size = 64; + } + ZD_ASSERT(rx->fragment_length == 0); +} + +static inline void init_usb_tx(struct zd_usb *usb) +{ + /* FIXME: at this point we will allocate a fixed number of urb's for + * use in a cyclic scheme */ +} + +void zd_usb_init(struct zd_usb *usb, struct net_device *netdev, + struct usb_interface *intf) +{ + memset(usb, 0, sizeof(*usb)); + usb->intf = usb_get_intf(intf); + usb_set_intfdata(usb->intf, netdev); + init_usb_interrupt(usb); + init_usb_tx(usb); + init_usb_rx(usb); +} + +int zd_usb_init_hw(struct zd_usb *usb) +{ + int r; + struct zd_chip *chip = zd_usb_to_chip(usb); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread16_locked(chip, &usb->fw_base_offset, + USB_REG((u16)FW_BASE_ADDR_OFFSET)); + if (r) + return r; + dev_dbg_f(zd_usb_dev(usb), "fw_base_offset: %#06hx\n", + usb->fw_base_offset); + + return 0; +} + +void zd_usb_clear(struct zd_usb *usb) +{ + usb_set_intfdata(usb->intf, NULL); + usb_put_intf(usb->intf); + memset(usb, 0, sizeof(*usb)); + /* FIXME: usb_interrupt, usb_tx, usb_rx? */ +} + +static const char *speed(enum usb_device_speed speed) +{ + switch (speed) { + case USB_SPEED_LOW: + return "low"; + case USB_SPEED_FULL: + return "full"; + case USB_SPEED_HIGH: + return "high"; + default: + return "unknown speed"; + } +} + +static int scnprint_id(struct usb_device *udev, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + get_bcdDevice(udev), + speed(udev->speed)); +} + +int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size) +{ + struct usb_device *udev = interface_to_usbdev(usb->intf); + return scnprint_id(udev, buffer, size); +} + +#ifdef DEBUG +static void print_id(struct usb_device *udev) +{ + char buffer[40]; + + scnprint_id(udev, buffer, sizeof(buffer)); + buffer[sizeof(buffer)-1] = 0; + dev_dbg_f(&udev->dev, "%s\n", buffer); +} +#else +#define print_id(udev) do { } while (0) +#endif + +static int probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int r; + struct usb_device *udev = interface_to_usbdev(intf); + struct net_device *netdev = NULL; + + print_id(udev); + + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + break; + default: + dev_dbg_f(&intf->dev, "Unknown USB speed\n"); + r = -ENODEV; + goto error; + } + + netdev = zd_netdev_alloc(intf); + if (netdev == NULL) { + r = -ENOMEM; + goto error; + } + + r = upload_firmware(udev, id->driver_info); + if (r) { + dev_err(&intf->dev, + "couldn't load firmware. Error number %d\n", r); + goto error; + } + + r = usb_reset_configuration(udev); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't reset configuration. Error number %d\n", r); + goto error; + } + + /* At this point the interrupt endpoint is not generally enabled. We + * save the USB bandwidth until the network device is opened. But + * notify that the initialization of the MAC will require the + * interrupts to be temporary enabled. + */ + r = zd_mac_init_hw(zd_netdev_mac(netdev), id->driver_info); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't initialize mac. Error number %d\n", r); + goto error; + } + + r = register_netdev(netdev); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't register netdev. Error number %d\n", r); + goto error; + } + + dev_dbg_f(&intf->dev, "successful\n"); + dev_info(&intf->dev,"%s\n", netdev->name); + return 0; +error: + usb_reset_device(interface_to_usbdev(intf)); + zd_netdev_free(netdev); + return r; +} + +static void disconnect(struct usb_interface *intf) +{ + struct net_device *netdev = zd_intf_to_netdev(intf); + struct zd_mac *mac = zd_netdev_mac(netdev); + struct zd_usb *usb = &mac->chip.usb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + zd_netdev_disconnect(netdev); + + /* Just in case something has gone wrong! */ + zd_usb_disable_rx(usb); + zd_usb_disable_int(usb); + + /* If the disconnect has been caused by a removal of the + * driver module, the reset allows reloading of the driver. If the + * reset will not be executed here, the upload of the firmware in the + * probe function caused by the reloading of the driver will fail. + */ + usb_reset_device(interface_to_usbdev(intf)); + + /* If somebody still waits on this lock now, this is an error. */ + zd_netdev_free(netdev); + dev_dbg(&intf->dev, "disconnected\n"); +} + +static struct usb_driver driver = { + .name = "zd1211rw", + .id_table = usb_ids, + .probe = probe, + .disconnect = disconnect, +}; + +static int __init usb_init(void) +{ + int r; + + pr_debug("usb_init()\n"); + + r = usb_register(&driver); + if (r) { + printk(KERN_ERR "usb_register() failed. Error number %d\n", r); + return r; + } + + pr_debug("zd1211rw initialized\n"); + return 0; +} + +static void __exit usb_exit(void) +{ + pr_debug("usb_exit()\n"); + usb_deregister(&driver); +} + +module_init(usb_init); +module_exit(usb_exit); + +static int usb_int_regs_length(unsigned int count) +{ + return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); +} + +static void prepare_read_regs_int(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock(&intr->lock); + intr->read_regs_enabled = 1; + INIT_COMPLETION(intr->read_regs.completion); + spin_unlock(&intr->lock); +} + +static int get_results(struct zd_usb *usb, u16 *values, + struct usb_req_read_regs *req, unsigned int count) +{ + int r; + int i; + struct zd_usb_interrupt *intr = &usb->intr; + struct read_regs_int *rr = &intr->read_regs; + struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; + + spin_lock(&intr->lock); + + r = -EIO; + /* The created block size seems to be larger than expected. + * However results appear to be correct. + */ + if (rr->length < usb_int_regs_length(count)) { + dev_dbg_f(zd_usb_dev(usb), + "error: actual length %d less than expected %d\n", + rr->length, usb_int_regs_length(count)); + goto error_unlock; + } + if (rr->length > sizeof(rr->buffer)) { + dev_dbg_f(zd_usb_dev(usb), + "error: actual length %d exceeds buffer size %zu\n", + rr->length, sizeof(rr->buffer)); + goto error_unlock; + } + + for (i = 0; i < count; i++) { + struct reg_data *rd = ®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; +} diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h new file mode 100644 index 000000000000..d6420283bd5a --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_usb.h @@ -0,0 +1,240 @@ +/* zd_usb.h: Header for USB interface implemented by ZD1211 chip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_USB_H +#define _ZD_USB_H + +#include <linux/completion.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> +#include <linux/skbuff.h> +#include <linux/usb.h> + +#include "zd_def.h" +#include "zd_types.h" + +enum devicetype { + DEVICE_ZD1211 = 0, + DEVICE_ZD1211B = 1, +}; + +enum endpoints { + EP_CTRL = 0, + EP_DATA_OUT = 1, + EP_DATA_IN = 2, + EP_INT_IN = 3, + EP_REGS_OUT = 4, +}; + +enum { + USB_MAX_TRANSFER_SIZE = 4096, /* bytes */ + /* FIXME: The original driver uses this value. We have to check, + * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be + * used if one combined frame is split over two USB transactions. + */ + USB_MAX_RX_SIZE = 4800, /* bytes */ + USB_MAX_IOWRITE16_COUNT = 15, + USB_MAX_IOWRITE32_COUNT = USB_MAX_IOWRITE16_COUNT/2, + USB_MAX_IOREAD16_COUNT = 15, + USB_MAX_IOREAD32_COUNT = USB_MAX_IOREAD16_COUNT/2, + USB_MIN_RFWRITE_BIT_COUNT = 16, + USB_MAX_RFWRITE_BIT_COUNT = 28, + USB_MAX_EP_INT_BUFFER = 64, + USB_ZD1211B_BCD_DEVICE = 0x4810, +}; + +enum control_requests { + USB_REQ_WRITE_REGS = 0x21, + USB_REQ_READ_REGS = 0x22, + USB_REQ_WRITE_RF = 0x23, + USB_REQ_PROG_FLASH = 0x24, + USB_REQ_EEPROM_START = 0x0128, /* ? request is a byte */ + USB_REQ_EEPROM_MID = 0x28, + USB_REQ_EEPROM_END = 0x0228, /* ? request is a byte */ + USB_REQ_FIRMWARE_DOWNLOAD = 0x30, + USB_REQ_FIRMWARE_CONFIRM = 0x31, + USB_REQ_FIRMWARE_READ_DATA = 0x32, +}; + +struct usb_req_read_regs { + __le16 id; + __le16 addr[0]; +} __attribute__((packed)); + +struct reg_data { + __le16 addr; + __le16 value; +} __attribute__((packed)); + +struct usb_req_write_regs { + __le16 id; + struct reg_data reg_writes[0]; +} __attribute__((packed)); + +enum { + RF_IF_LE = 0x02, + RF_CLK = 0x04, + RF_DATA = 0x08, +}; + +struct usb_req_rfwrite { + __le16 id; + __le16 value; + /* 1: 3683a */ + /* 2: other (default) */ + __le16 bits; + /* RF2595: 24 */ + __le16 bit_values[0]; + /* (CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */ +} __attribute__((packed)); + +/* USB interrupt */ + +enum usb_int_id { + USB_INT_TYPE = 0x01, + USB_INT_ID_REGS = 0x90, + USB_INT_ID_RETRY_FAILED = 0xa0, +}; + +enum usb_int_flags { + USB_INT_READ_REGS_EN = 0x01, +}; + +struct usb_int_header { + u8 type; /* must always be 1 */ + u8 id; +} __attribute__((packed)); + +struct usb_int_regs { + struct usb_int_header hdr; + struct reg_data regs[0]; +} __attribute__((packed)); + +struct usb_int_retry_fail { + struct usb_int_header hdr; + u8 new_rate; + u8 _dummy; + u8 addr[ETH_ALEN]; + u8 ibss_wakeup_dest; +} __attribute__((packed)); + +struct read_regs_int { + struct completion completion; + /* Stores the USB int structure and contains the USB address of the + * first requested register before request. + */ + u8 buffer[USB_MAX_EP_INT_BUFFER]; + int length; + __le16 cr_int_addr; +}; + +struct zd_ioreq16 { + zd_addr_t addr; + u16 value; +}; + +struct zd_ioreq32 { + zd_addr_t addr; + u32 value; +}; + +struct zd_usb_interrupt { + struct read_regs_int read_regs; + spinlock_t lock; + struct urb *urb; + int interval; + u8 read_regs_enabled:1; +}; + +static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr) +{ + return (struct usb_int_regs *)intr->read_regs.buffer; +} + +#define URBS_COUNT 5 + +struct zd_usb_rx { + spinlock_t lock; + u8 fragment[2*USB_MAX_RX_SIZE]; + unsigned int fragment_length; + unsigned int usb_packet_size; + struct urb **urbs; + int urbs_count; +}; + +struct zd_usb_tx { + spinlock_t lock; +}; + +/* Contains the usb parts. The structure doesn't require a lock, because intf + * and fw_base_offset, will not be changed after initialization. + */ +struct zd_usb { + struct zd_usb_interrupt intr; + struct zd_usb_rx rx; + struct zd_usb_tx tx; + struct usb_interface *intf; + u16 fw_base_offset; +}; + +#define zd_usb_dev(usb) (&usb->intf->dev) + +static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb) +{ + return interface_to_usbdev(usb->intf); +} + +static inline struct net_device *zd_intf_to_netdev(struct usb_interface *intf) +{ + return usb_get_intfdata(intf); +} + +static inline struct net_device *zd_usb_to_netdev(struct zd_usb *usb) +{ + return zd_intf_to_netdev(usb->intf); +} + +void zd_usb_init(struct zd_usb *usb, struct net_device *netdev, + struct usb_interface *intf); +int zd_usb_init_hw(struct zd_usb *usb); +void zd_usb_clear(struct zd_usb *usb); + +int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size); + +int zd_usb_enable_int(struct zd_usb *usb); +void zd_usb_disable_int(struct zd_usb *usb); + +int zd_usb_enable_rx(struct zd_usb *usb); +void zd_usb_disable_rx(struct zd_usb *usb); + +int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length); + +int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, + const zd_addr_t *addresses, unsigned int count); + +static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value, + const zd_addr_t addr) +{ + return zd_usb_ioread16v(usb, value, (const zd_addr_t *)&addr, 1); +} + +int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count); + +int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits); + +#endif /* _ZD_USB_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_util.c b/drivers/net/wireless/zd1211rw/zd_util.c new file mode 100644 index 000000000000..d20036c15d11 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_util.c @@ -0,0 +1,82 @@ +/* zd_util.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Utility program + */ + +#include "zd_def.h" +#include "zd_util.h" + +#ifdef DEBUG +static char hex(u8 v) +{ + v &= 0xf; + return (v < 10 ? '0' : 'a' - 10) + v; +} + +static char hex_print(u8 c) +{ + return (0x20 <= c && c < 0x7f) ? c : '.'; +} + +static void dump_line(const u8 *bytes, size_t size) +{ + char c; + size_t i; + + size = size <= 8 ? size : 8; + printk(KERN_DEBUG "zd1211 %p ", bytes); + for (i = 0; i < 8; i++) { + switch (i) { + case 1: + case 5: + c = '.'; + break; + case 3: + c = ':'; + break; + default: + c = ' '; + } + if (i < size) { + printk("%c%c%c", hex(bytes[i] >> 4), hex(bytes[i]), c); + } else { + printk(" %c", c); + } + } + + for (i = 0; i < size; i++) + printk("%c", hex_print(bytes[i])); + printk("\n"); +} + +void zd_hexdump(const void *bytes, size_t size) +{ + size_t i = 0; + + do { + dump_line((u8 *)bytes + i, size-i); + i += 8; + } while (i < size); +} +#endif /* DEBUG */ + +void *zd_tail(const void *buffer, size_t buffer_size, size_t tail_size) +{ + if (buffer_size < tail_size) + return NULL; + return (u8 *)buffer + (buffer_size - tail_size); +} diff --git a/drivers/net/wireless/zd1211rw/zd_util.h b/drivers/net/wireless/zd1211rw/zd_util.h new file mode 100644 index 000000000000..ce26f7adea92 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_util.h @@ -0,0 +1,29 @@ +/* zd_util.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_UTIL_H +#define _ZD_UTIL_H + +void *zd_tail(const void *buffer, size_t buffer_size, size_t tail_size); + +#ifdef DEBUG +void zd_hexdump(const void *bytes, size_t size); +#else +#define zd_hexdump(bytes, size) +#endif /* DEBUG */ + +#endif /* _ZD_UTIL_H */ |