diff options
Diffstat (limited to 'drivers/net/wireless/orinoco')
21 files changed, 2402 insertions, 590 deletions
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index 6116b546861..60819bcf437 100644 --- a/drivers/net/wireless/orinoco/Kconfig +++ b/drivers/net/wireless/orinoco/Kconfig @@ -132,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 9abd6329bcb..bfdefb85abc 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 c60df2c1aca..9bcee10c930 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 27f2d334264..81d228de9e5 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);  } @@ -187,7 +189,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 +198,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 5ea0f7cf85b..3e1947d097c 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 1a2fca76fd3..6c6a23e08df 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 2dddbb597c4..9ca34e722b4 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 fb157eb889c..6da85e75fce 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); +		err = hw->ops->program(hw, blk->data, blkaddr, blklen); +		if (err) +			break; -			addr += len; -			len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ? -				(blkaddr + blklen - addr) : MAX_DL_SIZE; -		} -#endif  		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 9f657afaa3e..6fbd7885012 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); @@ -286,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; @@ -296,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; @@ -374,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: @@ -387,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", @@ -437,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); @@ -501,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) { @@ -509,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) { @@ -521,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); @@ -638,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) @@ -669,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])); @@ -849,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); @@ -1066,7 +1093,7 @@ int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,  			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); @@ -1108,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;  	} @@ -1187,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) @@ -1255,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); @@ -1319,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/main.c b/drivers/net/wireless/orinoco/main.c index 413e9ab6cab..1d60c7e4392 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,6 +334,7 @@ static int orinoco_change_mtu(struct net_device *dev, int new_mtu)  	return 0;  } +EXPORT_SYMBOL(orinoco_change_mtu);  /********************************************************************/  /* Tx path                                                          */ @@ -400,8 +405,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 +419,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 " @@ -458,8 +463,8 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)  		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); @@ -490,8 +495,8 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)  			    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, HERMES_802_3_OFFSET + offset);  		if (err) {  			printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",  			       dev->name, err); @@ -502,7 +507,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); @@ -572,9 +577,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 +620,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 +635,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 +770,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 +798,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 +820,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 +888,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 +919,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 +1152,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 +1201,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 +1224,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 +1254,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 +1361,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 +1379,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 +1401,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 +1437,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 +1502,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 +1555,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 @@ -1571,6 +1579,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)  	return;  } +EXPORT_SYMBOL(__orinoco_ev_info);  static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)  { @@ -1647,7 +1656,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 +1744,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 +1759,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 +1993,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 +2076,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 +2201,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,12 +2221,17 @@ 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 */ @@ -2305,7 +2320,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 +2340,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 +2352,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 +2360,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 21ab36cd76c..4dadf9880a9 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 665ef56f838..e9f415a56d4 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,24 @@ 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); + +/* 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 +215,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 +228,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 fdc96137917..f99b13ba92b 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -296,7 +296,7 @@ orinoco_cs_config(struct pcmcia_device *link)  	/* Register an interface with the stack */  	if (orinoco_if_add(priv, link->io.BasePort1, -			   link->irq.AssignedIRQ) != 0) { +			   link->irq.AssignedIRQ, NULL) != 0) {  		printk(KERN_ERR PFX "orinoco_if_add() failed\n");  		goto failed;  	} @@ -327,9 +327,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) diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c index 075f446b313..bc3ea0b67a4 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 bda5317cc59..468197f8667 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 e0d5874ab42..9358f4d2307 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 88cbc7902aa..784605f0af1 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 00000000000..e22093359f3 --- /dev/null +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -0,0 +1,1800 @@ +/* + * 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 "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; +	int err = 0; +	char *p; +	struct ethhdr *eh; +	int len, data_len, data_off; +	__le16 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: orinoco_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). */ +		stats->tx_errors++; +		orinoco_unlock(priv, &flags); +		dev_kfree_skb(skb); +		return NETDEV_TX_OK; +	} + +	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0); +	if (!ctx) +		goto fail; + +	memset(ctx->buf, 0, BULK_BUF_SIZE); +	buf = ctx->buf->data; + +	/* Length of the packet body */ +	/* FIXME: what if the skb is smaller than this? */ +	len = max_t(int, skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN); + +	eh = (struct ethhdr *) skb->data; + +	tx_control = cpu_to_le16(0); +	memcpy(buf, &tx_control, sizeof(tx_control)); +	buf += sizeof(tx_control); +	/* Encapsulate Ethernet-II frames */ +	if (ntohs(eh->h_proto) > ETH_DATA_LEN) {	/* Ethernet-II frame */ +		struct header_struct *hdr = (void *) buf; +		buf += sizeof(*hdr); +		data_len = len; +		data_off = sizeof(tx_control) + sizeof(*hdr); +		p = skb->data + ETH_HLEN; + +		/* 802.3 header */ +		memcpy(hdr->dest, eh->h_dest, ETH_ALEN); +		memcpy(hdr->src, eh->h_source, ETH_ALEN); +		hdr->len = htons(data_len + ENCAPS_OVERHEAD); + +		/* 802.2 header */ +		memcpy(&hdr->dsap, &encaps_hdr, sizeof(encaps_hdr)); + +		hdr->ethertype = eh->h_proto; +	} else {		/* IEEE 802.3 frame */ +		data_len = len + ETH_HLEN; +		data_off = sizeof(tx_control); +		p = skb->data; +	} + +	memcpy(buf, p, data_len); +	buf += data_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); +		stats->tx_errors++; +		goto fail; +	} + +	dev->trans_start = jiffies; +	stats->tx_bytes += data_off + data_len; + +	orinoco_unlock(priv, &flags); + +	dev_kfree_skb(skb); + +	return NETDEV_TX_OK; + + fail: +	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 330d42d4533..4300d9db7d8 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 59bda240fdc..9b1af4976bf 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -349,6 +349,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 @@ -374,7 +375,7 @@ spectrum_cs_config(struct pcmcia_device *link)  	/* Register an interface with the stack */  	if (orinoco_if_add(priv, link->io.BasePort1, -			   link->irq.AssignedIRQ) != 0) { +			   link->irq.AssignedIRQ, NULL) != 0) {  		printk(KERN_ERR PFX "orinoco_if_add() failed\n");  		goto failed;  	} @@ -405,9 +406,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 57b850ebfeb..5775124e2ae 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; @@ -1528,11 +1355,11 @@ static const iw_handler	orinoco_handler[] = {  	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)orinoco_ioctl_setrts), -	IW_HANDLER(SIOCGIWRTS,		(iw_handler)orinoco_ioctl_getrts), -	IW_HANDLER(SIOCSIWFRAG,		(iw_handler)orinoco_ioctl_setfrag), -	IW_HANDLER(SIOCGIWFRAG,		(iw_handler)orinoco_ioctl_getfrag), -	IW_HANDLER(SIOCGIWRETRY,	(iw_handler)orinoco_ioctl_getretry), +	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),  |