diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-20 21:04:44 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-20 21:04:44 -0700 |
commit | f8965467f366fd18f01feafb5db10512d7b4422c (patch) | |
tree | 3706a9cd779859271ca61b85c63a1bc3f82d626e /drivers/net/wireless/orinoco | |
parent | a26272e5200765691e67d6780e52b32498fdb659 (diff) | |
parent | 2ec8c6bb5d8f3a62a79f463525054bae1e3d4487 (diff) | |
download | op-kernel-dev-f8965467f366fd18f01feafb5db10512d7b4422c.zip op-kernel-dev-f8965467f366fd18f01feafb5db10512d7b4422c.tar.gz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1674 commits)
qlcnic: adding co maintainer
ixgbe: add support for active DA cables
ixgbe: dcb, do not tag tc_prio_control frames
ixgbe: fix ixgbe_tx_is_paused logic
ixgbe: always enable vlan strip/insert when DCB is enabled
ixgbe: remove some redundant code in setting FCoE FIP filter
ixgbe: fix wrong offset to fc_frame_header in ixgbe_fcoe_ddp
ixgbe: fix header len when unsplit packet overflows to data buffer
ipv6: Never schedule DAD timer on dead address
ipv6: Use POSTDAD state
ipv6: Use state_lock to protect ifa state
ipv6: Replace inet6_ifaddr->dead with state
cxgb4: notify upper drivers if the device is already up when they load
cxgb4: keep interrupts available when the ports are brought down
cxgb4: fix initial addition of MAC address
cnic: Return SPQ credit to bnx2x after ring setup and shutdown.
cnic: Convert cnic_local_flags to atomic ops.
can: Fix SJA1000 command register writes on SMP systems
bridge: fix build for CONFIG_SYSFS disabled
ARCNET: Limit com20020 PCI ID matches for SOHARD cards
...
Fix up various conflicts with pcmcia tree drivers/net/
{pcmcia/3c589_cs.c, wireless/orinoco/orinoco_cs.c and
wireless/orinoco/spectrum_cs.c} and feature removal
(Documentation/feature-removal-schedule.txt).
Also fix a non-content conflict due to pm_qos_requirement getting
renamed in the PM tree (now pm_qos_request) in net/mac80211/scan.c
Diffstat (limited to 'drivers/net/wireless/orinoco')
22 files changed, 2614 insertions, 742 deletions
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index e2a2c18..60819bc 100644 --- a/drivers/net/wireless/orinoco/Kconfig +++ b/drivers/net/wireless/orinoco/Kconfig @@ -27,6 +27,17 @@ config HERMES configure your card and that /etc/pcmcia/wireless.opts works : <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html> +config HERMES_PRISM + bool "Support Prism 2/2.5 chipset" + depends on HERMES + ---help--- + + Say Y to enable support for Prism 2 and 2.5 chipsets. These + chipsets are better handled by the hostap driver. This driver + would not support WPA or firmware download for Prism chipset. + + If you are not sure, say N. + config HERMES_CACHE_FW_ON_INIT bool "Cache Hermes firmware on driver initialisation" depends on HERMES @@ -86,7 +97,7 @@ config NORTEL_HERMES config PCI_HERMES tristate "Prism 2.5 PCI 802.11b adaptor support" - depends on PCI && HERMES + depends on PCI && HERMES && HERMES_PRISM help Enable support for PCI and mini-PCI 802.11b wireless NICs based on the Prism 2.5 chipset. These are true PCI cards, not the 802.11b @@ -121,3 +132,10 @@ config PCMCIA_SPECTRUM This driver requires firmware download on startup. Utilities for downloading Symbol firmware are available at <http://sourceforge.net/projects/orinoco/> + +config ORINOCO_USB + tristate "Agere Orinoco USB support" + depends on USB && HERMES + select FW_LOADER + ---help--- + This driver is for USB versions of the Agere Orinoco card. diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/orinoco/Makefile index 9abd632..bfdefb8 100644 --- a/drivers/net/wireless/orinoco/Makefile +++ b/drivers/net/wireless/orinoco/Makefile @@ -11,3 +11,7 @@ obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o +obj-$(CONFIG_ORINOCO_USB) += orinoco_usb.o + +# Orinoco should be endian clean. +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c index c60df2c..9bcee10 100644 --- a/drivers/net/wireless/orinoco/airport.c +++ b/drivers/net/wireless/orinoco/airport.c @@ -77,9 +77,9 @@ airport_resume(struct macio_dev *mdev) enable_irq(card->irq); - spin_lock_irqsave(&priv->lock, flags); + priv->hw.ops->lock_irqsave(&priv->lock, &flags); err = orinoco_up(priv); - spin_unlock_irqrestore(&priv->lock, flags); + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); return err; } @@ -195,7 +195,7 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match) ssleep(1); /* Reset it before we get the interrupt */ - hermes_init(hw); + hw->ops->init(hw); if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) { printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq); @@ -210,7 +210,7 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match) } /* Register an interface with the stack */ - if (orinoco_if_add(priv, phys_addr, card->irq) != 0) { + if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index 27f2d33..8c4169c 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -88,7 +88,9 @@ int orinoco_wiphy_register(struct wiphy *wiphy) wiphy->rts_threshold = priv->rts_thresh; if (!priv->has_mwo) - wiphy->frag_threshold = priv->frag_thresh; + wiphy->frag_threshold = priv->frag_thresh + 1; + wiphy->retry_short = priv->short_retry_limit; + wiphy->retry_long = priv->long_retry_limit; return wiphy_register(wiphy); } @@ -157,6 +159,7 @@ static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev, } static int orinoco_set_channel(struct wiphy *wiphy, + struct net_device *netdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { @@ -187,7 +190,7 @@ static int orinoco_set_channel(struct wiphy *wiphy, if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { /* Fast channel change - no commit if successful */ hermes_t *hw = &priv->hw; - err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_SET_CHANNEL, channel, NULL); } @@ -196,8 +199,92 @@ static int orinoco_set_channel(struct wiphy *wiphy, return err; } +static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int frag_value = -1; + int rts_value = -1; + int err = 0; + + if (changed & WIPHY_PARAM_RETRY_SHORT) { + /* Setting short retry not supported */ + err = -EINVAL; + } + + if (changed & WIPHY_PARAM_RETRY_LONG) { + /* Setting long retry not supported */ + err = -EINVAL; + } + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + /* Set fragmentation */ + if (priv->has_mwo) { + if (wiphy->frag_threshold < 0) + frag_value = 0; + else { + printk(KERN_WARNING "%s: Fixed fragmentation " + "is not supported on this firmware. " + "Using MWO robust instead.\n", + priv->ndev->name); + frag_value = 1; + } + } else { + if (wiphy->frag_threshold < 0) + frag_value = 2346; + else if ((wiphy->frag_threshold < 257) || + (wiphy->frag_threshold > 2347)) + err = -EINVAL; + else + /* cfg80211 value is 257-2347 (odd only) + * orinoco rid has range 256-2346 (even only) */ + frag_value = wiphy->frag_threshold & ~0x1; + } + } + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + /* Set RTS. + * + * Prism documentation suggests default of 2432, + * and a range of 0-3000. + * + * Current implementation uses 2347 as the default and + * the upper limit. + */ + + if (wiphy->rts_threshold < 0) + rts_value = 2347; + else if (wiphy->rts_threshold > 2347) + err = -EINVAL; + else + rts_value = wiphy->rts_threshold; + } + + if (!err) { + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if (frag_value >= 0) { + if (priv->has_mwo) + priv->mwo_robust = frag_value; + else + priv->frag_thresh = frag_value; + } + if (rts_value >= 0) + priv->rts_thresh = rts_value; + + err = orinoco_commit(priv); + + orinoco_unlock(priv, &flags); + } + + return err; +} + const struct cfg80211_ops orinoco_cfg_ops = { .change_virtual_intf = orinoco_change_vif, .set_channel = orinoco_set_channel, .scan = orinoco_scan, + .set_wiphy_params = orinoco_set_wiphy_params, }; diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/orinoco/fw.c index 5ea0f7c..3e1947d 100644 --- a/drivers/net/wireless/orinoco/fw.c +++ b/drivers/net/wireless/orinoco/fw.c @@ -122,7 +122,7 @@ orinoco_dl_firmware(struct orinoco_private *priv, dev_dbg(dev, "Attempting to download firmware %s\n", firmware); /* Read current plug data */ - err = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 0); + err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); dev_dbg(dev, "Read PDA returned %d\n", err); if (err) goto free; @@ -149,7 +149,7 @@ orinoco_dl_firmware(struct orinoco_private *priv, } /* Enable aux port to allow programming */ - err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point)); + err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point)); dev_dbg(dev, "Program init returned %d\n", err); if (err != 0) goto abort; @@ -177,7 +177,7 @@ orinoco_dl_firmware(struct orinoco_private *priv, goto abort; /* Tell card we've finished */ - err = hermesi_program_end(hw); + err = hw->ops->program_end(hw); dev_dbg(dev, "Program end returned %d\n", err); if (err != 0) goto abort; @@ -224,7 +224,7 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, if (!pda) return -ENOMEM; - ret = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 1); + ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); if (ret) goto free; } @@ -260,7 +260,7 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, } /* Reset hermes chip and make sure it responds */ - ret = hermes_init(hw); + ret = hw->ops->init(hw); /* hermes_reset() should return 0 with the secondary firmware */ if (secondary && ret != 0) diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c index 1a2fca7..6c6a23e 100644 --- a/drivers/net/wireless/orinoco/hermes.c +++ b/drivers/net/wireless/orinoco/hermes.c @@ -52,6 +52,26 @@ #define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ /* + * AUX port access. To unlock the AUX port write the access keys to the + * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL + * register. Then read it and make sure it's HERMES_AUX_ENABLED. + */ +#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ +#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ +#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ +#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ + +#define HERMES_AUX_PW0 0xFE01 +#define HERMES_AUX_PW1 0xDC23 +#define HERMES_AUX_PW2 0xBA45 + +/* HERMES_CMD_DOWNLD */ +#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) + +/* * Debugging helpers */ @@ -70,6 +90,7 @@ #endif /* ! HERMES_DEBUG */ +static const struct hermes_ops hermes_ops_local; /* * Internal functions @@ -111,9 +132,9 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0, */ /* For doing cmds that wipe the magic constant in SWSUPPORT0 */ -int hermes_doicmd_wait(hermes_t *hw, u16 cmd, - u16 parm0, u16 parm1, u16 parm2, - struct hermes_response *resp) +static int hermes_doicmd_wait(hermes_t *hw, u16 cmd, + u16 parm0, u16 parm1, u16 parm2, + struct hermes_response *resp) { int err = 0; int k; @@ -163,17 +184,18 @@ int hermes_doicmd_wait(hermes_t *hw, u16 cmd, out: return err; } -EXPORT_SYMBOL(hermes_doicmd_wait); void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing) { hw->iobase = address; hw->reg_spacing = reg_spacing; hw->inten = 0x0; + hw->eeprom_pda = false; + hw->ops = &hermes_ops_local; } EXPORT_SYMBOL(hermes_struct_init); -int hermes_init(hermes_t *hw) +static int hermes_init(hermes_t *hw) { u16 reg; int err = 0; @@ -217,7 +239,6 @@ int hermes_init(hermes_t *hw) return err; } -EXPORT_SYMBOL(hermes_init); /* Issue a command to the chip, and (busy!) wait for it to * complete. @@ -228,8 +249,8 @@ EXPORT_SYMBOL(hermes_init); * > 0 on error returned by the firmware * * Callable from any context, but locking is your problem. */ -int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, - struct hermes_response *resp) +static int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, + struct hermes_response *resp) { int err; int k; @@ -291,9 +312,8 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, out: return err; } -EXPORT_SYMBOL(hermes_docmd_wait); -int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) +static int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) { int err = 0; int k; @@ -333,7 +353,6 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) return 0; } -EXPORT_SYMBOL(hermes_allocate); /* Set up a BAP to read a particular chunk of data from card's internal buffer. * @@ -403,8 +422,8 @@ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) * 0 on success * > 0 on error from firmware */ -int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, - u16 id, u16 offset) +static int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, + u16 id, u16 offset) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; @@ -422,7 +441,6 @@ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, out: return err; } -EXPORT_SYMBOL(hermes_bap_pread); /* Write a block of data to the chip's buffer, via the * BAP. Synchronization/serialization is the caller's problem. @@ -432,8 +450,8 @@ EXPORT_SYMBOL(hermes_bap_pread); * 0 on success * > 0 on error from firmware */ -int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, - u16 id, u16 offset) +static int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, + u16 id, u16 offset) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; @@ -451,7 +469,6 @@ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, out: return err; } -EXPORT_SYMBOL(hermes_bap_pwrite); /* Read a Length-Type-Value record from the card. * @@ -461,8 +478,8 @@ EXPORT_SYMBOL(hermes_bap_pwrite); * practice. * * Callable from user or bh context. */ -int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, - u16 *length, void *buf) +static int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, + u16 *length, void *buf) { int err = 0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; @@ -505,10 +522,9 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, return 0; } -EXPORT_SYMBOL(hermes_read_ltv); -int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, - u16 length, const void *value) +static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, + u16 length, const void *value) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; @@ -533,4 +549,228 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, return err; } -EXPORT_SYMBOL(hermes_write_ltv); + +/*** Hermes AUX control ***/ + +static inline void +hermes_aux_setaddr(hermes_t *hw, u32 addr) +{ + hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); + hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); +} + +static inline int +hermes_aux_control(hermes_t *hw, int enabled) +{ + int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; + int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; + int i; + + /* Already open? */ + if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) + return 0; + + hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); + hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); + hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); + hermes_write_reg(hw, HERMES_CONTROL, action); + + for (i = 0; i < 20; i++) { + udelay(10); + if (hermes_read_reg(hw, HERMES_CONTROL) == + desired_state) + return 0; + } + + return -EBUSY; +} + +/*** Hermes programming ***/ + +/* About to start programming data (Hermes I) + * offset is the entry point + * + * Spectrum_cs' Symbol fw does not require this + * wl_lkm Agere fw does + * Don't know about intersil + */ +static int hermesi_program_init(hermes_t *hw, u32 offset) +{ + int err; + + /* Disable interrupts?*/ + /*hw->inten = 0x0;*/ + /*hermes_write_regn(hw, INTEN, 0);*/ + /*hermes_set_irqmask(hw, 0);*/ + + /* Acknowledge any outstanding command */ + hermes_write_regn(hw, EVACK, 0xFFFF); + + /* Using init_cmd_wait rather than cmd_wait */ + err = hw->ops->init_cmd_wait(hw, + 0x0100 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + if (err) + return err; + + err = hw->ops->init_cmd_wait(hw, + 0x0000 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + if (err) + return err; + + err = hermes_aux_control(hw, 1); + pr_debug("AUX enable returned %d\n", err); + + if (err) + return err; + + pr_debug("Enabling volatile, EP 0x%08x\n", offset); + err = hw->ops->init_cmd_wait(hw, + HERMES_PROGRAM_ENABLE_VOLATILE, + offset & 0xFFFFu, + offset >> 16, + 0, + NULL); + pr_debug("PROGRAM_ENABLE returned %d\n", err); + + return err; +} + +/* Done programming data (Hermes I) + * + * Spectrum_cs' Symbol fw does not require this + * wl_lkm Agere fw does + * Don't know about intersil + */ +static int hermesi_program_end(hermes_t *hw) +{ + struct hermes_response resp; + int rc = 0; + int err; + + rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); + + pr_debug("PROGRAM_DISABLE returned %d, " + "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", + rc, resp.resp0, resp.resp1, resp.resp2); + + if ((rc == 0) && + ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) + rc = -EIO; + + err = hermes_aux_control(hw, 0); + pr_debug("AUX disable returned %d\n", err); + + /* Acknowledge any outstanding command */ + hermes_write_regn(hw, EVACK, 0xFFFF); + + /* Reinitialise, ignoring return */ + (void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + + return rc ? rc : err; +} + +static int hermes_program_bytes(struct hermes *hw, const char *data, + u32 addr, u32 len) +{ + /* wl lkm splits the programming into chunks of 2000 bytes. + * This restriction appears to come from USB. The PCMCIA + * adapters can program the whole lot in one go */ + hermes_aux_setaddr(hw, addr); + hermes_write_bytes(hw, HERMES_AUXDATA, data, len); + return 0; +} + +/* Read PDA from the adapter */ +static int hermes_read_pda(hermes_t *hw, __le16 *pda, u32 pda_addr, u16 pda_len) +{ + int ret; + u16 pda_size; + u16 data_len = pda_len; + __le16 *data = pda; + + if (hw->eeprom_pda) { + /* PDA of spectrum symbol is in eeprom */ + + /* Issue command to read EEPROM */ + ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); + if (ret) + return ret; + } else { + /* wl_lkm does not include PDA size in the PDA area. + * We will pad the information into pda, so other routines + * don't have to be modified */ + pda[0] = cpu_to_le16(pda_len - 2); + /* Includes CFG_PROD_DATA but not itself */ + pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ + data_len = pda_len - 4; + data = pda + 2; + } + + /* Open auxiliary port */ + ret = hermes_aux_control(hw, 1); + pr_debug("AUX enable returned %d\n", ret); + if (ret) + return ret; + + /* Read PDA */ + hermes_aux_setaddr(hw, pda_addr); + hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2); + + /* Close aux port */ + ret = hermes_aux_control(hw, 0); + pr_debug("AUX disable returned %d\n", ret); + + /* Check PDA length */ + pda_size = le16_to_cpu(pda[0]); + pr_debug("Actual PDA length %d, Max allowed %d\n", + pda_size, pda_len); + if (pda_size > pda_len) + return -EINVAL; + + return 0; +} + +static void hermes_lock_irqsave(spinlock_t *lock, + unsigned long *flags) __acquires(lock) +{ + spin_lock_irqsave(lock, *flags); +} + +static void hermes_unlock_irqrestore(spinlock_t *lock, + unsigned long *flags) __releases(lock) +{ + spin_unlock_irqrestore(lock, *flags); +} + +static void hermes_lock_irq(spinlock_t *lock) __acquires(lock) +{ + spin_lock_irq(lock); +} + +static void hermes_unlock_irq(spinlock_t *lock) __releases(lock) +{ + spin_unlock_irq(lock); +} + +/* Hermes operations for local buses */ +static const struct hermes_ops hermes_ops_local = { + .init = hermes_init, + .cmd_wait = hermes_docmd_wait, + .init_cmd_wait = hermes_doicmd_wait, + .allocate = hermes_allocate, + .read_ltv = hermes_read_ltv, + .write_ltv = hermes_write_ltv, + .bap_pread = hermes_bap_pread, + .bap_pwrite = hermes_bap_pwrite, + .read_pda = hermes_read_pda, + .program_init = hermesi_program_init, + .program_end = hermesi_program_end, + .program = hermes_program_bytes, + .lock_irqsave = hermes_lock_irqsave, + .unlock_irqrestore = hermes_unlock_irqrestore, + .lock_irq = hermes_lock_irq, + .unlock_irq = hermes_unlock_irq, +}; diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/orinoco/hermes.h index 2dddbb5..9ca34e7 100644 --- a/drivers/net/wireless/orinoco/hermes.h +++ b/drivers/net/wireless/orinoco/hermes.h @@ -374,6 +374,37 @@ struct hermes_multicast { /* Timeouts */ #define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */ +struct hermes; + +/* Functions to access hardware */ +struct hermes_ops { + int (*init)(struct hermes *hw); + int (*cmd_wait)(struct hermes *hw, u16 cmd, u16 parm0, + struct hermes_response *resp); + int (*init_cmd_wait)(struct hermes *hw, u16 cmd, + u16 parm0, u16 parm1, u16 parm2, + struct hermes_response *resp); + int (*allocate)(struct hermes *hw, u16 size, u16 *fid); + int (*read_ltv)(struct hermes *hw, int bap, u16 rid, unsigned buflen, + u16 *length, void *buf); + int (*write_ltv)(struct hermes *hw, int bap, u16 rid, + u16 length, const void *value); + int (*bap_pread)(struct hermes *hw, int bap, void *buf, int len, + u16 id, u16 offset); + int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf, + int len, u16 id, u16 offset); + int (*read_pda)(struct hermes *hw, __le16 *pda, + u32 pda_addr, u16 pda_len); + int (*program_init)(struct hermes *hw, u32 entry_point); + int (*program_end)(struct hermes *hw); + int (*program)(struct hermes *hw, const char *buf, + u32 addr, u32 len); + void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags); + void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags); + void (*lock_irq)(spinlock_t *lock); + void (*unlock_irq)(spinlock_t *lock); +}; + /* Basic control structure */ typedef struct hermes { void __iomem *iobase; @@ -381,6 +412,9 @@ typedef struct hermes { #define HERMES_16BIT_REGSPACING 0 #define HERMES_32BIT_REGSPACING 1 u16 inten; /* Which interrupts should be enabled? */ + bool eeprom_pda; + const struct hermes_ops *ops; + void *priv; } hermes_t; /* Register access convenience macros */ @@ -394,22 +428,6 @@ typedef struct hermes { /* Function prototypes */ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing); -int hermes_init(hermes_t *hw); -int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, - struct hermes_response *resp); -int hermes_doicmd_wait(hermes_t *hw, u16 cmd, - u16 parm0, u16 parm1, u16 parm2, - struct hermes_response *resp); -int hermes_allocate(hermes_t *hw, u16 size, u16 *fid); - -int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, - u16 id, u16 offset); -int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, - u16 id, u16 offset); -int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned buflen, - u16 *length, void *buf); -int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, - u16 length, const void *value); /* Inline functions */ @@ -426,13 +444,13 @@ static inline void hermes_set_irqmask(hermes_t *hw, u16 events) static inline int hermes_enable_port(hermes_t *hw, int port) { - return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), + return hw->ops->cmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), 0, NULL); } static inline int hermes_disable_port(hermes_t *hw, int port) { - return hermes_docmd_wait(hw, HERMES_CMD_DISABLE | (port << 8), + return hw->ops->cmd_wait(hw, HERMES_CMD_DISABLE | (port << 8), 0, NULL); } @@ -440,7 +458,7 @@ static inline int hermes_disable_port(hermes_t *hw, int port) * information frame in __orinoco_ev_info() */ static inline int hermes_inquire(hermes_t *hw, u16 rid) { - return hermes_docmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL); + return hw->ops->cmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL); } #define HERMES_BYTES_TO_RECLEN(n) ((((n)+1)/2) + 1) @@ -475,10 +493,10 @@ static inline void hermes_clear_words(struct hermes *hw, int off, } #define HERMES_READ_RECORD(hw, bap, rid, buf) \ - (hermes_read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf))) + (hw->ops->read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf))) #define HERMES_WRITE_RECORD(hw, bap, rid, buf) \ - (hermes_write_ltv((hw), (bap), (rid), \ - HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf))) + (hw->ops->write_ltv((hw), (bap), (rid), \ + HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf))) static inline int hermes_read_wordrec(hermes_t *hw, int bap, u16 rid, u16 *word) { diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c index fb157eb..6da85e7 100644 --- a/drivers/net/wireless/orinoco/hermes_dld.c +++ b/drivers/net/wireless/orinoco/hermes_dld.c @@ -46,37 +46,11 @@ #define PFX "hermes_dld: " -/* - * AUX port access. To unlock the AUX port write the access keys to the - * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL - * register. Then read it and make sure it's HERMES_AUX_ENABLED. - */ -#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ -#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ -#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ -#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ - -#define HERMES_AUX_PW0 0xFE01 -#define HERMES_AUX_PW1 0xDC23 -#define HERMES_AUX_PW2 0xBA45 - -/* HERMES_CMD_DOWNLD */ -#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) -#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) -#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) -#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) - /* End markers used in dblocks */ #define PDI_END 0x00000000 /* End of PDA */ #define BLOCK_END 0xFFFFFFFF /* Last image block */ #define TEXT_END 0x1A /* End of text header */ -/* Limit the amout we try to download in a single shot. - * Size is in bytes. - */ -#define MAX_DL_SIZE 1024 -#define LIMIT_PROGRAM_SIZE 0 - /* * The following structures have little-endian fields denoted by * the leading underscore. Don't access them directly - use inline @@ -165,41 +139,6 @@ pdi_len(const struct pdi *pdi) return 2 * (le16_to_cpu(pdi->len) - 1); } -/*** Hermes AUX control ***/ - -static inline void -hermes_aux_setaddr(hermes_t *hw, u32 addr) -{ - hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); - hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); -} - -static inline int -hermes_aux_control(hermes_t *hw, int enabled) -{ - int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; - int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; - int i; - - /* Already open? */ - if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) - return 0; - - hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); - hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); - hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); - hermes_write_reg(hw, HERMES_CONTROL, action); - - for (i = 0; i < 20; i++) { - udelay(10); - if (hermes_read_reg(hw, HERMES_CONTROL) == - desired_state) - return 0; - } - - return -EBUSY; -} - /*** Plug Data Functions ***/ /* @@ -271,62 +210,7 @@ hermes_plug_pdi(hermes_t *hw, const struct pdr *first_pdr, return -EINVAL; /* do the actual plugging */ - hermes_aux_setaddr(hw, pdr_addr(pdr)); - hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); - - return 0; -} - -/* Read PDA from the adapter */ -int hermes_read_pda(hermes_t *hw, - __le16 *pda, - u32 pda_addr, - u16 pda_len, - int use_eeprom) /* can we get this into hw? */ -{ - int ret; - u16 pda_size; - u16 data_len = pda_len; - __le16 *data = pda; - - if (use_eeprom) { - /* PDA of spectrum symbol is in eeprom */ - - /* Issue command to read EEPROM */ - ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); - if (ret) - return ret; - } else { - /* wl_lkm does not include PDA size in the PDA area. - * We will pad the information into pda, so other routines - * don't have to be modified */ - pda[0] = cpu_to_le16(pda_len - 2); - /* Includes CFG_PROD_DATA but not itself */ - pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ - data_len = pda_len - 4; - data = pda + 2; - } - - /* Open auxiliary port */ - ret = hermes_aux_control(hw, 1); - pr_debug(PFX "AUX enable returned %d\n", ret); - if (ret) - return ret; - - /* read PDA from EEPROM */ - hermes_aux_setaddr(hw, pda_addr); - hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2); - - /* Close aux port */ - ret = hermes_aux_control(hw, 0); - pr_debug(PFX "AUX disable returned %d\n", ret); - - /* Check PDA length */ - pda_size = le16_to_cpu(pda[0]); - pr_debug(PFX "Actual PDA length %d, Max allowed %d\n", - pda_size, pda_len); - if (pda_size > pda_len) - return -EINVAL; + hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi)); return 0; } @@ -389,101 +273,13 @@ hermes_blocks_length(const char *first_block, const void *end) /*** Hermes programming ***/ -/* About to start programming data (Hermes I) - * offset is the entry point - * - * Spectrum_cs' Symbol fw does not require this - * wl_lkm Agere fw does - * Don't know about intersil - */ -int hermesi_program_init(hermes_t *hw, u32 offset) -{ - int err; - - /* Disable interrupts?*/ - /*hw->inten = 0x0;*/ - /*hermes_write_regn(hw, INTEN, 0);*/ - /*hermes_set_irqmask(hw, 0);*/ - - /* Acknowledge any outstanding command */ - hermes_write_regn(hw, EVACK, 0xFFFF); - - /* Using doicmd_wait rather than docmd_wait */ - err = hermes_doicmd_wait(hw, - 0x0100 | HERMES_CMD_INIT, - 0, 0, 0, NULL); - if (err) - return err; - - err = hermes_doicmd_wait(hw, - 0x0000 | HERMES_CMD_INIT, - 0, 0, 0, NULL); - if (err) - return err; - - err = hermes_aux_control(hw, 1); - pr_debug(PFX "AUX enable returned %d\n", err); - - if (err) - return err; - - pr_debug(PFX "Enabling volatile, EP 0x%08x\n", offset); - err = hermes_doicmd_wait(hw, - HERMES_PROGRAM_ENABLE_VOLATILE, - offset & 0xFFFFu, - offset >> 16, - 0, - NULL); - pr_debug(PFX "PROGRAM_ENABLE returned %d\n", err); - - return err; -} - -/* Done programming data (Hermes I) - * - * Spectrum_cs' Symbol fw does not require this - * wl_lkm Agere fw does - * Don't know about intersil - */ -int hermesi_program_end(hermes_t *hw) -{ - struct hermes_response resp; - int rc = 0; - int err; - - rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); - - pr_debug(PFX "PROGRAM_DISABLE returned %d, " - "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", - rc, resp.resp0, resp.resp1, resp.resp2); - - if ((rc == 0) && - ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) - rc = -EIO; - - err = hermes_aux_control(hw, 0); - pr_debug(PFX "AUX disable returned %d\n", err); - - /* Acknowledge any outstanding command */ - hermes_write_regn(hw, EVACK, 0xFFFF); - - /* Reinitialise, ignoring return */ - (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT, - 0, 0, 0, NULL); - - return rc ? rc : err; -} - /* Program the data blocks */ int hermes_program(hermes_t *hw, const char *first_block, const void *end) { const struct dblock *blk; u32 blkaddr; u32 blklen; -#if LIMIT_PROGRAM_SIZE - u32 addr; - u32 len; -#endif + int err = 0; blk = (const struct dblock *) first_block; @@ -498,30 +294,10 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end) pr_debug(PFX "Programming block of length %d " "to address 0x%08x\n", blklen, blkaddr); -#if !LIMIT_PROGRAM_SIZE - /* wl_lkm driver splits this into writes of 2000 bytes */ - hermes_aux_setaddr(hw, blkaddr); - hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, - blklen); -#else - len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE; - addr = blkaddr; - - while (addr < (blkaddr + blklen)) { - pr_debug(PFX "Programming subblock of length %d " - "to address 0x%08x. Data @ %p\n", - len, addr, &blk->data[addr - blkaddr]); - - hermes_aux_setaddr(hw, addr); - hermes_write_bytes(hw, HERMES_AUXDATA, - &blk->data[addr - blkaddr], - len); - - addr += len; - len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ? - (blkaddr + blklen - addr) : MAX_DL_SIZE; - } -#endif + err = hw->ops->program(hw, blk->data, blkaddr, blklen); + if (err) + break; + blk = (const struct dblock *) &blk->data[blklen]; if ((void *) blk > (end - sizeof(*blk))) @@ -530,7 +306,7 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end) blkaddr = dblock_addr(blk); blklen = dblock_len(blk); } - return 0; + return err; } /*** Default plugging data for Hermes I ***/ @@ -690,9 +466,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, if ((pdi_len(pdi) == pdr_len(pdr)) && ((void *) pdi->data + pdi_len(pdi) < pda_end)) { /* do the actual plugging */ - hermes_aux_setaddr(hw, pdr_addr(pdr)); - hermes_write_bytes(hw, HERMES_AUXDATA, - pdi->data, pdi_len(pdi)); + hw->ops->program(hw, pdi->data, pdr_addr(pdr), + pdi_len(pdi)); } } diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c index e636924..6fbd788 100644 --- a/drivers/net/wireless/orinoco/hw.c +++ b/drivers/net/wireless/orinoco/hw.c @@ -177,9 +177,9 @@ int determine_fw_capabilities(struct orinoco_private *priv, /* 3Com MAC : 00:50:DA:* */ memset(tmp, 0, sizeof(tmp)); /* Get the Symbol firmware version */ - err = hermes_read_ltv(hw, USER_BAP, - HERMES_RID_SECONDARYVERSION_SYMBOL, - SYMBOL_MAX_VER_LEN, NULL, &tmp); + err = hw->ops->read_ltv(hw, USER_BAP, + HERMES_RID_SECONDARYVERSION_SYMBOL, + SYMBOL_MAX_VER_LEN, NULL, &tmp); if (err) { dev_warn(dev, "Error %d reading Symbol firmware info. " "Wildly guessing capabilities...\n", err); @@ -262,6 +262,13 @@ int determine_fw_capabilities(struct orinoco_private *priv, if (fw_name) dev_info(dev, "Firmware determined as %s\n", fw_name); +#ifndef CONFIG_HERMES_PRISM + if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { + dev_err(dev, "Support for Prism chipset is not enabled\n"); + return -ENODEV; + } +#endif + return 0; } @@ -279,8 +286,8 @@ int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) u16 reclen; /* Get the MAC address */ - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, - ETH_ALEN, NULL, dev_addr); + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + ETH_ALEN, NULL, dev_addr); if (err) { dev_warn(dev, "Failed to read MAC address!\n"); goto out; @@ -289,8 +296,8 @@ int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) dev_dbg(dev, "MAC address %pM\n", dev_addr); /* Get the station name */ - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, - sizeof(nickbuf), &reclen, &nickbuf); + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, + sizeof(nickbuf), &reclen, &nickbuf); if (err) { dev_err(dev, "failed to read station name\n"); goto out; @@ -367,6 +374,32 @@ int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL, &priv->preamble); + if (err) { + dev_err(dev, "Failed to read preamble setup\n"); + goto out; + } + } + + /* Retry settings */ + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, + &priv->short_retry_limit); + if (err) { + dev_err(dev, "Failed to read short retry limit\n"); + goto out; + } + + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, + &priv->long_retry_limit); + if (err) { + dev_err(dev, "Failed to read long retry limit\n"); + goto out; + } + + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, + &priv->retry_lifetime); + if (err) { + dev_err(dev, "Failed to read max retry lifetime\n"); + goto out; } out: @@ -380,11 +413,11 @@ int orinoco_hw_allocate_fid(struct orinoco_private *priv) struct hermes *hw = &priv->hw; int err; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { /* Try workaround for old Symbol firmware bug */ priv->nicbuf_size = TX_NICBUF_SIZE_BUG; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); dev_warn(dev, "Firmware ALLOC bug detected " "(old Symbol firmware?). Work around %s\n", @@ -430,8 +463,9 @@ int orinoco_hw_program_rids(struct orinoco_private *priv) struct hermes_idstring idbuf; /* Set the MAC address */ - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, - HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + HERMES_BYTES_TO_RECLEN(ETH_ALEN), + dev->dev_addr); if (err) { printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err); @@ -494,7 +528,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv) idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); if (err) { @@ -502,7 +536,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv) dev->name, err); return err; } - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); if (err) { @@ -514,9 +548,9 @@ int orinoco_hw_program_rids(struct orinoco_private *priv) /* Set the station name */ idbuf.len = cpu_to_le16(strlen(priv->nick)); memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, - HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), - &idbuf); + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, + HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), + &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err); @@ -631,12 +665,12 @@ int orinoco_hw_program_rids(struct orinoco_private *priv) if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { /* Enable monitor mode */ dev->type = ARPHRD_IEEE80211; - err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_MONITOR, 0, NULL); } else { /* Disable monitor mode */ dev->type = ARPHRD_ETHER; - err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_STOP, 0, NULL); } if (err) @@ -662,8 +696,8 @@ int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc) if ((key < 0) || (key >= 4)) return -EINVAL; - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, - sizeof(tsc_arr), NULL, &tsc_arr); + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, + sizeof(tsc_arr), NULL, &tsc_arr); if (!err) memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0])); @@ -842,7 +876,7 @@ int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) memcpy(key, priv->keys[i].key, priv->keys[i].key_len); - err = hermes_write_ltv(hw, USER_BAP, + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDEFAULTKEY0 + i, HERMES_BYTES_TO_RECLEN(keylen), key); @@ -1049,17 +1083,17 @@ int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, * group address if either we want to multicast, or if we were * multicasting and want to stop */ if (!promisc && (mc_count || priv->mc_count)) { - struct dev_mc_list *p; + struct netdev_hw_addr *ha; struct hermes_multicast mclist; int i = 0; - netdev_for_each_mc_addr(p, dev) { + netdev_for_each_mc_addr(ha, dev) { if (i == mc_count) break; - memcpy(mclist.addr[i++], p->dmi_addr, ETH_ALEN); + memcpy(mclist.addr[i++], ha->addr, ETH_ALEN); } - err = hermes_write_ltv(hw, USER_BAP, + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), &mclist); @@ -1101,15 +1135,15 @@ int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : HERMES_RID_CNFDESIREDSSID; - err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), - NULL, &essidbuf); + err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), + NULL, &essidbuf); if (err) goto fail_unlock; } else { *active = 0; - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, - sizeof(essidbuf), NULL, &essidbuf); + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, + sizeof(essidbuf), NULL, &essidbuf); if (err) goto fail_unlock; } @@ -1180,8 +1214,8 @@ int orinoco_hw_get_bitratelist(struct orinoco_private *priv, if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, - sizeof(list), NULL, &list); + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, + sizeof(list), NULL, &list); orinoco_unlock(priv, &flags); if (err) @@ -1248,7 +1282,7 @@ int orinoco_hw_trigger_scan(struct orinoco_private *priv, idbuf.len = cpu_to_le16(len); memcpy(idbuf.val, ssid->ssid, len); - err = hermes_write_ltv(hw, USER_BAP, + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFSCANSSID_AGERE, HERMES_BYTES_TO_RECLEN(len + 2), &idbuf); @@ -1312,8 +1346,8 @@ int orinoco_hw_get_current_bssid(struct orinoco_private *priv, hermes_t *hw = &priv->hw; int err; - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, - ETH_ALEN, NULL, addr); + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, addr); return err; } diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h index 9799a1d..97af71e 100644 --- a/drivers/net/wireless/orinoco/hw.h +++ b/drivers/net/wireless/orinoco/hw.h @@ -22,7 +22,6 @@ /* Forward declarations */ struct orinoco_private; -struct dev_addr_list; int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name, size_t fw_name_len, u32 *hw_ver); diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index 413e9ab..ca71f08 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -254,7 +254,7 @@ void set_port_type(struct orinoco_private *priv) /* Device methods */ /********************************************************************/ -static int orinoco_open(struct net_device *dev) +int orinoco_open(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; @@ -272,8 +272,9 @@ static int orinoco_open(struct net_device *dev) return err; } +EXPORT_SYMBOL(orinoco_open); -static int orinoco_stop(struct net_device *dev) +int orinoco_stop(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); int err = 0; @@ -281,25 +282,27 @@ static int orinoco_stop(struct net_device *dev) /* We mustn't use orinoco_lock() here, because we need to be able to close the interface even if hw_unavailable is set (e.g. as we're released after a PC Card removal) */ - spin_lock_irq(&priv->lock); + orinoco_lock_irq(priv); priv->open = 0; err = __orinoco_down(priv); - spin_unlock_irq(&priv->lock); + orinoco_unlock_irq(priv); return err; } +EXPORT_SYMBOL(orinoco_stop); -static struct net_device_stats *orinoco_get_stats(struct net_device *dev) +struct net_device_stats *orinoco_get_stats(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); return &priv->stats; } +EXPORT_SYMBOL(orinoco_get_stats); -static void orinoco_set_multicast_list(struct net_device *dev) +void orinoco_set_multicast_list(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; @@ -313,8 +316,9 @@ static void orinoco_set_multicast_list(struct net_device *dev) __orinoco_set_multicast_list(dev); orinoco_unlock(priv, &flags); } +EXPORT_SYMBOL(orinoco_set_multicast_list); -static int orinoco_change_mtu(struct net_device *dev, int new_mtu) +int orinoco_change_mtu(struct net_device *dev, int new_mtu) { struct orinoco_private *priv = ndev_priv(dev); @@ -330,23 +334,115 @@ static int orinoco_change_mtu(struct net_device *dev, int new_mtu) return 0; } +EXPORT_SYMBOL(orinoco_change_mtu); /********************************************************************/ /* Tx path */ /********************************************************************/ +/* Add encapsulation and MIC to the existing SKB. + * The main xmit routine will then send the whole lot to the card. + * Need 8 bytes headroom + * Need 8 bytes tailroom + * + * With encapsulated ethernet II frame + * -------- + * 803.3 header (14 bytes) + * dst[6] + * -------- src[6] + * 803.3 header (14 bytes) len[2] + * dst[6] 803.2 header (8 bytes) + * src[6] encaps[6] + * len[2] <- leave alone -> len[2] + * -------- -------- <-- 0 + * Payload Payload + * ... ... + * + * -------- -------- + * MIC (8 bytes) + * -------- + * + * returns 0 on success, -ENOMEM on error. + */ +int orinoco_process_xmit_skb(struct sk_buff *skb, + struct net_device *dev, + struct orinoco_private *priv, + int *tx_control, + u8 *mic_buf) +{ + struct orinoco_tkip_key *key; + struct ethhdr *eh; + int do_mic; + + key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key; + + do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) && + (key != NULL)); + + if (do_mic) + *tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | + HERMES_TXCTRL_MIC; + + eh = (struct ethhdr *)skb->data; + + /* Encapsulate Ethernet-II frames */ + if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ + struct header_struct { + struct ethhdr eth; /* 802.3 header */ + u8 encap[6]; /* 802.2 header */ + } __attribute__ ((packed)) hdr; + int len = skb->len + sizeof(encaps_hdr) - (2 * ETH_ALEN); + + if (skb_headroom(skb) < ENCAPS_OVERHEAD) { + if (net_ratelimit()) + printk(KERN_ERR + "%s: Not enough headroom for 802.2 headers %d\n", + dev->name, skb_headroom(skb)); + return -ENOMEM; + } + + /* Fill in new header */ + memcpy(&hdr.eth, eh, 2 * ETH_ALEN); + hdr.eth.h_proto = htons(len); + memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); + + /* Make room for the new header, and copy it in */ + eh = (struct ethhdr *) skb_push(skb, ENCAPS_OVERHEAD); + memcpy(eh, &hdr, sizeof(hdr)); + } + + /* Calculate Michael MIC */ + if (do_mic) { + size_t len = skb->len - ETH_HLEN; + u8 *mic = &mic_buf[0]; + + /* Have to write to an even address, so copy the spare + * byte across */ + if (skb->len % 2) { + *mic = skb->data[skb->len - 1]; + mic++; + } + + orinoco_mic(priv->tx_tfm_mic, key->tx_mic, + eh->h_dest, eh->h_source, 0 /* priority */, + skb->data + ETH_HLEN, + len, mic); + } + + return 0; +} +EXPORT_SYMBOL(orinoco_process_xmit_skb); + static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; - struct orinoco_tkip_key *key; hermes_t *hw = &priv->hw; int err = 0; u16 txfid = priv->txfid; - struct ethhdr *eh; int tx_control; unsigned long flags; - int do_mic; + u8 mic_buf[MICHAEL_MIC_LEN+1]; if (!netif_running(dev)) { printk(KERN_ERR "%s: Tx on stopped device!\n", @@ -378,16 +474,12 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->len < ETH_HLEN) goto drop; - key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key; - - do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) && - (key != NULL)); - tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; - if (do_mic) - tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | - HERMES_TXCTRL_MIC; + err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, + &mic_buf[0]); + if (err) + goto drop; if (priv->has_alt_txcntl) { /* WPA enabled firmwares have tx_cntl at the end of @@ -400,8 +492,8 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) memset(&desc, 0, sizeof(desc)); *txcntl = cpu_to_le16(tx_control); - err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), - txfid, 0); + err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), + txfid, 0); if (err) { if (net_ratelimit()) printk(KERN_ERR "%s: Error %d writing Tx " @@ -414,8 +506,8 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) memset(&desc, 0, sizeof(desc)); desc.tx_control = cpu_to_le16(tx_control); - err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), - txfid, 0); + err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), + txfid, 0); if (err) { if (net_ratelimit()) printk(KERN_ERR "%s: Error %d writing Tx " @@ -430,68 +522,24 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); } - eh = (struct ethhdr *)skb->data; - - /* Encapsulate Ethernet-II frames */ - if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ - struct header_struct { - struct ethhdr eth; /* 802.3 header */ - u8 encap[6]; /* 802.2 header */ - } __attribute__ ((packed)) hdr; - - /* Strip destination and source from the data */ - skb_pull(skb, 2 * ETH_ALEN); - - /* And move them to a separate header */ - memcpy(&hdr.eth, eh, 2 * ETH_ALEN); - hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len); - memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); - - /* Insert the SNAP header */ - if (skb_headroom(skb) < sizeof(hdr)) { - printk(KERN_ERR - "%s: Not enough headroom for 802.2 headers %d\n", - dev->name, skb_headroom(skb)); - goto drop; - } - eh = (struct ethhdr *) skb_push(skb, sizeof(hdr)); - memcpy(eh, &hdr, sizeof(hdr)); - } - - err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len, - txfid, HERMES_802_3_OFFSET); + err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len, + txfid, HERMES_802_3_OFFSET); if (err) { printk(KERN_ERR "%s: Error %d writing packet to BAP\n", dev->name, err); goto busy; } - /* Calculate Michael MIC */ - if (do_mic) { - u8 mic_buf[MICHAEL_MIC_LEN + 1]; - u8 *mic; - size_t offset; - size_t len; + if (tx_control & HERMES_TXCTRL_MIC) { + size_t offset = HERMES_802_3_OFFSET + skb->len; + size_t len = MICHAEL_MIC_LEN; - if (skb->len % 2) { - /* MIC start is on an odd boundary */ - mic_buf[0] = skb->data[skb->len - 1]; - mic = &mic_buf[1]; - offset = skb->len - 1; - len = MICHAEL_MIC_LEN + 1; - } else { - mic = &mic_buf[0]; - offset = skb->len; - len = MICHAEL_MIC_LEN; + if (offset % 2) { + offset--; + len++; } - - orinoco_mic(priv->tx_tfm_mic, key->tx_mic, - eh->h_dest, eh->h_source, 0 /* priority */, - skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic); - - /* Write the MIC */ - err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len, - txfid, HERMES_802_3_OFFSET + offset); + err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len, + txfid, offset); if (err) { printk(KERN_ERR "%s: Error %d writing MIC to BAP\n", dev->name, err); @@ -502,7 +550,7 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) /* Finally, we actually initiate the send */ netif_stop_queue(dev); - err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, + err = hw->ops->cmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, txfid, NULL); if (err) { netif_start_queue(dev); @@ -512,7 +560,6 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) goto busy; } - dev->trans_start = jiffies; stats->tx_bytes += HERMES_802_3_OFFSET + skb->len; goto ok; @@ -572,9 +619,9 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) return; /* Nothing's really happened */ /* Read part of the frame header - we need status and addr1 */ - err = hermes_bap_pread(hw, IRQ_BAP, &hdr, - sizeof(struct hermes_txexc_data), - fid, 0); + err = hw->ops->bap_pread(hw, IRQ_BAP, &hdr, + sizeof(struct hermes_txexc_data), + fid, 0); hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); stats->tx_errors++; @@ -615,7 +662,7 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) netif_wake_queue(dev); } -static void orinoco_tx_timeout(struct net_device *dev) +void orinoco_tx_timeout(struct net_device *dev) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; @@ -630,6 +677,7 @@ static void orinoco_tx_timeout(struct net_device *dev) schedule_work(&priv->reset_work); } +EXPORT_SYMBOL(orinoco_tx_timeout); /********************************************************************/ /* Rx path (data frames) */ @@ -764,9 +812,9 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, /* If any, copy the data from the card to the skb */ if (datalen > 0) { - err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen), - ALIGN(datalen, 2), rxfid, - HERMES_802_2_OFFSET); + err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, datalen), + ALIGN(datalen, 2), rxfid, + HERMES_802_2_OFFSET); if (err) { printk(KERN_ERR "%s: error %d reading monitor frame\n", dev->name, err); @@ -792,7 +840,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, stats->rx_dropped++; } -static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) +void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; @@ -814,8 +862,8 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) rxfid = hermes_read_regn(hw, RXFID); - err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc), - rxfid, 0); + err = hw->ops->bap_pread(hw, IRQ_BAP, desc, sizeof(*desc), + rxfid, 0); if (err) { printk(KERN_ERR "%s: error %d reading Rx descriptor. " "Frame dropped.\n", dev->name, err); @@ -882,9 +930,9 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) nothing is removed. 2 is for aligning the IP header. */ skb_reserve(skb, ETH_HLEN + 2); - err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length), - ALIGN(length, 2), rxfid, - HERMES_802_2_OFFSET); + err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, length), + ALIGN(length, 2), rxfid, + HERMES_802_2_OFFSET); if (err) { printk(KERN_ERR "%s: error %d reading frame. " "Frame dropped.\n", dev->name, err); @@ -913,6 +961,7 @@ update_stats: out: kfree(desc); } +EXPORT_SYMBOL(__orinoco_ev_rx); static void orinoco_rx(struct net_device *dev, struct hermes_rx_descriptor *desc, @@ -1145,9 +1194,9 @@ static void orinoco_join_ap(struct work_struct *work) goto out; /* Read scan results from the firmware */ - err = hermes_read_ltv(hw, USER_BAP, - HERMES_RID_SCANRESULTSTABLE, - MAX_SCAN_LEN, &len, buf); + err = hw->ops->read_ltv(hw, USER_BAP, + HERMES_RID_SCANRESULTSTABLE, + MAX_SCAN_LEN, &len, buf); if (err) { printk(KERN_ERR "%s: Cannot read scan results\n", dev->name); @@ -1194,8 +1243,8 @@ static void orinoco_send_bssid_wevent(struct orinoco_private *priv) union iwreq_data wrqu; int err; - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, - ETH_ALEN, NULL, wrqu.ap_addr.sa_data); + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, wrqu.ap_addr.sa_data); if (err != 0) return; @@ -1217,8 +1266,8 @@ static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv) if (!priv->has_wpa) return; - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO, - sizeof(buf), NULL, &buf); + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO, + sizeof(buf), NULL, &buf); if (err != 0) return; @@ -1247,8 +1296,9 @@ static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv) if (!priv->has_wpa) return; - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO, - sizeof(buf), NULL, &buf); + err = hw->ops->read_ltv(hw, USER_BAP, + HERMES_RID_CURRENT_ASSOC_RESP_INFO, + sizeof(buf), NULL, &buf); if (err != 0) return; @@ -1353,7 +1403,7 @@ static void orinoco_process_scan_results(struct work_struct *work) spin_unlock_irqrestore(&priv->scan_lock, flags); } -static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) +void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = ndev_priv(dev); u16 infofid; @@ -1371,8 +1421,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) infofid = hermes_read_regn(hw, INFOFID); /* Read the info frame header - don't try too hard */ - err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info), - infofid, 0); + err = hw->ops->bap_pread(hw, IRQ_BAP, &info, sizeof(info), + infofid, 0); if (err) { printk(KERN_ERR "%s: error %d reading info frame. " "Frame dropped.\n", dev->name, err); @@ -1393,8 +1443,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) len = sizeof(tallies); } - err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len, - infofid, sizeof(info)); + err = hw->ops->bap_pread(hw, IRQ_BAP, &tallies, len, + infofid, sizeof(info)); if (err) break; @@ -1429,8 +1479,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) break; } - err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len, - infofid, sizeof(info)); + err = hw->ops->bap_pread(hw, IRQ_BAP, &linkstatus, len, + infofid, sizeof(info)); if (err) break; newstatus = le16_to_cpu(linkstatus.linkstatus); @@ -1494,8 +1544,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) } /* Read scan data */ - err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len, - infofid, sizeof(info)); + err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) buf, len, + infofid, sizeof(info)); if (err) { kfree(buf); qabort_scan(priv); @@ -1547,8 +1597,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) break; /* Read scan data */ - err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len, - infofid, sizeof(info)); + err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) bss, len, + infofid, sizeof(info)); if (err) kfree(bss); else @@ -1568,9 +1618,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) /* We don't actually do anything about it */ break; } - - return; } +EXPORT_SYMBOL(__orinoco_ev_info); static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) { @@ -1647,7 +1696,7 @@ static int orinoco_reinit_firmware(struct orinoco_private *priv) struct hermes *hw = &priv->hw; int err; - err = hermes_init(hw); + err = hw->ops->init(hw); if (priv->do_fw_download && !err) { err = orinoco_download(priv); if (err) @@ -1735,7 +1784,7 @@ void orinoco_reset(struct work_struct *work) } /* This has to be called from user context */ - spin_lock_irq(&priv->lock); + orinoco_lock_irq(priv); priv->hw_unavailable--; @@ -1750,7 +1799,7 @@ void orinoco_reset(struct work_struct *work) dev->trans_start = jiffies; } - spin_unlock_irq(&priv->lock); + orinoco_unlock_irq(priv); return; disable: @@ -1984,7 +2033,7 @@ int orinoco_init(struct orinoco_private *priv) priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN; /* Initialize the firmware */ - err = hermes_init(hw); + err = hw->ops->init(hw); if (err != 0) { dev_err(dev, "Failed to initialize firmware (err = %d)\n", err); @@ -2067,9 +2116,9 @@ int orinoco_init(struct orinoco_private *priv) /* Make the hardware available, as long as it hasn't been * removed elsewhere (e.g. by PCMCIA hot unplug) */ - spin_lock_irq(&priv->lock); + orinoco_lock_irq(priv); priv->hw_unavailable--; - spin_unlock_irq(&priv->lock); + orinoco_unlock_irq(priv); dev_dbg(dev, "Ready\n"); @@ -2192,7 +2241,8 @@ EXPORT_SYMBOL(alloc_orinocodev); */ int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr, - unsigned int irq) + unsigned int irq, + const struct net_device_ops *ops) { struct wiphy *wiphy = priv_to_wiphy(priv); struct wireless_dev *wdev; @@ -2211,16 +2261,21 @@ int orinoco_if_add(struct orinoco_private *priv, /* Setup / override net_device fields */ dev->ieee80211_ptr = wdev; - dev->netdev_ops = &orinoco_netdev_ops; dev->watchdog_timeo = HZ; /* 1 second timeout */ dev->wireless_handlers = &orinoco_handler_def; #ifdef WIRELESS_SPY dev->wireless_data = &priv->wireless_data; #endif + /* Default to standard ops if not set */ + if (ops) + dev->netdev_ops = ops; + else + dev->netdev_ops = &orinoco_netdev_ops; + /* we use the default eth_mac_addr for setting the MAC addr */ /* Reserve space in skb for the SNAP header */ - dev->hard_header_len += ENCAPS_OVERHEAD; + dev->needed_headroom = ENCAPS_OVERHEAD; netif_carrier_off(dev); @@ -2305,7 +2360,7 @@ int orinoco_up(struct orinoco_private *priv) unsigned long flags; int err; - spin_lock_irqsave(&priv->lock, flags); + priv->hw.ops->lock_irqsave(&priv->lock, &flags); err = orinoco_reinit_firmware(priv); if (err) { @@ -2325,7 +2380,7 @@ int orinoco_up(struct orinoco_private *priv) } exit: - spin_unlock_irqrestore(&priv->lock, flags); + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); return 0; } @@ -2337,7 +2392,7 @@ void orinoco_down(struct orinoco_private *priv) unsigned long flags; int err; - spin_lock_irqsave(&priv->lock, flags); + priv->hw.ops->lock_irqsave(&priv->lock, &flags); err = __orinoco_down(priv); if (err) printk(KERN_WARNING "%s: Error %d downing interface\n", @@ -2345,7 +2400,7 @@ void orinoco_down(struct orinoco_private *priv) netif_device_detach(dev); priv->hw_unavailable++; - spin_unlock_irqrestore(&priv->lock, flags); + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); } EXPORT_SYMBOL(orinoco_down); diff --git a/drivers/net/wireless/orinoco/main.h b/drivers/net/wireless/orinoco/main.h index 21ab36c..4dadf98 100644 --- a/drivers/net/wireless/orinoco/main.h +++ b/drivers/net/wireless/orinoco/main.h @@ -33,18 +33,6 @@ int orinoco_commit(struct orinoco_private *priv); void orinoco_reset(struct work_struct *work); /* Information element helpers - find a home for these... */ -static inline u8 *orinoco_get_ie(u8 *data, size_t len, - enum ieee80211_eid eid) -{ - u8 *p = data; - while ((p + 2) < (data + len)) { - if (p[0] == eid) - return p; - p += p[1] + 2; - } - return NULL; -} - #define WPA_OUI_TYPE "\x00\x50\xF2\x01" #define WPA_SELECTOR_LEN 4 static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h index 665ef56..a6da86e 100644 --- a/drivers/net/wireless/orinoco/orinoco.h +++ b/drivers/net/wireless/orinoco/orinoco.h @@ -131,6 +131,8 @@ struct orinoco_private { u16 ap_density, rts_thresh; u16 pm_on, pm_mcast, pm_period, pm_timeout; u16 preamble; + u16 short_retry_limit, long_retry_limit; + u16 retry_lifetime; #ifdef WIRELESS_SPY struct iw_spy_data spy_data; /* iwspy support */ struct iw_public_data wireless_data; @@ -188,12 +190,30 @@ extern void free_orinocodev(struct orinoco_private *priv); extern int orinoco_init(struct orinoco_private *priv); extern int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr, - unsigned int irq); + unsigned int irq, + const struct net_device_ops *ops); extern void orinoco_if_del(struct orinoco_private *priv); extern int orinoco_up(struct orinoco_private *priv); extern void orinoco_down(struct orinoco_private *priv); extern irqreturn_t orinoco_interrupt(int irq, void *dev_id); +extern void __orinoco_ev_info(struct net_device *dev, hermes_t *hw); +extern void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw); + +int orinoco_process_xmit_skb(struct sk_buff *skb, + struct net_device *dev, + struct orinoco_private *priv, + int *tx_control, + u8 *mic); + +/* Common ndo functions exported for reuse by orinoco_usb */ +int orinoco_open(struct net_device *dev); +int orinoco_stop(struct net_device *dev); +struct net_device_stats *orinoco_get_stats(struct net_device *dev); +void orinoco_set_multicast_list(struct net_device *dev); +int orinoco_change_mtu(struct net_device *dev, int new_mtu); +void orinoco_tx_timeout(struct net_device *dev); + /********************************************************************/ /* Locking and synchronization functions */ /********************************************************************/ @@ -201,11 +221,11 @@ extern irqreturn_t orinoco_interrupt(int irq, void *dev_id); static inline int orinoco_lock(struct orinoco_private *priv, unsigned long *flags) { - spin_lock_irqsave(&priv->lock, *flags); + priv->hw.ops->lock_irqsave(&priv->lock, flags); if (priv->hw_unavailable) { DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n", priv->ndev); - spin_unlock_irqrestore(&priv->lock, *flags); + priv->hw.ops->unlock_irqrestore(&priv->lock, flags); return -EBUSY; } return 0; @@ -214,7 +234,17 @@ static inline int orinoco_lock(struct orinoco_private *priv, static inline void orinoco_unlock(struct orinoco_private *priv, unsigned long *flags) { - spin_unlock_irqrestore(&priv->lock, *flags); + priv->hw.ops->unlock_irqrestore(&priv->lock, flags); +} + +static inline void orinoco_lock_irq(struct orinoco_private *priv) +{ + priv->hw.ops->lock_irq(&priv->lock); +} + +static inline void orinoco_unlock_irq(struct orinoco_private *priv) +{ + priv->hw.ops->unlock_irq(&priv->lock); } /*** Navigate from net_device to orinoco_private ***/ diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index 03056ab..b16d5db 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -281,7 +281,7 @@ orinoco_cs_config(struct pcmcia_device *link) /* Register an interface with the stack */ if (orinoco_if_add(priv, link->io.BasePort1, - link->irq) != 0) { + link->irq, NULL) != 0) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } @@ -306,9 +306,9 @@ orinoco_cs_release(struct pcmcia_device *link) /* We're committed to taking the device away now, so mark the * hardware as unavailable */ - spin_lock_irqsave(&priv->lock, flags); + priv->hw.ops->lock_irqsave(&priv->lock, &flags); priv->hw_unavailable++; - spin_unlock_irqrestore(&priv->lock, flags); + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); pcmcia_disable_device(link); if (priv->hw.iobase) @@ -353,87 +353,90 @@ static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION "Pavel Roskin <proski@gnu.org>, et al)"; static struct pcmcia_device_id orinoco_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */ - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), /* Sohoware NCP110, Philips 802.11b */ - PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), /* AnyPoint(TM) Wireless II PC Card */ PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */ - PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), /* PROXIM RangeLAN-DS/LAN PC CARD */ - PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), /* Compaq WL100 11 Mbps Wireless Adapter */ PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), /* Lucent Orinoco and old Intersil */ PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */ PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), /* Nortel Networks eMobility 802.11 Wireless Adapter */ - PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), /* Intermec MobileLAN 11Mbps 802.11b WLAN Card */ - PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), /* Samsung SWL2000-N 11Mb/s WLAN Card */ PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), /* AirWay 802.11 Adapter (PCMCIA) */ PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0001), /* ARtem Onair */ PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0003), /* ARtem Onair Comcard 11 */ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0305), /* Buffalo WLI-PCM-S11 */ - PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), /* Linksys WPC11 Version 2.5 */ - PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), /* Linksys WPC11 Version 3 */ - PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), /* Compaq HNW-100 11 Mbps Wireless Adapter */ - PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), /* Linksys WCF12 Wireless CompactFlash Card */ PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), /* ASUS SpaceLink WL-100 */ PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x0002), /* SpeedStream SS1021 Wireless Adapter */ PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x3021), /* SpeedStream Wireless Adapter */ PCMCIA_DEVICE_MANF_CARD(0x14ea, 0xb001), /* PLANEX RoadLannerWave GW-NS11H */ + PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3), + PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f), + PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e), + PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842), + PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169), + PCMCIA_DEVICE_PROD_ID12("BENQ", "AWL100 PCMCIA ADAPTER", 0x35dadc74, 0x01f7fedb), + PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90), + PCMCIA_DEVICE_PROD_ID12("D-Link Corporation", "D-Link DWL-650H 11Mbps WLAN Adapter", 0xef544d24, 0xcd8ea916), + PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3), + PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c), + PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless 2011 LAN PC Card", 0x816cc815, 0x07f58077), + PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92), + PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a), + PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410), + PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3), + PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a), + PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767), + PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed), + PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9), + PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26), + PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b), + PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e), +#ifdef CONFIG_HERMES_PRISM + /* Only entries that certainly identify Prism chipset */ + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */ + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), /* Sohoware NCP110, Philips 802.11b */ + PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), /* AnyPoint(TM) Wireless II PC Card */ + PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), /* PROXIM RangeLAN-DS/LAN PC CARD */ + PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), /* Compaq WL100 11 Mbps Wireless Adapter */ + PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), /* Intermec MobileLAN 11Mbps 802.11b WLAN Card */ + PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), /* Samsung SWL2000-N 11Mb/s WLAN Card */ + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), /* Linksys WPC11 Version 2.5 */ + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), /* Linksys WPC11 Version 3 */ + PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), /* Compaq HNW-100 11 Mbps Wireless Adapter */ + PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), /* Linksys WCF12 Wireless CompactFlash Card */ PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), /* Airvast WN-100 */ PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), /* Adaptec Ultra Wireless ANW-8030 */ PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0008), /* CONTEC FLEXSCAN/FX-DDS110-PCC */ PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), /* Conceptronic CON11Cpro, EMTAC A2424i */ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), /* Safeway 802.11b, ZCOMAX AirRunner/XI-300 */ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), /* D-Link DCF660, Sandisk Connect SDWCFB-000 */ - PCMCIA_DEVICE_PROD_ID12(" ", "IEEE 802.11 Wireless LAN/PC Card", 0x3b6e20c8, 0xefccafe9), - PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3), + PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0), PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5), PCMCIA_DEVICE_PROD_ID12("Addtron", "AWP-100 Wireless PCMCIA", 0xe6ec52ce, 0x08649af2), - PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f), - PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842), - PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e), - PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169), - PCMCIA_DEVICE_PROD_ID12("BENQ", "AWL100 PCMCIA ADAPTER", 0x35dadc74, 0x01f7fedb), - PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3), PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18), - PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90), + PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3), PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", 0x54f7c49c, 0x15a75e5b), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9), PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae), + PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146), PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac), PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab), - PCMCIA_DEVICE_PROD_ID12("D-Link Corporation", "D-Link DWL-650H 11Mbps WLAN Adapter", 0xef544d24, 0xcd8ea916), - PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146), - PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3), - PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c), - PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0), - PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless 2011 LAN PC Card", 0x816cc815, 0x07f58077), + PCMCIA_DEVICE_PROD_ID12(" ", "IEEE 802.11 Wireless LAN/PC Card", 0x3b6e20c8, 0xefccafe9), PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18), PCMCIA_DEVICE_PROD_ID12("INTERSIL", "I-GATE 11M PC Card / PC Card plus", 0x74c5e40d, 0x8304ff77), PCMCIA_DEVICE_PROD_ID12("Intersil", "PRISM 2_5 PCMCIA ADAPTER", 0x4b801a17, 0x6345a0bf), - PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92), PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", 0x0733cc81, 0x0c52f395), - PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a), - PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410), - PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3), PCMCIA_DEVICE_PROD_ID12("Microsoft", "Wireless Notebook Adapter MN-520", 0x5961bf85, 0x6eec8c01), - PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a), - PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", 0xa37434e9, 0x9762e8f1), PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401RA Wireless PC", "Card", 0x0306467f, 0x9762e8f1), - PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767), + PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", 0xa37434e9, 0x9762e8f1), PCMCIA_DEVICE_PROD_ID12("OEM", "PRISM2 IEEE 802.11 PC-Card", 0xfea54c90, 0x48f2bdd6), - PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed), PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264), PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178), - PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9), - PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26), - PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b), PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2532W-B EliteConnect Wireless Adapter", 0xc4f8b18b, 0x196bd757), PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2632W", 0xc4f8b18b, 0x474a1f2a), - PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e), PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee), PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), +#endif PCMCIA_DEVICE_NULL, }; MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids); diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c index 075f446..bc3ea0b 100644 --- a/drivers/net/wireless/orinoco/orinoco_nortel.c +++ b/drivers/net/wireless/orinoco/orinoco_nortel.c @@ -220,7 +220,7 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev, goto fail; } - err = orinoco_if_add(priv, 0, 0); + err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail; diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c index bda5317..468197f 100644 --- a/drivers/net/wireless/orinoco/orinoco_pci.c +++ b/drivers/net/wireless/orinoco/orinoco_pci.c @@ -170,7 +170,7 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, goto fail; } - err = orinoco_if_add(priv, 0, 0); + err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail; diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c index e0d5874..9358f4d 100644 --- a/drivers/net/wireless/orinoco/orinoco_plx.c +++ b/drivers/net/wireless/orinoco/orinoco_plx.c @@ -259,7 +259,7 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, goto fail; } - err = orinoco_if_add(priv, 0, 0); + err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail; diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c index 88cbc79..784605f 100644 --- a/drivers/net/wireless/orinoco/orinoco_tmd.c +++ b/drivers/net/wireless/orinoco/orinoco_tmd.c @@ -156,7 +156,7 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev, goto fail; } - err = orinoco_if_add(priv, 0, 0); + err = orinoco_if_add(priv, 0, 0, NULL); if (err) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto fail; diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c new file mode 100644 index 0000000..78f089b --- /dev/null +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -0,0 +1,1795 @@ +/* + * USB Orinoco driver + * + * Copyright (c) 2003 Manuel Estrada Sainz + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + * + * Queueing code based on linux-wlan-ng 0.2.1-pre5 + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * + * The license is the same as above. + * + * Initialy based on USB Skeleton driver - 0.7 + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * + * 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. + * + * NOTE: The original USB Skeleton driver is GPL, but all that code is + * gone so MPL/GPL applies. + */ + +#define DRIVER_NAME "orinoco_usb" +#define PFX DRIVER_NAME ": " + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/fcntl.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/smp_lock.h> +#include <linux/usb.h> +#include <linux/timer.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/etherdevice.h> +#include <linux/wireless.h> +#include <linux/firmware.h> + +#include "mic.h" +#include "orinoco.h" + +#ifndef URB_ASYNC_UNLINK +#define URB_ASYNC_UNLINK 0 +#endif + +/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ +static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; +#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) + +struct header_struct { + /* 802.3 */ + u8 dest[ETH_ALEN]; + u8 src[ETH_ALEN]; + __be16 len; + /* 802.2 */ + u8 dsap; + u8 ssap; + u8 ctrl; + /* SNAP */ + u8 oui[3]; + __be16 ethertype; +} __attribute__ ((packed)); + +struct ez_usb_fw { + u16 size; + const u8 *code; +}; + +static struct ez_usb_fw firmware = { + .size = 0, + .code = NULL, +}; + +#ifdef CONFIG_USB_DEBUG +static int debug = 1; +#else +static int debug; +#endif + +/* Debugging macros */ +#undef dbg +#define dbg(format, arg...) \ + do { if (debug) printk(KERN_DEBUG PFX "%s: " format "\n", \ + __func__ , ## arg); } while (0) +#undef err +#define err(format, arg...) \ + do { printk(KERN_ERR PFX format "\n", ## arg); } while (0) + +/* Module paramaters */ +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + +MODULE_FIRMWARE("orinoco_ezusb_fw"); + +/* + * Under some conditions, the card gets stuck and stops paying attention + * to the world (i.e. data communication stalls) until we do something to + * it. Sending an INQ_TALLIES command seems to be enough and should be + * harmless otherwise. This behaviour has been observed when using the + * driver on a systemimager client during installation. In the past a + * timer was used to send INQ_TALLIES commands when there was no other + * activity, but it was troublesome and was removed. + */ + +#define USB_COMPAQ_VENDOR_ID 0x049f /* Compaq Computer Corp. */ +#define USB_COMPAQ_WL215_ID 0x001f /* Compaq WL215 USB Adapter */ +#define USB_COMPAQ_W200_ID 0x0076 /* Compaq W200 USB Adapter */ +#define USB_HP_WL215_ID 0x0082 /* Compaq WL215 USB Adapter */ + +#define USB_MELCO_VENDOR_ID 0x0411 +#define USB_BUFFALO_L11_ID 0x0006 /* BUFFALO WLI-USB-L11 */ +#define USB_BUFFALO_L11G_WR_ID 0x000B /* BUFFALO WLI-USB-L11G-WR */ +#define USB_BUFFALO_L11G_ID 0x000D /* BUFFALO WLI-USB-L11G */ + +#define USB_LUCENT_VENDOR_ID 0x047E /* Lucent Technologies */ +#define USB_LUCENT_ORINOCO_ID 0x0300 /* Lucent/Agere Orinoco USB Client */ + +#define USB_AVAYA8_VENDOR_ID 0x0D98 +#define USB_AVAYAE_VENDOR_ID 0x0D9E +#define USB_AVAYA_WIRELESS_ID 0x0300 /* Avaya Wireless USB Card */ + +#define USB_AGERE_VENDOR_ID 0x0D4E /* Agere Systems */ +#define USB_AGERE_MODEL0801_ID 0x1000 /* Wireless USB Card Model 0801 */ +#define USB_AGERE_MODEL0802_ID 0x1001 /* Wireless USB Card Model 0802 */ +#define USB_AGERE_REBRANDED_ID 0x047A /* WLAN USB Card */ + +#define USB_ELSA_VENDOR_ID 0x05CC +#define USB_ELSA_AIRLANCER_ID 0x3100 /* ELSA AirLancer USB-11 */ + +#define USB_LEGEND_VENDOR_ID 0x0E7C +#define USB_LEGEND_JOYNET_ID 0x0300 /* Joynet WLAN USB Card */ + +#define USB_SAMSUNG_VENDOR_ID 0x04E8 +#define USB_SAMSUNG_SEW2001U1_ID 0x5002 /* Samsung SEW-2001u Card */ +#define USB_SAMSUNG_SEW2001U2_ID 0x5B11 /* Samsung SEW-2001u Card */ +#define USB_SAMSUNG_SEW2003U_ID 0x7011 /* Samsung SEW-2003U Card */ + +#define USB_IGATE_VENDOR_ID 0x0681 +#define USB_IGATE_IGATE_11M_ID 0x0012 /* I-GATE 11M USB Card */ + +#define USB_FUJITSU_VENDOR_ID 0x0BF8 +#define USB_FUJITSU_E1100_ID 0x1002 /* connect2AIR WLAN E-1100 USB */ + +#define USB_2WIRE_VENDOR_ID 0x1630 +#define USB_2WIRE_WIRELESS_ID 0xff81 /* 2Wire Wireless USB adapter */ + + +#define EZUSB_REQUEST_FW_TRANS 0xA0 +#define EZUSB_REQUEST_TRIGER 0xAA +#define EZUSB_REQUEST_TRIG_AC 0xAC +#define EZUSB_CPUCS_REG 0x7F92 + +#define EZUSB_RID_TX 0x0700 +#define EZUSB_RID_RX 0x0701 +#define EZUSB_RID_INIT1 0x0702 +#define EZUSB_RID_ACK 0x0710 +#define EZUSB_RID_READ_PDA 0x0800 +#define EZUSB_RID_PROG_INIT 0x0852 +#define EZUSB_RID_PROG_SET_ADDR 0x0853 +#define EZUSB_RID_PROG_BYTES 0x0854 +#define EZUSB_RID_PROG_END 0x0855 +#define EZUSB_RID_DOCMD 0x0860 + +/* Recognize info frames */ +#define EZUSB_IS_INFO(id) ((id >= 0xF000) && (id <= 0xF2FF)) + +#define EZUSB_MAGIC 0x0210 + +#define EZUSB_FRAME_DATA 1 +#define EZUSB_FRAME_CONTROL 2 + +#define DEF_TIMEOUT (3*HZ) + +#define BULK_BUF_SIZE 2048 + +#define MAX_DL_SIZE (BULK_BUF_SIZE - sizeof(struct ezusb_packet)) + +#define FW_BUF_SIZE 64 +#define FW_VAR_OFFSET_PTR 0x359 +#define FW_VAR_VALUE 0 +#define FW_HOLE_START 0x100 +#define FW_HOLE_END 0x300 + +struct ezusb_packet { + __le16 magic; /* 0x0210 */ + u8 req_reply_count; + u8 ans_reply_count; + __le16 frame_type; /* 0x01 for data frames, 0x02 otherwise */ + __le16 size; /* transport size */ + __le16 crc; /* CRC up to here */ + __le16 hermes_len; + __le16 hermes_rid; + u8 data[0]; +} __attribute__ ((packed)); + +/* Table of devices that work or may work with this driver */ +static struct usb_device_id ezusb_table[] = { + {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_WL215_ID)}, + {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_HP_WL215_ID)}, + {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_W200_ID)}, + {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11_ID)}, + {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_WR_ID)}, + {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_ID)}, + {USB_DEVICE(USB_LUCENT_VENDOR_ID, USB_LUCENT_ORINOCO_ID)}, + {USB_DEVICE(USB_AVAYA8_VENDOR_ID, USB_AVAYA_WIRELESS_ID)}, + {USB_DEVICE(USB_AVAYAE_VENDOR_ID, USB_AVAYA_WIRELESS_ID)}, + {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0801_ID)}, + {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0802_ID)}, + {USB_DEVICE(USB_ELSA_VENDOR_ID, USB_ELSA_AIRLANCER_ID)}, + {USB_DEVICE(USB_LEGEND_VENDOR_ID, USB_LEGEND_JOYNET_ID)}, + {USB_DEVICE_VER(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U1_ID, + 0, 0)}, + {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U2_ID)}, + {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2003U_ID)}, + {USB_DEVICE(USB_IGATE_VENDOR_ID, USB_IGATE_IGATE_11M_ID)}, + {USB_DEVICE(USB_FUJITSU_VENDOR_ID, USB_FUJITSU_E1100_ID)}, + {USB_DEVICE(USB_2WIRE_VENDOR_ID, USB_2WIRE_WIRELESS_ID)}, + {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_REBRANDED_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ezusb_table); + +/* Structure to hold all of our device specific stuff */ +struct ezusb_priv { + struct usb_device *udev; + struct net_device *dev; + struct mutex mtx; + spinlock_t req_lock; + struct list_head req_pending; + struct list_head req_active; + spinlock_t reply_count_lock; + u16 hermes_reg_fake[0x40]; + u8 *bap_buf; + struct urb *read_urb; + int read_pipe; + int write_pipe; + u8 reply_count; +}; + +enum ezusb_state { + EZUSB_CTX_START, + EZUSB_CTX_QUEUED, + EZUSB_CTX_REQ_SUBMITTED, + EZUSB_CTX_REQ_COMPLETE, + EZUSB_CTX_RESP_RECEIVED, + EZUSB_CTX_REQ_TIMEOUT, + EZUSB_CTX_REQ_FAILED, + EZUSB_CTX_RESP_TIMEOUT, + EZUSB_CTX_REQSUBMIT_FAIL, + EZUSB_CTX_COMPLETE, +}; + +struct request_context { + struct list_head list; + atomic_t refcount; + struct completion done; /* Signals that CTX is dead */ + int killed; + struct urb *outurb; /* OUT for req pkt */ + struct ezusb_priv *upriv; + struct ezusb_packet *buf; + int buf_length; + struct timer_list timer; /* Timeout handling */ + enum ezusb_state state; /* Current state */ + /* the RID that we will wait for */ + u16 out_rid; + u16 in_rid; +}; + + +/* Forward declarations */ +static void ezusb_ctx_complete(struct request_context *ctx); +static void ezusb_req_queue_run(struct ezusb_priv *upriv); +static void ezusb_bulk_in_callback(struct urb *urb); + +static inline u8 ezusb_reply_inc(u8 count) +{ + if (count < 0x7F) + return count + 1; + else + return 1; +} + +static void ezusb_request_context_put(struct request_context *ctx) +{ + if (!atomic_dec_and_test(&ctx->refcount)) + return; + + WARN_ON(!ctx->done.done); + BUG_ON(ctx->outurb->status == -EINPROGRESS); + BUG_ON(timer_pending(&ctx->timer)); + usb_free_urb(ctx->outurb); + kfree(ctx->buf); + kfree(ctx); +} + +static inline void ezusb_mod_timer(struct ezusb_priv *upriv, + struct timer_list *timer, + unsigned long expire) +{ + if (!upriv->udev) + return; + mod_timer(timer, expire); +} + +static void ezusb_request_timerfn(u_long _ctx) +{ + struct request_context *ctx = (void *) _ctx; + + ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; + if (usb_unlink_urb(ctx->outurb) == -EINPROGRESS) { + ctx->state = EZUSB_CTX_REQ_TIMEOUT; + } else { + ctx->state = EZUSB_CTX_RESP_TIMEOUT; + dbg("couldn't unlink"); + atomic_inc(&ctx->refcount); + ctx->killed = 1; + ezusb_ctx_complete(ctx); + ezusb_request_context_put(ctx); + } +}; + +static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv, + u16 out_rid, u16 in_rid) +{ + struct request_context *ctx; + + ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return NULL; + + memset(ctx, 0, sizeof(*ctx)); + + ctx->buf = kmalloc(BULK_BUF_SIZE, GFP_ATOMIC); + if (!ctx->buf) { + kfree(ctx); + return NULL; + } + ctx->outurb = usb_alloc_urb(0, GFP_ATOMIC); + if (!ctx->outurb) { + kfree(ctx->buf); + kfree(ctx); + return NULL; + } + + ctx->upriv = upriv; + ctx->state = EZUSB_CTX_START; + ctx->out_rid = out_rid; + ctx->in_rid = in_rid; + + atomic_set(&ctx->refcount, 1); + init_completion(&ctx->done); + + init_timer(&ctx->timer); + ctx->timer.function = ezusb_request_timerfn; + ctx->timer.data = (u_long) ctx; + return ctx; +} + + +/* Hopefully the real complete_all will soon be exported, in the mean + * while this should work. */ +static inline void ezusb_complete_all(struct completion *comp) +{ + complete(comp); + complete(comp); + complete(comp); + complete(comp); +} + +static void ezusb_ctx_complete(struct request_context *ctx) +{ + struct ezusb_priv *upriv = ctx->upriv; + unsigned long flags; + + spin_lock_irqsave(&upriv->req_lock, flags); + + list_del_init(&ctx->list); + if (upriv->udev) { + spin_unlock_irqrestore(&upriv->req_lock, flags); + ezusb_req_queue_run(upriv); + spin_lock_irqsave(&upriv->req_lock, flags); + } + + switch (ctx->state) { + case EZUSB_CTX_COMPLETE: + case EZUSB_CTX_REQSUBMIT_FAIL: + case EZUSB_CTX_REQ_FAILED: + case EZUSB_CTX_REQ_TIMEOUT: + case EZUSB_CTX_RESP_TIMEOUT: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + if ((ctx->out_rid == EZUSB_RID_TX) && upriv->dev) { + struct net_device *dev = upriv->dev; + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + + if (ctx->state != EZUSB_CTX_COMPLETE) + stats->tx_errors++; + else + stats->tx_packets++; + + netif_wake_queue(dev); + } + ezusb_complete_all(&ctx->done); + ezusb_request_context_put(ctx); + break; + + default: + spin_unlock_irqrestore(&upriv->req_lock, flags); + if (!upriv->udev) { + /* This is normal, as all request contexts get flushed + * when the device is disconnected */ + err("Called, CTX not terminating, but device gone"); + ezusb_complete_all(&ctx->done); + ezusb_request_context_put(ctx); + break; + } + + err("Called, CTX not in terminating state."); + /* Things are really bad if this happens. Just leak + * the CTX because it may still be linked to the + * queue or the OUT urb may still be active. + * Just leaking at least prevents an Oops or Panic. + */ + break; + } +} + +/** + * ezusb_req_queue_run: + * Description: + * Note: Only one active CTX at any one time, because there's no + * other (reliable) way to match the response URB to the correct + * CTX. + **/ +static void ezusb_req_queue_run(struct ezusb_priv *upriv) +{ + unsigned long flags; + struct request_context *ctx; + int result; + + spin_lock_irqsave(&upriv->req_lock, flags); + + if (!list_empty(&upriv->req_active)) + goto unlock; + + if (list_empty(&upriv->req_pending)) + goto unlock; + + ctx = + list_entry(upriv->req_pending.next, struct request_context, + list); + + if (!ctx->upriv->udev) + goto unlock; + + /* We need to split this off to avoid a race condition */ + list_move_tail(&ctx->list, &upriv->req_active); + + if (ctx->state == EZUSB_CTX_QUEUED) { + atomic_inc(&ctx->refcount); + result = usb_submit_urb(ctx->outurb, GFP_ATOMIC); + if (result) { + ctx->state = EZUSB_CTX_REQSUBMIT_FAIL; + + spin_unlock_irqrestore(&upriv->req_lock, flags); + + err("Fatal, failed to submit command urb." + " error=%d\n", result); + + ezusb_ctx_complete(ctx); + ezusb_request_context_put(ctx); + goto done; + } + + ctx->state = EZUSB_CTX_REQ_SUBMITTED; + ezusb_mod_timer(ctx->upriv, &ctx->timer, + jiffies + DEF_TIMEOUT); + } + + unlock: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + done: + return; +} + +static void ezusb_req_enqueue_run(struct ezusb_priv *upriv, + struct request_context *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&upriv->req_lock, flags); + + if (!ctx->upriv->udev) { + spin_unlock_irqrestore(&upriv->req_lock, flags); + goto done; + } + atomic_inc(&ctx->refcount); + list_add_tail(&ctx->list, &upriv->req_pending); + spin_unlock_irqrestore(&upriv->req_lock, flags); + + ctx->state = EZUSB_CTX_QUEUED; + ezusb_req_queue_run(upriv); + + done: + return; +} + +static void ezusb_request_out_callback(struct urb *urb) +{ + unsigned long flags; + enum ezusb_state state; + struct request_context *ctx = urb->context; + struct ezusb_priv *upriv = ctx->upriv; + + spin_lock_irqsave(&upriv->req_lock, flags); + + del_timer(&ctx->timer); + + if (ctx->killed) { + spin_unlock_irqrestore(&upriv->req_lock, flags); + pr_warning("interrupt called with dead ctx"); + goto out; + } + + state = ctx->state; + + if (urb->status == 0) { + switch (state) { + case EZUSB_CTX_REQ_SUBMITTED: + if (ctx->in_rid) { + ctx->state = EZUSB_CTX_REQ_COMPLETE; + /* reply URB still pending */ + ezusb_mod_timer(upriv, &ctx->timer, + jiffies + DEF_TIMEOUT); + spin_unlock_irqrestore(&upriv->req_lock, + flags); + break; + } + /* fall through */ + case EZUSB_CTX_RESP_RECEIVED: + /* IN already received before this OUT-ACK */ + ctx->state = EZUSB_CTX_COMPLETE; + spin_unlock_irqrestore(&upriv->req_lock, flags); + ezusb_ctx_complete(ctx); + break; + + default: + spin_unlock_irqrestore(&upriv->req_lock, flags); + err("Unexpected state(0x%x, %d) in OUT URB", + state, urb->status); + break; + } + } else { + /* If someone cancels the OUT URB then its status + * should be either -ECONNRESET or -ENOENT. + */ + switch (state) { + case EZUSB_CTX_REQ_SUBMITTED: + case EZUSB_CTX_RESP_RECEIVED: + ctx->state = EZUSB_CTX_REQ_FAILED; + /* fall through */ + + case EZUSB_CTX_REQ_FAILED: + case EZUSB_CTX_REQ_TIMEOUT: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + ezusb_ctx_complete(ctx); + break; + + default: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + err("Unexpected state(0x%x, %d) in OUT URB", + state, urb->status); + break; + } + } + out: + ezusb_request_context_put(ctx); +} + +static void ezusb_request_in_callback(struct ezusb_priv *upriv, + struct urb *urb) +{ + struct ezusb_packet *ans = urb->transfer_buffer; + struct request_context *ctx = NULL; + enum ezusb_state state; + unsigned long flags; + + /* Find the CTX on the active queue that requested this URB */ + spin_lock_irqsave(&upriv->req_lock, flags); + if (upriv->udev) { + struct list_head *item; + + list_for_each(item, &upriv->req_active) { + struct request_context *c; + int reply_count; + + c = list_entry(item, struct request_context, list); + reply_count = + ezusb_reply_inc(c->buf->req_reply_count); + if ((ans->ans_reply_count == reply_count) + && (le16_to_cpu(ans->hermes_rid) == c->in_rid)) { + ctx = c; + break; + } + dbg("Skipped (0x%x/0x%x) (%d/%d)", + le16_to_cpu(ans->hermes_rid), + c->in_rid, ans->ans_reply_count, reply_count); + } + } + + if (ctx == NULL) { + spin_unlock_irqrestore(&upriv->req_lock, flags); + err("%s: got unexpected RID: 0x%04X", __func__, + le16_to_cpu(ans->hermes_rid)); + ezusb_req_queue_run(upriv); + return; + } + + /* The data we want is in the in buffer, exchange */ + urb->transfer_buffer = ctx->buf; + ctx->buf = (void *) ans; + ctx->buf_length = urb->actual_length; + + state = ctx->state; + switch (state) { + case EZUSB_CTX_REQ_SUBMITTED: + /* We have received our response URB before + * our request has been acknowledged. Do NOT + * destroy our CTX yet, because our OUT URB + * is still alive ... + */ + ctx->state = EZUSB_CTX_RESP_RECEIVED; + spin_unlock_irqrestore(&upriv->req_lock, flags); + + /* Let the machine continue running. */ + break; + + case EZUSB_CTX_REQ_COMPLETE: + /* This is the usual path: our request + * has already been acknowledged, and + * we have now received the reply. + */ + ctx->state = EZUSB_CTX_COMPLETE; + + /* Stop the intimer */ + del_timer(&ctx->timer); + spin_unlock_irqrestore(&upriv->req_lock, flags); + + /* Call the completion handler */ + ezusb_ctx_complete(ctx); + break; + + default: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + pr_warning("Matched IN URB, unexpected context state(0x%x)", + state); + /* Throw this CTX away and try submitting another */ + del_timer(&ctx->timer); + ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; + usb_unlink_urb(ctx->outurb); + ezusb_req_queue_run(upriv); + break; + } /* switch */ +} + + +static void ezusb_req_ctx_wait(struct ezusb_priv *upriv, + struct request_context *ctx) +{ + switch (ctx->state) { + case EZUSB_CTX_QUEUED: + case EZUSB_CTX_REQ_SUBMITTED: + case EZUSB_CTX_REQ_COMPLETE: + case EZUSB_CTX_RESP_RECEIVED: + if (in_softirq()) { + /* If we get called from a timer, timeout timers don't + * get the chance to run themselves. So we make sure + * that we don't sleep for ever */ + int msecs = DEF_TIMEOUT * (1000 / HZ); + while (!ctx->done.done && msecs--) + udelay(1000); + } else { + wait_event_interruptible(ctx->done.wait, + ctx->done.done); + } + break; + default: + /* Done or failed - nothing to wait for */ + break; + } +} + +static inline u16 build_crc(struct ezusb_packet *data) +{ + u16 crc = 0; + u8 *bytes = (u8 *)data; + int i; + + for (i = 0; i < 8; i++) + crc = (crc << 1) + bytes[i]; + + return crc; +} + +/** + * ezusb_fill_req: + * + * if data == NULL and length > 0 the data is assumed to be already in + * the target buffer and only the header is filled. + * + */ +static int ezusb_fill_req(struct ezusb_packet *req, u16 length, u16 rid, + const void *data, u16 frame_type, u8 reply_count) +{ + int total_size = sizeof(*req) + length; + + BUG_ON(total_size > BULK_BUF_SIZE); + + req->magic = cpu_to_le16(EZUSB_MAGIC); + req->req_reply_count = reply_count; + req->ans_reply_count = 0; + req->frame_type = cpu_to_le16(frame_type); + req->size = cpu_to_le16(length + 4); + req->crc = cpu_to_le16(build_crc(req)); + req->hermes_len = cpu_to_le16(HERMES_BYTES_TO_RECLEN(length)); + req->hermes_rid = cpu_to_le16(rid); + if (data) + memcpy(req->data, data, length); + return total_size; +} + +static int ezusb_submit_in_urb(struct ezusb_priv *upriv) +{ + int retval = 0; + void *cur_buf = upriv->read_urb->transfer_buffer; + + if (upriv->read_urb->status == -EINPROGRESS) { + dbg("urb busy, not resubmiting"); + retval = -EBUSY; + goto exit; + } + usb_fill_bulk_urb(upriv->read_urb, upriv->udev, upriv->read_pipe, + cur_buf, BULK_BUF_SIZE, + ezusb_bulk_in_callback, upriv); + upriv->read_urb->transfer_flags = 0; + retval = usb_submit_urb(upriv->read_urb, GFP_ATOMIC); + if (retval) + err("%s submit failed %d", __func__, retval); + + exit: + return retval; +} + +static inline int ezusb_8051_cpucs(struct ezusb_priv *upriv, int reset) +{ + u8 res_val = reset; /* avoid argument promotion */ + + if (!upriv->udev) { + err("%s: !upriv->udev", __func__); + return -EFAULT; + } + return usb_control_msg(upriv->udev, + usb_sndctrlpipe(upriv->udev, 0), + EZUSB_REQUEST_FW_TRANS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_OUT, EZUSB_CPUCS_REG, 0, &res_val, + sizeof(res_val), DEF_TIMEOUT); +} + +static int ezusb_firmware_download(struct ezusb_priv *upriv, + struct ez_usb_fw *fw) +{ + u8 fw_buffer[FW_BUF_SIZE]; + int retval, addr; + int variant_offset; + + /* + * This byte is 1 and should be replaced with 0. The offset is + * 0x10AD in version 0.0.6. The byte in question should follow + * the end of the code pointed to by the jump in the beginning + * of the firmware. Also, it is read by code located at 0x358. + */ + variant_offset = be16_to_cpup((__be16 *) &fw->code[FW_VAR_OFFSET_PTR]); + if (variant_offset >= fw->size) { + printk(KERN_ERR PFX "Invalid firmware variant offset: " + "0x%04x\n", variant_offset); + retval = -EINVAL; + goto fail; + } + + retval = ezusb_8051_cpucs(upriv, 1); + if (retval < 0) + goto fail; + for (addr = 0; addr < fw->size; addr += FW_BUF_SIZE) { + /* 0x100-0x300 should be left alone, it contains card + * specific data, like USB enumeration information */ + if ((addr >= FW_HOLE_START) && (addr < FW_HOLE_END)) + continue; + + memcpy(fw_buffer, &fw->code[addr], FW_BUF_SIZE); + if (variant_offset >= addr && + variant_offset < addr + FW_BUF_SIZE) { + dbg("Patching card_variant byte at 0x%04X", + variant_offset); + fw_buffer[variant_offset - addr] = FW_VAR_VALUE; + } + retval = usb_control_msg(upriv->udev, + usb_sndctrlpipe(upriv->udev, 0), + EZUSB_REQUEST_FW_TRANS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE + | USB_DIR_OUT, + addr, 0x0, + fw_buffer, FW_BUF_SIZE, + DEF_TIMEOUT); + + if (retval < 0) + goto fail; + } + retval = ezusb_8051_cpucs(upriv, 0); + if (retval < 0) + goto fail; + + goto exit; + fail: + printk(KERN_ERR PFX "Firmware download failed, error %d\n", + retval); + exit: + return retval; +} + +static int ezusb_access_ltv(struct ezusb_priv *upriv, + struct request_context *ctx, + u16 length, const void *data, u16 frame_type, + void *ans_buff, int ans_size, u16 *ans_length) +{ + int req_size; + int retval = 0; + enum ezusb_state state; + + BUG_ON(in_irq()); + + if (!upriv->udev) { + dbg("Device disconnected"); + return -ENODEV; + } + + if (upriv->read_urb->status != -EINPROGRESS) + err("%s: in urb not pending", __func__); + + /* protect upriv->reply_count, guarantee sequential numbers */ + spin_lock_bh(&upriv->reply_count_lock); + req_size = ezusb_fill_req(ctx->buf, length, ctx->out_rid, data, + frame_type, upriv->reply_count); + usb_fill_bulk_urb(ctx->outurb, upriv->udev, upriv->write_pipe, + ctx->buf, req_size, + ezusb_request_out_callback, ctx); + + if (ctx->in_rid) + upriv->reply_count = ezusb_reply_inc(upriv->reply_count); + + ezusb_req_enqueue_run(upriv, ctx); + + spin_unlock_bh(&upriv->reply_count_lock); + + if (ctx->in_rid) + ezusb_req_ctx_wait(upriv, ctx); + + state = ctx->state; + switch (state) { + case EZUSB_CTX_COMPLETE: + retval = ctx->outurb->status; + break; + + case EZUSB_CTX_QUEUED: + case EZUSB_CTX_REQ_SUBMITTED: + if (!ctx->in_rid) + break; + default: + err("%s: Unexpected context state %d", __func__, + state); + /* fall though */ + case EZUSB_CTX_REQ_TIMEOUT: + case EZUSB_CTX_REQ_FAILED: + case EZUSB_CTX_RESP_TIMEOUT: + case EZUSB_CTX_REQSUBMIT_FAIL: + printk(KERN_ERR PFX "Access failed, resetting (state %d," + " reply_count %d)\n", state, upriv->reply_count); + upriv->reply_count = 0; + if (state == EZUSB_CTX_REQ_TIMEOUT + || state == EZUSB_CTX_RESP_TIMEOUT) { + printk(KERN_ERR PFX "ctx timed out\n"); + retval = -ETIMEDOUT; + } else { + printk(KERN_ERR PFX "ctx failed\n"); + retval = -EFAULT; + } + goto exit; + break; + } + if (ctx->in_rid) { + struct ezusb_packet *ans = ctx->buf; + int exp_len; + + if (ans->hermes_len != 0) + exp_len = le16_to_cpu(ans->hermes_len) * 2 + 12; + else + exp_len = 14; + + if (exp_len != ctx->buf_length) { + err("%s: length mismatch for RID 0x%04x: " + "expected %d, got %d", __func__, + ctx->in_rid, exp_len, ctx->buf_length); + retval = -EIO; + goto exit; + } + + if (ans_buff) + memcpy(ans_buff, ans->data, + min_t(int, exp_len, ans_size)); + if (ans_length) + *ans_length = le16_to_cpu(ans->hermes_len); + } + exit: + ezusb_request_context_put(ctx); + return retval; +} + +static int ezusb_write_ltv(hermes_t *hw, int bap, u16 rid, + u16 length, const void *data) +{ + struct ezusb_priv *upriv = hw->priv; + u16 frame_type; + struct request_context *ctx; + + if (length == 0) + return -EINVAL; + + length = HERMES_RECLEN_TO_BYTES(length); + + /* On memory mapped devices HERMES_RID_CNFGROUPADDRESSES can be + * set to be empty, but the USB bridge doesn't like it */ + if (length == 0) + return 0; + + ctx = ezusb_alloc_ctx(upriv, rid, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + if (rid == EZUSB_RID_TX) + frame_type = EZUSB_FRAME_DATA; + else + frame_type = EZUSB_FRAME_CONTROL; + + return ezusb_access_ltv(upriv, ctx, length, data, frame_type, + NULL, 0, NULL); +} + +static int ezusb_read_ltv(hermes_t *hw, int bap, u16 rid, + unsigned bufsize, u16 *length, void *buf) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + + if ((bufsize < 0) || (bufsize % 2)) + return -EINVAL; + + ctx = ezusb_alloc_ctx(upriv, rid, rid); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, 0, NULL, EZUSB_FRAME_CONTROL, + buf, bufsize, length); +} + +static int ezusb_doicmd_wait(hermes_t *hw, u16 cmd, u16 parm0, u16 parm1, + u16 parm2, struct hermes_response *resp) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + + __le16 data[4] = { + cpu_to_le16(cmd), + cpu_to_le16(parm0), + cpu_to_le16(parm1), + cpu_to_le16(parm2), + }; + dbg("0x%04X, parm0 0x%04X, parm1 0x%04X, parm2 0x%04X", + cmd, parm0, parm1, parm2); + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, + struct hermes_response *resp) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + + __le16 data[4] = { + cpu_to_le16(cmd), + cpu_to_le16(parm0), + 0, + 0, + }; + dbg("0x%04X, parm0 0x%04X", cmd, parm0); + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_bap_pread(struct hermes *hw, int bap, + void *buf, int len, u16 id, u16 offset) +{ + struct ezusb_priv *upriv = hw->priv; + struct ezusb_packet *ans = (void *) upriv->read_urb->transfer_buffer; + int actual_length = upriv->read_urb->actual_length; + + if (id == EZUSB_RID_RX) { + if ((sizeof(*ans) + offset + len) > actual_length) { + printk(KERN_ERR PFX "BAP read beyond buffer end " + "in rx frame\n"); + return -EINVAL; + } + memcpy(buf, ans->data + offset, len); + return 0; + } + + if (EZUSB_IS_INFO(id)) { + /* Include 4 bytes for length/type */ + if ((sizeof(*ans) + offset + len - 4) > actual_length) { + printk(KERN_ERR PFX "BAP read beyond buffer end " + "in info frame\n"); + return -EFAULT; + } + memcpy(buf, ans->data + offset - 4, len); + } else { + printk(KERN_ERR PFX "Unexpected fid 0x%04x\n", id); + return -EINVAL; + } + + return 0; +} + +static int ezusb_read_pda(struct hermes *hw, __le16 *pda, + u32 pda_addr, u16 pda_len) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + __le16 data[] = { + cpu_to_le16(pda_addr & 0xffff), + cpu_to_le16(pda_len - 4) + }; + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_READ_PDA, EZUSB_RID_READ_PDA); + if (!ctx) + return -ENOMEM; + + /* wl_lkm does not include PDA size in the PDA area. + * We will pad the information into pda, so other routines + * don't have to be modified */ + pda[0] = cpu_to_le16(pda_len - 2); + /* Includes CFG_PROD_DATA but not itself */ + pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ + + return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, &pda[2], pda_len - 4, + NULL); +} + +static int ezusb_program_init(struct hermes *hw, u32 entry_point) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + __le32 data = cpu_to_le32(entry_point); + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_INIT, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_program_end(struct hermes *hw) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_END, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, 0, NULL, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_program_bytes(struct hermes *hw, const char *buf, + u32 addr, u32 len) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + __le32 data = cpu_to_le32(addr); + int err; + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_SET_ADDR, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + err = ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); + if (err) + return err; + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_BYTES, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, len, buf, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_program(struct hermes *hw, const char *buf, + u32 addr, u32 len) +{ + u32 ch_addr; + u32 ch_len; + int err = 0; + + /* We can only send 2048 bytes out of the bulk xmit at a time, + * so we have to split any programming into chunks of <2048 + * bytes. */ + + ch_len = (len < MAX_DL_SIZE) ? len : MAX_DL_SIZE; + ch_addr = addr; + + while (ch_addr < (addr + len)) { + pr_debug("Programming subblock of length %d " + "to address 0x%08x. Data @ %p\n", + ch_len, ch_addr, &buf[ch_addr - addr]); + + err = ezusb_program_bytes(hw, &buf[ch_addr - addr], + ch_addr, ch_len); + if (err) + break; + + ch_addr += ch_len; + ch_len = ((addr + len - ch_addr) < MAX_DL_SIZE) ? + (addr + len - ch_addr) : MAX_DL_SIZE; + } + + return err; +} + +static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct ezusb_priv *upriv = priv->card; + u8 mic[MICHAEL_MIC_LEN+1]; + int err = 0; + int tx_control; + unsigned long flags; + struct request_context *ctx; + u8 *buf; + int tx_size; + + if (!netif_running(dev)) { + printk(KERN_ERR "%s: Tx on stopped device!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (netif_queue_stopped(dev)) { + printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_ERR + "%s: ezusb_xmit() called while hw_unavailable\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (!netif_carrier_ok(dev) || + (priv->iw_mode == NL80211_IFTYPE_MONITOR)) { + /* Oops, the firmware hasn't established a connection, + silently drop the packet (this seems to be the + safest approach). */ + goto drop; + } + + /* Check packet length */ + if (skb->len < ETH_HLEN) + goto drop; + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0); + if (!ctx) + goto busy; + + memset(ctx->buf, 0, BULK_BUF_SIZE); + buf = ctx->buf->data; + + tx_control = 0; + + err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, + &mic[0]); + if (err) + goto drop; + + { + __le16 *tx_cntl = (__le16 *)buf; + *tx_cntl = cpu_to_le16(tx_control); + buf += sizeof(*tx_cntl); + } + + memcpy(buf, skb->data, skb->len); + buf += skb->len; + + if (tx_control & HERMES_TXCTRL_MIC) { + u8 *m = mic; + /* Mic has been offset so it can be copied to an even + * address. We're copying eveything anyway, so we + * don't need to copy that first byte. */ + if (skb->len % 2) + m++; + memcpy(buf, m, MICHAEL_MIC_LEN); + buf += MICHAEL_MIC_LEN; + } + + /* Finally, we actually initiate the send */ + netif_stop_queue(dev); + + /* The card may behave better if we send evenly sized usb transfers */ + tx_size = ALIGN(buf - ctx->buf->data, 2); + + err = ezusb_access_ltv(upriv, ctx, tx_size, NULL, + EZUSB_FRAME_DATA, NULL, 0, NULL); + + if (err) { + netif_start_queue(dev); + if (net_ratelimit()) + printk(KERN_ERR "%s: Error %d transmitting packet\n", + dev->name, err); + goto busy; + } + + dev->trans_start = jiffies; + stats->tx_bytes += skb->len; + goto ok; + + drop: + stats->tx_errors++; + stats->tx_dropped++; + + ok: + orinoco_unlock(priv, &flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + + busy: + orinoco_unlock(priv, &flags); + return NETDEV_TX_BUSY; +} + +static int ezusb_allocate(struct hermes *hw, u16 size, u16 *fid) +{ + *fid = EZUSB_RID_TX; + return 0; +} + + +static int ezusb_hard_reset(struct orinoco_private *priv) +{ + struct ezusb_priv *upriv = priv->card; + int retval = ezusb_8051_cpucs(upriv, 1); + + if (retval < 0) { + err("Failed to reset"); + return retval; + } + + retval = ezusb_8051_cpucs(upriv, 0); + if (retval < 0) { + err("Failed to unreset"); + return retval; + } + + dbg("sending control message"); + retval = usb_control_msg(upriv->udev, + usb_sndctrlpipe(upriv->udev, 0), + EZUSB_REQUEST_TRIGER, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_OUT, 0x0, 0x0, NULL, 0, + DEF_TIMEOUT); + if (retval < 0) { + err("EZUSB_REQUEST_TRIGER failed retval %d", retval); + return retval; + } +#if 0 + dbg("Sending EZUSB_REQUEST_TRIG_AC"); + retval = usb_control_msg(upriv->udev, + usb_sndctrlpipe(upriv->udev, 0), + EZUSB_REQUEST_TRIG_AC, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_OUT, 0x00FA, 0x0, NULL, 0, + DEF_TIMEOUT); + if (retval < 0) { + err("EZUSB_REQUEST_TRIG_AC failed retval %d", retval); + return retval; + } +#endif + + return 0; +} + + +static int ezusb_init(hermes_t *hw) +{ + struct ezusb_priv *upriv = hw->priv; + int retval; + + BUG_ON(in_interrupt()); + BUG_ON(!upriv); + + upriv->reply_count = 0; + /* Write the MAGIC number on the simulated registers to keep + * orinoco.c happy */ + hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); + hermes_write_regn(hw, RXFID, EZUSB_RID_RX); + + usb_kill_urb(upriv->read_urb); + ezusb_submit_in_urb(upriv); + + retval = ezusb_write_ltv(hw, 0, EZUSB_RID_INIT1, + HERMES_BYTES_TO_RECLEN(2), "\x10\x00"); + if (retval < 0) { + printk(KERN_ERR PFX "EZUSB_RID_INIT1 error %d\n", retval); + return retval; + } + + retval = ezusb_docmd_wait(hw, HERMES_CMD_INIT, 0, NULL); + if (retval < 0) { + printk(KERN_ERR PFX "HERMES_CMD_INIT error %d\n", retval); + return retval; + } + + return 0; +} + +static void ezusb_bulk_in_callback(struct urb *urb) +{ + struct ezusb_priv *upriv = (struct ezusb_priv *) urb->context; + struct ezusb_packet *ans = urb->transfer_buffer; + u16 crc; + u16 hermes_rid; + + if (upriv->udev == NULL) { + dbg("disconnected"); + return; + } + + if (urb->status == -ETIMEDOUT) { + /* When a device gets unplugged we get this every time + * we resubmit, flooding the logs. Since we don't use + * USB timeouts, it shouldn't happen any other time*/ + pr_warning("%s: urb timed out, not resubmiting", __func__); + return; + } + if (urb->status == -ECONNABORTED) { + pr_warning("%s: connection abort, resubmiting urb", + __func__); + goto resubmit; + } + if ((urb->status == -EILSEQ) + || (urb->status == -ENOENT) + || (urb->status == -ECONNRESET)) { + dbg("status %d, not resubmiting", urb->status); + return; + } + if (urb->status) + dbg("status: %d length: %d", + urb->status, urb->actual_length); + if (urb->actual_length < sizeof(*ans)) { + err("%s: short read, ignoring", __func__); + goto resubmit; + } + crc = build_crc(ans); + if (le16_to_cpu(ans->crc) != crc) { + err("CRC error, ignoring packet"); + goto resubmit; + } + + hermes_rid = le16_to_cpu(ans->hermes_rid); + if ((hermes_rid != EZUSB_RID_RX) && !EZUSB_IS_INFO(hermes_rid)) { + ezusb_request_in_callback(upriv, urb); + } else if (upriv->dev) { + struct net_device *dev = upriv->dev; + struct orinoco_private *priv = ndev_priv(dev); + hermes_t *hw = &priv->hw; + + if (hermes_rid == EZUSB_RID_RX) { + __orinoco_ev_rx(dev, hw); + } else { + hermes_write_regn(hw, INFOFID, + le16_to_cpu(ans->hermes_rid)); + __orinoco_ev_info(dev, hw); + } + } + + resubmit: + if (upriv->udev) + ezusb_submit_in_urb(upriv); +} + +static inline void ezusb_delete(struct ezusb_priv *upriv) +{ + struct net_device *dev; + struct list_head *item; + struct list_head *tmp_item; + unsigned long flags; + + BUG_ON(in_interrupt()); + BUG_ON(!upriv); + + dev = upriv->dev; + mutex_lock(&upriv->mtx); + + upriv->udev = NULL; /* No timer will be rearmed from here */ + + usb_kill_urb(upriv->read_urb); + + spin_lock_irqsave(&upriv->req_lock, flags); + list_for_each_safe(item, tmp_item, &upriv->req_active) { + struct request_context *ctx; + int err; + + ctx = list_entry(item, struct request_context, list); + atomic_inc(&ctx->refcount); + + ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; + err = usb_unlink_urb(ctx->outurb); + + spin_unlock_irqrestore(&upriv->req_lock, flags); + if (err == -EINPROGRESS) + wait_for_completion(&ctx->done); + + del_timer_sync(&ctx->timer); + /* FIXME: there is an slight chance for the irq handler to + * be running */ + if (!list_empty(&ctx->list)) + ezusb_ctx_complete(ctx); + + ezusb_request_context_put(ctx); + spin_lock_irqsave(&upriv->req_lock, flags); + } + spin_unlock_irqrestore(&upriv->req_lock, flags); + + list_for_each_safe(item, tmp_item, &upriv->req_pending) + ezusb_ctx_complete(list_entry(item, + struct request_context, list)); + + if (upriv->read_urb->status == -EINPROGRESS) + printk(KERN_ERR PFX "Some URB in progress\n"); + + mutex_unlock(&upriv->mtx); + + kfree(upriv->read_urb->transfer_buffer); + if (upriv->bap_buf != NULL) + kfree(upriv->bap_buf); + if (upriv->read_urb != NULL) + usb_free_urb(upriv->read_urb); + if (upriv->dev) { + struct orinoco_private *priv = ndev_priv(upriv->dev); + orinoco_if_del(priv); + free_orinocodev(priv); + } +} + +static void ezusb_lock_irqsave(spinlock_t *lock, + unsigned long *flags) __acquires(lock) +{ + spin_lock_bh(lock); +} + +static void ezusb_unlock_irqrestore(spinlock_t *lock, + unsigned long *flags) __releases(lock) +{ + spin_unlock_bh(lock); +} + +static void ezusb_lock_irq(spinlock_t *lock) __acquires(lock) +{ + spin_lock_bh(lock); +} + +static void ezusb_unlock_irq(spinlock_t *lock) __releases(lock) +{ + spin_unlock_bh(lock); +} + +static const struct hermes_ops ezusb_ops = { + .init = ezusb_init, + .cmd_wait = ezusb_docmd_wait, + .init_cmd_wait = ezusb_doicmd_wait, + .allocate = ezusb_allocate, + .read_ltv = ezusb_read_ltv, + .write_ltv = ezusb_write_ltv, + .bap_pread = ezusb_bap_pread, + .read_pda = ezusb_read_pda, + .program_init = ezusb_program_init, + .program_end = ezusb_program_end, + .program = ezusb_program, + .lock_irqsave = ezusb_lock_irqsave, + .unlock_irqrestore = ezusb_unlock_irqrestore, + .lock_irq = ezusb_lock_irq, + .unlock_irq = ezusb_unlock_irq, +}; + +static const struct net_device_ops ezusb_netdev_ops = { + .ndo_open = orinoco_open, + .ndo_stop = orinoco_stop, + .ndo_start_xmit = ezusb_xmit, + .ndo_set_multicast_list = orinoco_set_multicast_list, + .ndo_change_mtu = orinoco_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = orinoco_tx_timeout, + .ndo_get_stats = orinoco_get_stats, +}; + +static int ezusb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct orinoco_private *priv; + hermes_t *hw; + struct ezusb_priv *upriv = NULL; + struct usb_interface_descriptor *iface_desc; + struct usb_endpoint_descriptor *ep; + const struct firmware *fw_entry; + int retval = 0; + int i; + + priv = alloc_orinocodev(sizeof(*upriv), &udev->dev, + ezusb_hard_reset, NULL); + if (!priv) { + err("Couldn't allocate orinocodev"); + goto exit; + } + + hw = &priv->hw; + + upriv = priv->card; + + mutex_init(&upriv->mtx); + spin_lock_init(&upriv->reply_count_lock); + + spin_lock_init(&upriv->req_lock); + INIT_LIST_HEAD(&upriv->req_pending); + INIT_LIST_HEAD(&upriv->req_active); + + upriv->udev = udev; + + hw->iobase = (void __force __iomem *) &upriv->hermes_reg_fake; + hw->reg_spacing = HERMES_16BIT_REGSPACING; + hw->priv = upriv; + hw->ops = &ezusb_ops; + + /* set up the endpoint information */ + /* check out the endpoints */ + + iface_desc = &interface->altsetting[0].desc; + for (i = 0; i < iface_desc->bNumEndpoints; ++i) { + ep = &interface->altsetting[0].endpoint[i].desc; + + if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) && + ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk in endpoint */ + if (upriv->read_urb != NULL) { + pr_warning("Found a second bulk in ep, ignored"); + continue; + } + + upriv->read_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!upriv->read_urb) { + err("No free urbs available"); + goto error; + } + if (le16_to_cpu(ep->wMaxPacketSize) != 64) + pr_warning("bulk in: wMaxPacketSize!= 64"); + if (ep->bEndpointAddress != (2 | USB_DIR_IN)) + pr_warning("bulk in: bEndpointAddress: %d", + ep->bEndpointAddress); + upriv->read_pipe = usb_rcvbulkpipe(udev, + ep-> + bEndpointAddress); + upriv->read_urb->transfer_buffer = + kmalloc(BULK_BUF_SIZE, GFP_KERNEL); + if (!upriv->read_urb->transfer_buffer) { + err("Couldn't allocate IN buffer"); + goto error; + } + } + + if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_OUT) && + ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk out endpoint */ + if (upriv->bap_buf != NULL) { + pr_warning("Found a second bulk out ep, ignored"); + continue; + } + + if (le16_to_cpu(ep->wMaxPacketSize) != 64) + pr_warning("bulk out: wMaxPacketSize != 64"); + if (ep->bEndpointAddress != 2) + pr_warning("bulk out: bEndpointAddress: %d", + ep->bEndpointAddress); + upriv->write_pipe = usb_sndbulkpipe(udev, + ep-> + bEndpointAddress); + upriv->bap_buf = kmalloc(BULK_BUF_SIZE, GFP_KERNEL); + if (!upriv->bap_buf) { + err("Couldn't allocate bulk_out_buffer"); + goto error; + } + } + } + if (!upriv->bap_buf || !upriv->read_urb) { + err("Didn't find the required bulk endpoints"); + goto error; + } + + if (request_firmware(&fw_entry, "orinoco_ezusb_fw", + &interface->dev) == 0) { + firmware.size = fw_entry->size; + firmware.code = fw_entry->data; + } + if (firmware.size && firmware.code) { + ezusb_firmware_download(upriv, &firmware); + } else { + err("No firmware to download"); + goto error; + } + + if (ezusb_hard_reset(priv) < 0) { + err("Cannot reset the device"); + goto error; + } + + /* If the firmware is already downloaded orinoco.c will call + * ezusb_init but if the firmware is not already there, that will make + * the kernel very unstable, so we try initializing here and quit in + * case of error */ + if (ezusb_init(hw) < 0) { + err("Couldn't initialize the device"); + err("Firmware may not be downloaded or may be wrong."); + goto error; + } + + /* Initialise the main driver */ + if (orinoco_init(priv) != 0) { + err("orinoco_init() failed\n"); + goto error; + } + + if (orinoco_if_add(priv, 0, 0, &ezusb_netdev_ops) != 0) { + upriv->dev = NULL; + err("%s: orinoco_if_add() failed", __func__); + goto error; + } + upriv->dev = priv->ndev; + + goto exit; + + error: + ezusb_delete(upriv); + if (upriv->dev) { + /* upriv->dev was 0, so ezusb_delete() didn't free it */ + free_orinocodev(priv); + } + upriv = NULL; + retval = -EFAULT; + exit: + if (fw_entry) { + firmware.code = NULL; + firmware.size = 0; + release_firmware(fw_entry); + } + usb_set_intfdata(interface, upriv); + return retval; +} + + +static void ezusb_disconnect(struct usb_interface *intf) +{ + struct ezusb_priv *upriv = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + ezusb_delete(upriv); + printk(KERN_INFO PFX "Disconnected\n"); +} + + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver orinoco_driver = { + .name = DRIVER_NAME, + .probe = ezusb_probe, + .disconnect = ezusb_disconnect, + .id_table = ezusb_table, +}; + +/* Can't be declared "const" or the whole __initdata section will + * become const */ +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (Manuel Estrada Sainz)"; + +static int __init ezusb_module_init(void) +{ + int err; + + printk(KERN_DEBUG "%s\n", version); + + /* register this driver with the USB subsystem */ + err = usb_register(&orinoco_driver); + if (err < 0) { + printk(KERN_ERR PFX "usb_register failed, error %d\n", + err); + return err; + } + + return 0; +} + +static void __exit ezusb_module_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&orinoco_driver); +} + + +module_init(ezusb_module_init); +module_exit(ezusb_module_exit); + +MODULE_AUTHOR("Manuel Estrada Sainz"); +MODULE_DESCRIPTION + ("Driver for Orinoco wireless LAN cards using EZUSB bridge"); +MODULE_LICENSE("Dual MPL/GPL"); diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c index 330d42d..4300d9d 100644 --- a/drivers/net/wireless/orinoco/scan.c +++ b/drivers/net/wireless/orinoco/scan.c @@ -127,7 +127,7 @@ void orinoco_add_extscan_result(struct orinoco_private *priv, { struct wiphy *wiphy = priv_to_wiphy(priv); struct ieee80211_channel *channel; - u8 *ie; + const u8 *ie; u64 timestamp; s32 signal; u16 capability; @@ -136,7 +136,7 @@ void orinoco_add_extscan_result(struct orinoco_private *priv, int chan, freq; ie_len = len - sizeof(*bss); - ie = orinoco_get_ie(bss->data, ie_len, WLAN_EID_DS_PARAMS); + ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len); chan = ie ? ie[2] : 0; freq = ieee80211_dsss_chan_to_freq(chan); channel = ieee80211_get_channel(wiphy, freq); diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c index 41b9ce4..b51a9ad 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -337,6 +337,7 @@ spectrum_cs_config(struct pcmcia_device *link) goto failed; hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); + hw->eeprom_pda = true; /* * This actually configures the PCMCIA socket -- setting up @@ -359,7 +360,7 @@ spectrum_cs_config(struct pcmcia_device *link) /* Register an interface with the stack */ if (orinoco_if_add(priv, link->io.BasePort1, - link->irq) != 0) { + link->irq, NULL) != 0) { printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } @@ -384,9 +385,9 @@ spectrum_cs_release(struct pcmcia_device *link) /* We're committed to taking the device away now, so mark the * hardware as unavailable */ - spin_lock_irqsave(&priv->lock, flags); + priv->hw.ops->lock_irqsave(&priv->lock, &flags); priv->hw_unavailable++; - spin_unlock_irqrestore(&priv->lock, flags); + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); pcmcia_disable_device(link); if (priv->hw.iobase) diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c index fbcc6e1..5775124 100644 --- a/drivers/net/wireless/orinoco/wext.c +++ b/drivers/net/wireless/orinoco/wext.c @@ -458,7 +458,7 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { /* Fast channel change - no commit if successful */ hermes_t *hw = &priv->hw; - err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_SET_CHANNEL, chan, NULL); } @@ -538,125 +538,6 @@ static int orinoco_ioctl_setsens(struct net_device *dev, return -EINPROGRESS; /* Call commit handler */ } -static int orinoco_ioctl_setrts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int val = rrq->value; - unsigned long flags; - - if (rrq->disabled) - val = 2347; - - if ((val < 0) || (val > 2347)) - return -EINVAL; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - priv->rts_thresh = val; - orinoco_unlock(priv, &flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -static int orinoco_ioctl_getrts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - - rrq->value = priv->rts_thresh; - rrq->disabled = (rrq->value == 2347); - rrq->fixed = 1; - - return 0; -} - -static int orinoco_ioctl_setfrag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *frq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int err = -EINPROGRESS; /* Call commit handler */ - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if (priv->has_mwo) { - if (frq->disabled) - priv->mwo_robust = 0; - else { - if (frq->fixed) - printk(KERN_WARNING "%s: Fixed fragmentation " - "is not supported on this firmware. " - "Using MWO robust instead.\n", - dev->name); - priv->mwo_robust = 1; - } - } else { - if (frq->disabled) - priv->frag_thresh = 2346; - else { - if ((frq->value < 256) || (frq->value > 2346)) - err = -EINVAL; - else - /* must be even */ - priv->frag_thresh = frq->value & ~0x1; - } - } - - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_getfrag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *frq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - hermes_t *hw = &priv->hw; - int err; - u16 val; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if (priv->has_mwo) { - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFMWOROBUST_AGERE, - &val); - if (err) - val = 0; - - frq->value = val ? 2347 : 0; - frq->disabled = !val; - frq->fixed = 0; - } else { - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, - &val); - if (err) - val = 0; - - frq->value = val; - frq->disabled = (val >= 2346); - frq->fixed = 1; - } - - orinoco_unlock(priv, &flags); - - return err; -} - static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, @@ -1201,60 +1082,6 @@ static int orinoco_ioctl_set_mlme(struct net_device *dev, return ret; } -static int orinoco_ioctl_getretry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - hermes_t *hw = &priv->hw; - int err = 0; - u16 short_limit, long_limit, lifetime; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, - &short_limit); - if (err) - goto out; - - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, - &long_limit); - if (err) - goto out; - - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, - &lifetime); - if (err) - goto out; - - rrq->disabled = 0; /* Can't be disabled */ - - /* Note : by default, display the retry number */ - if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { - rrq->flags = IW_RETRY_LIFETIME; - rrq->value = lifetime * 1000; /* ??? */ - } else { - /* By default, display the min number */ - if ((rrq->flags & IW_RETRY_LONG)) { - rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - rrq->value = long_limit; - } else { - rrq->flags = IW_RETRY_LIMIT; - rrq->value = short_limit; - if (short_limit != long_limit) - rrq->flags |= IW_RETRY_SHORT; - } - } - - out: - orinoco_unlock(priv, &flags); - - return err; -} - static int orinoco_ioctl_reset(struct net_device *dev, struct iw_request_info *info, void *wrqu, @@ -1446,8 +1273,8 @@ static int orinoco_ioctl_getrid(struct net_device *dev, if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, - extra); + err = hw->ops->read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, + extra); if (err) goto out; @@ -1506,46 +1333,44 @@ static const struct iw_priv_args orinoco_privtab[] = { * Structures to export the Wireless Handlers */ -#define STD_IW_HANDLER(id, func) \ - [IW_IOCTL_IDX(id)] = (iw_handler) func static const iw_handler orinoco_handler[] = { - STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), - STD_IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), - STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), - STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), - STD_IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode), - STD_IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode), - STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), - STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), - STD_IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange), - STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), - STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), - STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), - STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), - STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), - STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), - STD_IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan), - STD_IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan), - STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), - STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), - STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), - STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), - STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), - STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts), - STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag), - STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag), - STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry), - STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), - STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), - STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), - STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), - STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), - STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), - STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), - STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), - STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), - STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), - STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), + IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit), + IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq), + IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq), + IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode), + IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode), + IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens), + IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens), + IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap), + IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap), + IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), + IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), + IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid), + IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid), + IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate), + IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate), + IW_HANDLER(SIOCSIWRTS, (iw_handler)cfg80211_wext_siwrts), + IW_HANDLER(SIOCGIWRTS, (iw_handler)cfg80211_wext_giwrts), + IW_HANDLER(SIOCSIWFRAG, (iw_handler)cfg80211_wext_siwfrag), + IW_HANDLER(SIOCGIWFRAG, (iw_handler)cfg80211_wext_giwfrag), + IW_HANDLER(SIOCGIWRETRY, (iw_handler)cfg80211_wext_giwretry), + IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode), + IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode), + IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower), + IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), + IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), + IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), + IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), + IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), + IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), }; @@ -1553,15 +1378,15 @@ static const iw_handler orinoco_handler[] = { Added typecasting since we no longer use iwreq_data -- Moustafa */ static const iw_handler orinoco_private_handler[] = { - [0] = (iw_handler) orinoco_ioctl_reset, - [1] = (iw_handler) orinoco_ioctl_reset, - [2] = (iw_handler) orinoco_ioctl_setport3, - [3] = (iw_handler) orinoco_ioctl_getport3, - [4] = (iw_handler) orinoco_ioctl_setpreamble, - [5] = (iw_handler) orinoco_ioctl_getpreamble, - [6] = (iw_handler) orinoco_ioctl_setibssport, - [7] = (iw_handler) orinoco_ioctl_getibssport, - [9] = (iw_handler) orinoco_ioctl_getrid, + [0] = (iw_handler)orinoco_ioctl_reset, + [1] = (iw_handler)orinoco_ioctl_reset, + [2] = (iw_handler)orinoco_ioctl_setport3, + [3] = (iw_handler)orinoco_ioctl_getport3, + [4] = (iw_handler)orinoco_ioctl_setpreamble, + [5] = (iw_handler)orinoco_ioctl_getpreamble, + [6] = (iw_handler)orinoco_ioctl_setibssport, + [7] = (iw_handler)orinoco_ioctl_getibssport, + [9] = (iw_handler)orinoco_ioctl_getrid, }; const struct iw_handler_def orinoco_handler_def = { |