diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-rxon.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 281 | 
1 files changed, 181 insertions, 100 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index dfdbea6e8f9..02387430f7f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -1,6 +1,6 @@  /******************************************************************************   * - * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.   *   * This program is free software; you can redistribute it and/or modify it   * under the terms of version 2 of the GNU General Public License as @@ -29,6 +29,7 @@  #include "iwl-sta.h"  #include "iwl-core.h"  #include "iwl-agn-calib.h" +#include "iwl-helpers.h"  static int iwlagn_disable_bss(struct iwl_priv *priv,  			      struct iwl_rxon_context *ctx, @@ -57,8 +58,9 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,  	u8 old_dev_type = send->dev_type;  	int ret; -	iwlagn_init_notification_wait(priv, &disable_wait, NULL, -				      REPLY_WIPAN_DEACTIVATION_COMPLETE); +	iwlagn_init_notification_wait(priv, &disable_wait, +				      REPLY_WIPAN_DEACTIVATION_COMPLETE, +				      NULL, NULL);  	send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;  	send->dev_type = RXON_DEV_TYPE_P2P; @@ -71,13 +73,9 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,  		IWL_ERR(priv, "Error disabling PAN (%d)\n", ret);  		iwlagn_remove_notification(priv, &disable_wait);  	} else { -		signed long wait_res; - -		wait_res = iwlagn_wait_notification(priv, &disable_wait, HZ); -		if (wait_res == 0) { +		ret = iwlagn_wait_notification(priv, &disable_wait, HZ); +		if (ret)  			IWL_ERR(priv, "Timed out waiting for PAN disable\n"); -			ret = -EIO; -		}  	}  	return ret; @@ -123,6 +121,151 @@ static int iwlagn_update_beacon(struct iwl_priv *priv,  	return iwlagn_send_beacon_cmd(priv);  } +static int iwlagn_send_rxon_assoc(struct iwl_priv *priv, +			   struct iwl_rxon_context *ctx) +{ +	int ret = 0; +	struct iwl_rxon_assoc_cmd rxon_assoc; +	const struct iwl_rxon_cmd *rxon1 = &ctx->staging; +	const struct iwl_rxon_cmd *rxon2 = &ctx->active; + +	if ((rxon1->flags == rxon2->flags) && +	    (rxon1->filter_flags == rxon2->filter_flags) && +	    (rxon1->cck_basic_rates == rxon2->cck_basic_rates) && +	    (rxon1->ofdm_ht_single_stream_basic_rates == +	     rxon2->ofdm_ht_single_stream_basic_rates) && +	    (rxon1->ofdm_ht_dual_stream_basic_rates == +	     rxon2->ofdm_ht_dual_stream_basic_rates) && +	    (rxon1->ofdm_ht_triple_stream_basic_rates == +	     rxon2->ofdm_ht_triple_stream_basic_rates) && +	    (rxon1->acquisition_data == rxon2->acquisition_data) && +	    (rxon1->rx_chain == rxon2->rx_chain) && +	    (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) { +		IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC.  Not resending.\n"); +		return 0; +	} + +	rxon_assoc.flags = ctx->staging.flags; +	rxon_assoc.filter_flags = ctx->staging.filter_flags; +	rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates; +	rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates; +	rxon_assoc.reserved1 = 0; +	rxon_assoc.reserved2 = 0; +	rxon_assoc.reserved3 = 0; +	rxon_assoc.ofdm_ht_single_stream_basic_rates = +	    ctx->staging.ofdm_ht_single_stream_basic_rates; +	rxon_assoc.ofdm_ht_dual_stream_basic_rates = +	    ctx->staging.ofdm_ht_dual_stream_basic_rates; +	rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain; +	rxon_assoc.ofdm_ht_triple_stream_basic_rates = +		 ctx->staging.ofdm_ht_triple_stream_basic_rates; +	rxon_assoc.acquisition_data = ctx->staging.acquisition_data; + +	ret = iwl_send_cmd_pdu_async(priv, ctx->rxon_assoc_cmd, +				     sizeof(rxon_assoc), &rxon_assoc, NULL); +	if (ret) +		return ret; + +	return ret; +} + +static int iwlagn_rxon_disconn(struct iwl_priv *priv, +			       struct iwl_rxon_context *ctx) +{ +	int ret; +	struct iwl_rxon_cmd *active = (void *)&ctx->active; + +	if (ctx->ctxid == IWL_RXON_CTX_BSS) +		ret = iwlagn_disable_bss(priv, ctx, &ctx->staging); +	else +		ret = iwlagn_disable_pan(priv, ctx, &ctx->staging); +	if (ret) +		return ret; + +	/* +	 * Un-assoc RXON clears the station table and WEP +	 * keys, so we have to restore those afterwards. +	 */ +	iwl_clear_ucode_stations(priv, ctx); +	iwl_restore_stations(priv, ctx); +	ret = iwl_restore_default_wep_keys(priv, ctx); +	if (ret) { +		IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); +		return ret; +	} + +	memcpy(active, &ctx->staging, sizeof(*active)); +	return 0; +} + +static int iwlagn_rxon_connect(struct iwl_priv *priv, +			       struct iwl_rxon_context *ctx) +{ +	int ret; +	struct iwl_rxon_cmd *active = (void *)&ctx->active; + +	/* RXON timing must be before associated RXON */ +	ret = iwl_send_rxon_timing(priv, ctx); +	if (ret) { +		IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); +		return ret; +	} +	/* QoS info may be cleared by previous un-assoc RXON */ +	iwlagn_update_qos(priv, ctx); + +	/* +	 * We'll run into this code path when beaconing is +	 * enabled, but then we also need to send the beacon +	 * to the device. +	 */ +	if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) { +		ret = iwlagn_update_beacon(priv, ctx->vif); +		if (ret) { +			IWL_ERR(priv, +				"Error sending required beacon (%d)!\n", +				ret); +			return ret; +		} +	} + +	priv->start_calib = 0; +	/* +	 * Apply the new configuration. +	 * +	 * Associated RXON doesn't clear the station table in uCode, +	 * so we don't need to restore stations etc. after this. +	 */ +	ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, +		      sizeof(struct iwl_rxon_cmd), &ctx->staging); +	if (ret) { +		IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); +		return ret; +	} +	memcpy(active, &ctx->staging, sizeof(*active)); + +	iwl_reprogram_ap_sta(priv, ctx); + +	/* IBSS beacon needs to be sent after setting assoc */ +	if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC)) +		if (iwlagn_update_beacon(priv, ctx->vif)) +			IWL_ERR(priv, "Error sending IBSS beacon\n"); +	iwl_init_sensitivity(priv); + +	/* +	 * If we issue a new RXON command which required a tune then +	 * we must send a new TXPOWER command or we won't be able to +	 * Tx any frames. +	 * +	 * It's expected we set power here if channel is changing. +	 */ +	ret = iwl_set_tx_power(priv, priv->tx_power_next, true); +	if (ret) { +		IWL_ERR(priv, "Error sending TX power (%d)\n", ret); +		return ret; +	} +	return 0; +} +  /**   * iwlagn_commit_rxon - commit staging_rxon to hardware   * @@ -130,6 +273,16 @@ static int iwlagn_update_beacon(struct iwl_priv *priv,   * the active_rxon structure is updated with the new data.  This   * function correctly transitions out of the RXON_ASSOC_MSK state if   * a HW tune is required based on the RXON structure changes. + * + * The connect/disconnect flow should be as the following: + * + * 1. make sure send RXON command with association bit unset if not connect + *	this should include the channel and the band for the candidate + *	to be connected to + * 2. Add Station before RXON association with the AP + * 3. RXON_timing has to send before RXON for connection + * 4. full RXON command - associated bit set + * 5. use RXON_ASSOC command to update any flags changes   */  int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)  { @@ -179,6 +332,7 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)  	else  		ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; +	iwl_print_rx_config_cmd(priv, ctx);  	ret = iwl_check_rxon_cmd(priv, ctx);  	if (ret) {  		IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n"); @@ -202,14 +356,13 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)  	 * and other flags for the current radio configuration.  	 */  	if (!iwl_full_rxon_required(priv, ctx)) { -		ret = iwl_send_rxon_assoc(priv, ctx); +		ret = iwlagn_send_rxon_assoc(priv, ctx);  		if (ret) {  			IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret);  			return ret;  		}  		memcpy(active, &ctx->staging, sizeof(*active)); -		iwl_print_rx_config_cmd(priv, ctx);  		return 0;  	} @@ -219,7 +372,7 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)  			return ret;  	} -	iwl_set_rxon_hwcrypto(priv, ctx, !priv->cfg->mod_params->sw_crypto); +	iwl_set_rxon_hwcrypto(priv, ctx, !iwlagn_mod_params.sw_crypto);  	IWL_DEBUG_INFO(priv,  		       "Going to commit RXON\n" @@ -237,92 +390,13 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)  	 * set up filters in the device.  	 */  	if ((old_assoc && new_assoc) || !new_assoc) { -		if (ctx->ctxid == IWL_RXON_CTX_BSS) -			ret = iwlagn_disable_bss(priv, ctx, &ctx->staging); -		else -			ret = iwlagn_disable_pan(priv, ctx, &ctx->staging); +		ret = iwlagn_rxon_disconn(priv, ctx);  		if (ret)  			return ret; - -		memcpy(active, &ctx->staging, sizeof(*active)); - -		/* -		 * Un-assoc RXON clears the station table and WEP -		 * keys, so we have to restore those afterwards. -		 */ -		iwl_clear_ucode_stations(priv, ctx); -		iwl_restore_stations(priv, ctx); -		ret = iwl_restore_default_wep_keys(priv, ctx); -		if (ret) { -			IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); -			return ret; -		} -	} - -	/* RXON timing must be before associated RXON */ -	ret = iwl_send_rxon_timing(priv, ctx); -	if (ret) { -		IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); -		return ret;  	} -	if (new_assoc) { -		/* QoS info may be cleared by previous un-assoc RXON */ -		iwlagn_update_qos(priv, ctx); - -		/* -		 * We'll run into this code path when beaconing is -		 * enabled, but then we also need to send the beacon -		 * to the device. -		 */ -		if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) { -			ret = iwlagn_update_beacon(priv, ctx->vif); -			if (ret) { -				IWL_ERR(priv, -					"Error sending required beacon (%d)!\n", -					ret); -				return ret; -			} -		} - -		priv->start_calib = 0; -		/* -		 * Apply the new configuration. -		 * -		 * Associated RXON doesn't clear the station table in uCode, -		 * so we don't need to restore stations etc. after this. -		 */ -		ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, -			      sizeof(struct iwl_rxon_cmd), &ctx->staging); -		if (ret) { -			IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); -			return ret; -		} -		memcpy(active, &ctx->staging, sizeof(*active)); - -		iwl_reprogram_ap_sta(priv, ctx); - -		/* IBSS beacon needs to be sent after setting assoc */ -		if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC)) -			if (iwlagn_update_beacon(priv, ctx->vif)) -				IWL_ERR(priv, "Error sending IBSS beacon\n"); -	} - -	iwl_print_rx_config_cmd(priv, ctx); - -	iwl_init_sensitivity(priv); - -	/* -	 * If we issue a new RXON command which required a tune then we must -	 * send a new TXPOWER command or we won't be able to Tx any frames. -	 * -	 * It's expected we set power here if channel is changing. -	 */ -	ret = iwl_set_tx_power(priv, priv->tx_power_next, true); -	if (ret) { -		IWL_ERR(priv, "Error sending TX power (%d)\n", ret); -		return ret; -	} +	if (new_assoc) +		return iwlagn_rxon_connect(priv, ctx);  	return 0;  } @@ -335,7 +409,6 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)  	struct ieee80211_channel *channel = conf->channel;  	const struct iwl_channel_info *ch_info;  	int ret = 0; -	bool ht_changed[NUM_IWL_RXON_CTX] = {};  	IWL_DEBUG_MAC80211(priv, "changed %#x", changed); @@ -383,10 +456,8 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)  		for_each_context(priv, ctx) {  			/* Configure HT40 channels */ -			if (ctx->ht.enabled != conf_is_ht(conf)) { +			if (ctx->ht.enabled != conf_is_ht(conf))  				ctx->ht.enabled = conf_is_ht(conf); -				ht_changed[ctx->ctxid] = true; -			}  			if (ctx->ht.enabled) {  				if (conf_is_ht40_minus(conf)) { @@ -455,8 +526,6 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)  		if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))  			continue;  		iwlagn_commit_rxon(priv, ctx); -		if (ht_changed[ctx->ctxid]) -			iwlagn_update_qos(priv, ctx);  	}   out:  	mutex_unlock(&priv->mutex); @@ -600,6 +669,18 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,  			priv->timestamp = bss_conf->timestamp;  			ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;  		} else { +			/* +			 * If we disassociate while there are pending +			 * frames, just wake up the queues and let the +			 * frames "escape" ... This shouldn't really +			 * be happening to start with, but we should +			 * not get stuck in this case either since it +			 * can happen if userspace gets confused. +			 */ +			if (ctx->last_tx_rejected) { +				ctx->last_tx_rejected = false; +				iwl_wake_any_queue(priv, ctx); +			}  			ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;  		}  	}  |