diff options
Diffstat (limited to 'drivers/net/wireless/libertas/cmd.c')
| -rw-r--r-- | drivers/net/wireless/libertas/cmd.c | 767 | 
1 files changed, 407 insertions, 360 deletions
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index cdb9b9650d7..70745928f3f 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -6,18 +6,14 @@  #include <linux/kfifo.h>  #include <linux/sched.h>  #include <linux/slab.h> +#include <linux/if_arp.h> -#include "host.h"  #include "decl.h" -#include "defs.h" -#include "dev.h" -#include "assoc.h" -#include "wext.h" -#include "scan.h" +#include "cfg.h"  #include "cmd.h" - -static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv); +#define CAL_NF(nf)		((s32)(-(s32)(nf))) +#define CAL_RSSI(snr, nf)	((s32)((s32)(snr) + CAL_NF(nf)))  /**   *  @brief Simple callback that copies response back into command @@ -70,6 +66,8 @@ static u8 is_command_allowed_in_ps(u16 cmd)  	switch (cmd) {  	case CMD_802_11_RSSI:  		return 1; +	case CMD_802_11_HOST_SLEEP_CFG: +		return 1;  	default:  		break;  	} @@ -77,30 +75,6 @@ static u8 is_command_allowed_in_ps(u16 cmd)  }  /** - *  @brief This function checks if the command is allowed. - * - *  @param priv         A pointer to lbs_private structure - *  @return             allowed or not allowed. - */ - -static int lbs_is_cmd_allowed(struct lbs_private *priv) -{ -	int ret = 1; - -	lbs_deb_enter(LBS_DEB_CMD); - -	if (!priv->is_auto_deep_sleep_enabled) { -		if (priv->is_deep_sleep) { -			lbs_deb_cmd("command not allowed in deep sleep\n"); -			ret = 0; -		} -	} - -	lbs_deb_leave(LBS_DEB_CMD); -	return ret; -} - -/**   *  @brief Updates the hardware details like MAC address and regulatory region   *   *  @param priv    	A pointer to struct lbs_private structure @@ -175,16 +149,28 @@ int lbs_update_hw_spec(struct lbs_private *priv)  	if (priv->mesh_dev)  		memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN); -	if (lbs_set_regiontable(priv, priv->regioncode, 0)) { -		ret = -1; -		goto out; -	} -  out:  	lbs_deb_leave(LBS_DEB_CMD);  	return ret;  } +static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, +			struct cmd_header *resp) +{ +	lbs_deb_enter(LBS_DEB_CMD); +	if (priv->is_host_sleep_activated) { +		priv->is_host_sleep_configured = 0; +		if (priv->psstate == PS_STATE_FULL_POWER) { +			priv->is_host_sleep_activated = 0; +			wake_up_interruptible(&priv->host_sleep_q); +		} +	} else { +		priv->is_host_sleep_configured = 1; +	} +	lbs_deb_leave(LBS_DEB_CMD); +	return 0; +} +  int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,  		struct wol_config *p_wol_config)  { @@ -202,12 +188,11 @@ int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,  	else  		cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE; -	ret = lbs_cmd_with_response(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config); +	ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr, +			le16_to_cpu(cmd_config.hdr.size), +			lbs_ret_host_sleep_cfg, 0);  	if (!ret) { -		if (criteria) { -			lbs_deb_cmd("Set WOL criteria to %x\n", criteria); -			priv->wol_criteria = criteria; -		} else +		if (p_wol_config)  			memcpy((uint8_t *) p_wol_config,  					(uint8_t *)&cmd_config.wol_conf,  					sizeof(struct wol_config)); @@ -219,42 +204,49 @@ int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,  }  EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg); -static int lbs_cmd_802_11_ps_mode(struct cmd_ds_command *cmd, -				   u16 cmd_action) +/** + *  @brief Sets the Power Save mode + * + *  @param priv    	A pointer to struct lbs_private structure + *  @param cmd_action	The Power Save operation (PS_MODE_ACTION_ENTER_PS or + *                         PS_MODE_ACTION_EXIT_PS) + *  @param block	Whether to block on a response or not + * + *  @return 	   	0 on success, error on failure + */ +int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block)  { -	struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode; +	struct cmd_ds_802_11_ps_mode cmd; +	int ret = 0;  	lbs_deb_enter(LBS_DEB_CMD); -	cmd->command = cpu_to_le16(CMD_802_11_PS_MODE); -	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) + -				sizeof(struct cmd_header)); -	psm->action = cpu_to_le16(cmd_action); -	psm->multipledtim = 0; -	switch (cmd_action) { -	case CMD_SUBCMD_ENTER_PS: -		lbs_deb_cmd("PS command:" "SubCode- Enter PS\n"); - -		psm->locallisteninterval = 0; -		psm->nullpktinterval = 0; -		psm->multipledtim = -		    cpu_to_le16(MRVDRV_DEFAULT_MULTIPLE_DTIM); -		break; - -	case CMD_SUBCMD_EXIT_PS: -		lbs_deb_cmd("PS command:" "SubCode- Exit PS\n"); -		break; - -	case CMD_SUBCMD_SLEEP_CONFIRMED: -		lbs_deb_cmd("PS command: SubCode- sleep confirm\n"); -		break; +	memset(&cmd, 0, sizeof(cmd)); +	cmd.hdr.size = cpu_to_le16(sizeof(cmd)); +	cmd.action = cpu_to_le16(cmd_action); -	default: -		break; +	if (cmd_action == PS_MODE_ACTION_ENTER_PS) { +		lbs_deb_cmd("PS_MODE: action ENTER_PS\n"); +		cmd.multipledtim = cpu_to_le16(1);  /* Default DTIM multiple */ +	} else if (cmd_action == PS_MODE_ACTION_EXIT_PS) { +		lbs_deb_cmd("PS_MODE: action EXIT_PS\n"); +	} else { +		/* We don't handle CONFIRM_SLEEP here because it needs to +		 * be fastpathed to the firmware. +		 */ +		lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action); +		ret = -EOPNOTSUPP; +		goto out;  	} -	lbs_deb_leave(LBS_DEB_CMD); -	return 0; +	if (block) +		ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd); +	else +		lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd)); + +out: +	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); +	return ret;  }  int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, @@ -353,6 +345,65 @@ int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)  	return ret;  } +static int lbs_ret_host_sleep_activate(struct lbs_private *priv, +		unsigned long dummy, +		struct cmd_header *cmd) +{ +	lbs_deb_enter(LBS_DEB_FW); +	priv->is_host_sleep_activated = 1; +	wake_up_interruptible(&priv->host_sleep_q); +	lbs_deb_leave(LBS_DEB_FW); +	return 0; +} + +int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) +{ +	struct cmd_header cmd; +	int ret = 0; +	uint32_t criteria = EHS_REMOVE_WAKEUP; + +	lbs_deb_enter(LBS_DEB_CMD); + +	if (host_sleep) { +		if (priv->is_host_sleep_activated != 1) { +			memset(&cmd, 0, sizeof(cmd)); +			ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, +					(struct wol_config *)NULL); +			if (ret) { +				lbs_pr_info("Host sleep configuration failed: " +						"%d\n", ret); +				return ret; +			} +			if (priv->psstate == PS_STATE_FULL_POWER) { +				ret = __lbs_cmd(priv, +						CMD_802_11_HOST_SLEEP_ACTIVATE, +						&cmd, +						sizeof(cmd), +						lbs_ret_host_sleep_activate, 0); +				if (ret) +					lbs_pr_info("HOST_SLEEP_ACTIVATE " +							"failed: %d\n", ret); +			} + +			if (!wait_event_interruptible_timeout( +						priv->host_sleep_q, +						priv->is_host_sleep_activated, +						(10 * HZ))) { +				lbs_pr_err("host_sleep_q: timer expired\n"); +				ret = -1; +			} +		} else { +			lbs_pr_err("host sleep: already enabled\n"); +		} +	} else { +		if (priv->is_host_sleep_activated) +			ret = lbs_host_sleep_cfg(priv, criteria, +					(struct wol_config *)NULL); +	} + +	return ret; +} +  /**   *  @brief Set an SNMP MIB value   * @@ -509,23 +560,35 @@ int lbs_set_tx_power(struct lbs_private *priv, s16 dbm)  	return ret;  } -static int lbs_cmd_802_11_monitor_mode(struct cmd_ds_command *cmd, -				      u16 cmd_action, void *pdata_buf) +/** + *  @brief Enable or disable monitor mode (only implemented on OLPC usb8388 FW) + * + *  @param priv        A pointer to struct lbs_private structure + *  @param enable      1 to enable monitor mode, 0 to disable + * + *  @return            0 on success, error on failure + */ +int lbs_set_monitor_mode(struct lbs_private *priv, int enable)  { -	struct cmd_ds_802_11_monitor_mode *monitor = &cmd->params.monitor; +	struct cmd_ds_802_11_monitor_mode cmd; +	int ret; -	cmd->command = cpu_to_le16(CMD_802_11_MONITOR_MODE); -	cmd->size = -	    cpu_to_le16(sizeof(struct cmd_ds_802_11_monitor_mode) + -			     sizeof(struct cmd_header)); +	memset(&cmd, 0, sizeof(cmd)); +	cmd.hdr.size = cpu_to_le16(sizeof(cmd)); +	cmd.action = cpu_to_le16(CMD_ACT_SET); +	if (enable) +		cmd.mode = cpu_to_le16(0x1); -	monitor->action = cpu_to_le16(cmd_action); -	if (cmd_action == CMD_ACT_SET) { -		monitor->mode = -		    cpu_to_le16((u16) (*(u32 *) pdata_buf)); +	lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable); + +	ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd); +	if (ret == 0) { +		priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP : +						ARPHRD_ETHER;  	} -	return 0; +	lbs_deb_leave(LBS_DEB_CMD); +	return ret;  }  /** @@ -610,78 +673,242 @@ out:  	return ret;  } -static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr, -			       u8 cmd_action, void *pdata_buf) +/** + *  @brief Get current RSSI and noise floor + * + *  @param priv		A pointer to struct lbs_private structure + *  @param rssi		On successful return, signal level in mBm + * + *  @return 	   	The channel on success, error on failure + */ +int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf)  { -	struct lbs_offset_value *offval; +	struct cmd_ds_802_11_rssi cmd; +	int ret = 0;  	lbs_deb_enter(LBS_DEB_CMD); -	offval = (struct lbs_offset_value *)pdata_buf; +	BUG_ON(rssi == NULL); +	BUG_ON(nf == NULL); -	switch (le16_to_cpu(cmdptr->command)) { -	case CMD_MAC_REG_ACCESS: -		{ -			struct cmd_ds_mac_reg_access *macreg; +	memset(&cmd, 0, sizeof(cmd)); +	cmd.hdr.size = cpu_to_le16(sizeof(cmd)); +	/* Average SNR over last 8 beacons */ +	cmd.n_or_snr = cpu_to_le16(8); -			cmdptr->size = -			    cpu_to_le16(sizeof (struct cmd_ds_mac_reg_access) -					+ sizeof(struct cmd_header)); -			macreg = -			    (struct cmd_ds_mac_reg_access *)&cmdptr->params. -			    macreg; +	ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd); +	if (ret == 0) { +		*nf = CAL_NF(le16_to_cpu(cmd.nf)); +		*rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf)); +	} -			macreg->action = cpu_to_le16(cmd_action); -			macreg->offset = cpu_to_le16((u16) offval->offset); -			macreg->value = cpu_to_le32(offval->value); +	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); +	return ret; +} -			break; -		} +/** + *  @brief Send regulatory and 802.11d domain information to the firmware + * + *  @param priv		pointer to struct lbs_private + *  @param request	cfg80211 regulatory request structure + *  @param bands	the device's supported bands and channels + * + *  @return		0 on success, error code on failure +*/ +int lbs_set_11d_domain_info(struct lbs_private *priv, +			    struct regulatory_request *request, +			    struct ieee80211_supported_band **bands) +{ +	struct cmd_ds_802_11d_domain_info cmd; +	struct mrvl_ie_domain_param_set *domain = &cmd.domain; +	struct ieee80211_country_ie_triplet *t; +	enum ieee80211_band band; +	struct ieee80211_channel *ch; +	u8 num_triplet = 0; +	u8 num_parsed_chan = 0; +	u8 first_channel = 0, next_chan = 0, max_pwr = 0; +	u8 i, flag = 0; +	size_t triplet_size; +	int ret; -	case CMD_BBP_REG_ACCESS: -		{ -			struct cmd_ds_bbp_reg_access *bbpreg; +	lbs_deb_enter(LBS_DEB_11D); -			cmdptr->size = -			    cpu_to_le16(sizeof -					     (struct cmd_ds_bbp_reg_access) -					     + sizeof(struct cmd_header)); -			bbpreg = -			    (struct cmd_ds_bbp_reg_access *)&cmdptr->params. -			    bbpreg; +	memset(&cmd, 0, sizeof(cmd)); +	cmd.action = cpu_to_le16(CMD_ACT_SET); -			bbpreg->action = cpu_to_le16(cmd_action); -			bbpreg->offset = cpu_to_le16((u16) offval->offset); -			bbpreg->value = (u8) offval->value; +	lbs_deb_11d("Setting country code '%c%c'\n", +		    request->alpha2[0], request->alpha2[1]); -			break; -		} +	domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); -	case CMD_RF_REG_ACCESS: -		{ -			struct cmd_ds_rf_reg_access *rfreg; +	/* Set country code */ +	domain->country_code[0] = request->alpha2[0]; +	domain->country_code[1] = request->alpha2[1]; +	domain->country_code[2] = ' '; -			cmdptr->size = -			    cpu_to_le16(sizeof -					     (struct cmd_ds_rf_reg_access) + -					     sizeof(struct cmd_header)); -			rfreg = -			    (struct cmd_ds_rf_reg_access *)&cmdptr->params. -			    rfreg; +	/* Now set up the channel triplets; firmware is somewhat picky here +	 * and doesn't validate channel numbers and spans; hence it would +	 * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39.  Since +	 * the last 3 aren't valid channels, the driver is responsible for +	 * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20) +	 * etc. +	 */ +	for (band = 0; +	     (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS); +	     band++) { -			rfreg->action = cpu_to_le16(cmd_action); -			rfreg->offset = cpu_to_le16((u16) offval->offset); -			rfreg->value = (u8) offval->value; +		if (!bands[band]) +			continue; -			break; +		for (i = 0; +		     (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS); +		     i++) { +			ch = &bands[band]->channels[i]; +			if (ch->flags & IEEE80211_CHAN_DISABLED) +				continue; + +			if (!flag) { +				flag = 1; +				next_chan = first_channel = (u32) ch->hw_value; +				max_pwr = ch->max_power; +				num_parsed_chan = 1; +				continue; +			} + +			if ((ch->hw_value == next_chan + 1) && +					(ch->max_power == max_pwr)) { +				/* Consolidate adjacent channels */ +				next_chan++; +				num_parsed_chan++; +			} else { +				/* Add this triplet */ +				lbs_deb_11d("11D triplet (%d, %d, %d)\n", +					first_channel, num_parsed_chan, +					max_pwr); +				t = &domain->triplet[num_triplet]; +				t->chans.first_channel = first_channel; +				t->chans.num_channels = num_parsed_chan; +				t->chans.max_power = max_pwr; +				num_triplet++; +				flag = 0; +			}  		} -	default: -		break; +		if (flag) { +			/* Add last triplet */ +			lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, +				num_parsed_chan, max_pwr); +			t = &domain->triplet[num_triplet]; +			t->chans.first_channel = first_channel; +			t->chans.num_channels = num_parsed_chan; +			t->chans.max_power = max_pwr; +			num_triplet++; +		}  	} -	lbs_deb_leave(LBS_DEB_CMD); -	return 0; +	lbs_deb_11d("# triplets %d\n", num_triplet); + +	/* Set command header sizes */ +	triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet); +	domain->header.len = cpu_to_le16(sizeof(domain->country_code) + +					triplet_size); + +	lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set", +			(u8 *) &cmd.domain.country_code, +			le16_to_cpu(domain->header.len)); + +	cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) + +				   sizeof(cmd.action) + +				   sizeof(cmd.domain.header) + +				   sizeof(cmd.domain.country_code) + +				   triplet_size); + +	ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); + +	lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); +	return ret; +} + +/** + *  @brief Read a MAC, Baseband, or RF register + * + *  @param priv		pointer to struct lbs_private + *  @param cmd		register command, one of CMD_MAC_REG_ACCESS, + *                        CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS + *  @param offset       byte offset of the register to get + *  @param value        on success, the value of the register at 'offset' + * + *  @return		0 on success, error code on failure +*/ +int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) +{ +	struct cmd_ds_reg_access cmd; +	int ret = 0; + +	lbs_deb_enter(LBS_DEB_CMD); + +	BUG_ON(value == NULL); + +	memset(&cmd, 0, sizeof(cmd)); +	cmd.hdr.size = cpu_to_le16(sizeof(cmd)); +	cmd.action = cpu_to_le16(CMD_ACT_GET); + +	if (reg != CMD_MAC_REG_ACCESS && +	    reg != CMD_BBP_REG_ACCESS && +	    reg != CMD_RF_REG_ACCESS) { +		ret = -EINVAL; +		goto out; +	} + +	ret = lbs_cmd_with_response(priv, reg, &cmd); +	if (ret) { +		if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) +			*value = cmd.value.bbp_rf; +		else if (reg == CMD_MAC_REG_ACCESS) +			*value = le32_to_cpu(cmd.value.mac); +	} + +out: +	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); +	return ret; +} + +/** + *  @brief Write a MAC, Baseband, or RF register + * + *  @param priv		pointer to struct lbs_private + *  @param cmd		register command, one of CMD_MAC_REG_ACCESS, + *                        CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS + *  @param offset       byte offset of the register to set + *  @param value        the value to write to the register at 'offset' + * + *  @return		0 on success, error code on failure +*/ +int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) +{ +	struct cmd_ds_reg_access cmd; +	int ret = 0; + +	lbs_deb_enter(LBS_DEB_CMD); + +	memset(&cmd, 0, sizeof(cmd)); +	cmd.hdr.size = cpu_to_le16(sizeof(cmd)); +	cmd.action = cpu_to_le16(CMD_ACT_SET); + +	if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) +		cmd.value.bbp_rf = (u8) (value & 0xFF); +	else if (reg == CMD_MAC_REG_ACCESS) +		cmd.value.mac = cpu_to_le32(value); +	else { +		ret = -EINVAL; +		goto out; +	} + +	ret = lbs_cmd_with_response(priv, reg, &cmd); + +out: +	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); +	return ret;  }  static void lbs_queue_cmd(struct lbs_private *priv, @@ -704,14 +931,17 @@ static void lbs_queue_cmd(struct lbs_private *priv,  	/* Exit_PS command needs to be queued in the header always. */  	if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { -		struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf[1]; +		struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf; -		if (psm->action == cpu_to_le16(CMD_SUBCMD_EXIT_PS)) { +		if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {  			if (priv->psstate != PS_STATE_FULL_POWER)  				addtail = 0;  		}  	} +	if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM) +		addtail = 0; +  	spin_lock_irqsave(&priv->driver_lock, flags);  	if (addtail) @@ -744,7 +974,6 @@ static void lbs_submit_command(struct lbs_private *priv,  	spin_lock_irqsave(&priv->driver_lock, flags);  	priv->cur_cmd = cmdnode; -	priv->cur_cmd_retcode = 0;  	spin_unlock_irqrestore(&priv->driver_lock, flags);  	cmdsize = le16_to_cpu(cmd->size); @@ -817,9 +1046,6 @@ static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,  void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,  			  int result)  { -	if (cmd == priv->cur_cmd) -		priv->cur_cmd_retcode = result; -  	cmd->result = result;  	cmd->cmdwaitqwoken = 1;  	wake_up_interruptible(&cmd->cmdwait_q); @@ -887,175 +1113,6 @@ void lbs_set_mac_control(struct lbs_private *priv)  }  /** - *  @brief This function prepare the command before send to firmware. - * - *  @param priv		A pointer to struct lbs_private structure - *  @param cmd_no	command number - *  @param cmd_action	command action: GET or SET - *  @param wait_option	wait option: wait response or not - *  @param cmd_oid	cmd oid: treated as sub command - *  @param pdata_buf	A pointer to informaion buffer - *  @return 		0 or -1 - */ -int lbs_prepare_and_send_command(struct lbs_private *priv, -			  u16 cmd_no, -			  u16 cmd_action, -			  u16 wait_option, u32 cmd_oid, void *pdata_buf) -{ -	int ret = 0; -	struct cmd_ctrl_node *cmdnode; -	struct cmd_ds_command *cmdptr; -	unsigned long flags; - -	lbs_deb_enter(LBS_DEB_HOST); - -	if (!priv) { -		lbs_deb_host("PREP_CMD: priv is NULL\n"); -		ret = -1; -		goto done; -	} - -	if (priv->surpriseremoved) { -		lbs_deb_host("PREP_CMD: card removed\n"); -		ret = -1; -		goto done; -	} - -	if (!lbs_is_cmd_allowed(priv)) { -		ret = -EBUSY; -		goto done; -	} - -	cmdnode = lbs_get_cmd_ctrl_node(priv); - -	if (cmdnode == NULL) { -		lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); - -		/* Wake up main thread to execute next command */ -		wake_up_interruptible(&priv->waitq); -		ret = -1; -		goto done; -	} - -	cmdnode->callback = NULL; -	cmdnode->callback_arg = (unsigned long)pdata_buf; - -	cmdptr = (struct cmd_ds_command *)cmdnode->cmdbuf; - -	lbs_deb_host("PREP_CMD: command 0x%04x\n", cmd_no); - -	/* Set sequence number, command and INT option */ -	priv->seqnum++; -	cmdptr->seqnum = cpu_to_le16(priv->seqnum); - -	cmdptr->command = cpu_to_le16(cmd_no); -	cmdptr->result = 0; - -	switch (cmd_no) { -	case CMD_802_11_PS_MODE: -		ret = lbs_cmd_802_11_ps_mode(cmdptr, cmd_action); -		break; - -	case CMD_MAC_REG_ACCESS: -	case CMD_BBP_REG_ACCESS: -	case CMD_RF_REG_ACCESS: -		ret = lbs_cmd_reg_access(cmdptr, cmd_action, pdata_buf); -		break; - -	case CMD_802_11_MONITOR_MODE: -		ret = lbs_cmd_802_11_monitor_mode(cmdptr, -				          cmd_action, pdata_buf); -		break; - -	case CMD_802_11_RSSI: -		ret = lbs_cmd_802_11_rssi(priv, cmdptr); -		break; - -	case CMD_802_11_SET_AFC: -	case CMD_802_11_GET_AFC: - -		cmdptr->command = cpu_to_le16(cmd_no); -		cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) + -					   sizeof(struct cmd_header)); - -		memmove(&cmdptr->params.afc, -			pdata_buf, sizeof(struct cmd_ds_802_11_afc)); - -		ret = 0; -		goto done; - -	case CMD_802_11_TPC_CFG: -		cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG); -		cmdptr->size = -		    cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) + -				     sizeof(struct cmd_header)); - -		memmove(&cmdptr->params.tpccfg, -			pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg)); - -		ret = 0; -		break; - -#ifdef CONFIG_LIBERTAS_MESH - -	case CMD_BT_ACCESS: -		ret = lbs_cmd_bt_access(cmdptr, cmd_action, pdata_buf); -		break; - -	case CMD_FWT_ACCESS: -		ret = lbs_cmd_fwt_access(cmdptr, cmd_action, pdata_buf); -		break; - -#endif - -	case CMD_802_11_BEACON_CTRL: -		ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action); -		break; -	case CMD_802_11_DEEP_SLEEP: -		cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP); -		cmdptr->size = cpu_to_le16(sizeof(struct cmd_header)); -		break; -	default: -		lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no); -		ret = -1; -		break; -	} - -	/* return error, since the command preparation failed */ -	if (ret != 0) { -		lbs_deb_host("PREP_CMD: command preparation failed\n"); -		lbs_cleanup_and_insert_cmd(priv, cmdnode); -		ret = -1; -		goto done; -	} - -	cmdnode->cmdwaitqwoken = 0; - -	lbs_queue_cmd(priv, cmdnode); -	wake_up_interruptible(&priv->waitq); - -	if (wait_option & CMD_OPTION_WAITFORRSP) { -		lbs_deb_host("PREP_CMD: wait for response\n"); -		might_sleep(); -		wait_event_interruptible(cmdnode->cmdwait_q, -					 cmdnode->cmdwaitqwoken); -	} - -	spin_lock_irqsave(&priv->driver_lock, flags); -	if (priv->cur_cmd_retcode) { -		lbs_deb_host("PREP_CMD: command failed with return code %d\n", -		       priv->cur_cmd_retcode); -		priv->cur_cmd_retcode = 0; -		ret = -1; -	} -	spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: -	lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); -	return ret; -} - -/**   *  @brief This function allocates the command buffer and link   *  it to command free queue.   * @@ -1148,7 +1205,7 @@ done:   *  @param priv		A pointer to struct lbs_private structure   *  @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL   */ -static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv) +static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv)  {  	struct cmd_ctrl_node *tempnode;  	unsigned long flags; @@ -1231,10 +1288,10 @@ int lbs_execute_next_command(struct lbs_private *priv)  			/*  			 * 1. Non-PS command:  			 * Queue it. set needtowakeup to TRUE if current state -			 * is SLEEP, otherwise call lbs_ps_wakeup to send Exit_PS. -			 * 2. PS command but not Exit_PS: +			 * is SLEEP, otherwise call send EXIT_PS. +			 * 2. PS command but not EXIT_PS:  			 * Ignore it. -			 * 3. PS command Exit_PS: +			 * 3. PS command EXIT_PS:  			 * Set needtowakeup to TRUE if current state is SLEEP,  			 * otherwise send this command down to firmware  			 * immediately. @@ -1248,8 +1305,11 @@ int lbs_execute_next_command(struct lbs_private *priv)  					/* w/ new scheme, it will not reach here.  					   since it is blocked in main_thread. */  					priv->needtowakeup = 1; -				} else -					lbs_ps_wakeup(priv, 0); +				} else { +					lbs_set_ps_mode(priv, +							PS_MODE_ACTION_EXIT_PS, +							false); +				}  				ret = 0;  				goto done; @@ -1264,7 +1324,7 @@ int lbs_execute_next_command(struct lbs_private *priv)  				       "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n",  				       psm->action);  				if (psm->action != -				    cpu_to_le16(CMD_SUBCMD_EXIT_PS)) { +				    cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {  					lbs_deb_host(  					       "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");  					list_del(&cmdnode->list); @@ -1303,6 +1363,15 @@ int lbs_execute_next_command(struct lbs_private *priv)  		 * check if in power save mode, if yes, put the device back  		 * to PS mode  		 */ +#ifdef TODO +		/* +		 * This was the old code for libertas+wext. Someone that +		 * understands this beast should re-code it in a sane way. +		 * +		 * I actually don't understand why this is related to WPA +		 * and to connection status, shouldn't powering should be +		 * independ of such things? +		 */  		if ((priv->psmode != LBS802_11POWERMODECAM) &&  		    (priv->psstate == PS_STATE_FULL_POWER) &&  		    ((priv->connect_status == LBS_CONNECTED) || @@ -1315,15 +1384,19 @@ int lbs_execute_next_command(struct lbs_private *priv)  					lbs_deb_host(  					       "EXEC_NEXT_CMD: WPA enabled and GTK_SET"  					       " go back to PS_SLEEP"); -					lbs_ps_sleep(priv, 0); +					lbs_set_ps_mode(priv, +							PS_MODE_ACTION_ENTER_PS, +							false);  				}  			} else {  				lbs_deb_host(  				       "EXEC_NEXT_CMD: cmdpendingq empty, "  				       "go back to PS_SLEEP"); -				lbs_ps_sleep(priv, 0); +				lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, +						false);  			}  		} +#endif  	}  	ret = 0; @@ -1353,6 +1426,11 @@ static void lbs_send_confirmsleep(struct lbs_private *priv)  	/* We don't get a response on the sleep-confirmation */  	priv->dnld_sent = DNLD_RES_RECEIVED; +	if (priv->is_host_sleep_configured) { +		priv->is_host_sleep_activated = 1; +		wake_up_interruptible(&priv->host_sleep_q); +	} +  	/* If nothing to do, go back to sleep (?) */  	if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx])  		priv->psstate = PS_STATE_SLEEP; @@ -1363,43 +1441,6 @@ out:  	lbs_deb_leave(LBS_DEB_HOST);  } -void lbs_ps_sleep(struct lbs_private *priv, int wait_option) -{ -	lbs_deb_enter(LBS_DEB_HOST); - -	/* -	 * PS is currently supported only in Infrastructure mode -	 * Remove this check if it is to be supported in IBSS mode also -	 */ - -	lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE, -			      CMD_SUBCMD_ENTER_PS, wait_option, 0, NULL); - -	lbs_deb_leave(LBS_DEB_HOST); -} - -/** - *  @brief This function sends Exit_PS command to firmware. - * - *  @param priv    	A pointer to struct lbs_private structure - *  @param wait_option	wait response or not - *  @return 	   	n/a - */ -void lbs_ps_wakeup(struct lbs_private *priv, int wait_option) -{ -	__le32 Localpsmode; - -	lbs_deb_enter(LBS_DEB_HOST); - -	Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM); - -	lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE, -			      CMD_SUBCMD_EXIT_PS, -			      wait_option, 0, &Localpsmode); - -	lbs_deb_leave(LBS_DEB_HOST); -} -  /**   *  @brief This function checks condition and prepares to   *  send sleep confirm command to firmware if ok. @@ -1524,12 +1565,18 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,  		goto done;  	} -	if (!lbs_is_cmd_allowed(priv)) { -		cmdnode = ERR_PTR(-EBUSY); -		goto done; +	/* No commands are allowed in Deep Sleep until we toggle the GPIO +	 * to wake up the card and it has signaled that it's ready. +	 */ +	if (!priv->is_auto_deep_sleep_enabled) { +		if (priv->is_deep_sleep) { +			lbs_deb_cmd("command not allowed in deep sleep\n"); +			cmdnode = ERR_PTR(-EBUSY); +			goto done; +		}  	} -	cmdnode = lbs_get_cmd_ctrl_node(priv); +	cmdnode = lbs_get_free_cmd_node(priv);  	if (cmdnode == NULL) {  		lbs_deb_host("PREP_CMD: cmdnode is NULL\n");  |