Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[pandora-kernel.git] / drivers / net / wireless / iwlwifi / iwl-agn-rxon.c
index fbbde07..09f679d 100644 (file)
@@ -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,13 +273,22 @@ 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)
 {
        /* cast away the const for active_rxon in this function */
        struct iwl_rxon_cmd *active = (void *)&ctx->active;
        bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK);
-       bool old_assoc = !!(ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK);
        int ret;
 
        lockdep_assert_held(&priv->mutex);
@@ -173,12 +325,21 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                        return 0;
        }
 
+       /*
+        * force CTS-to-self frames protection if RTS-CTS is not preferred
+        * one aggregation protection method
+        */
+       if (!(priv->cfg->ht_params &&
+             priv->cfg->ht_params->use_rts_for_aggregation))
+               ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
+
        if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
            !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
                ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
        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");
@@ -189,10 +350,10 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
         * receive commit_rxon request
         * abort any previous channel switch if still in process
         */
-       if (priv->switch_rxon.switch_in_progress &&
-           (priv->switch_rxon.channel != ctx->staging.channel)) {
+       if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) &&
+           (priv->switch_channel != ctx->staging.channel)) {
                IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
-                     le16_to_cpu(priv->switch_rxon.channel));
+                             le16_to_cpu(priv->switch_channel));
                iwl_chswitch_done(priv, false);
        }
 
@@ -202,14 +363,18 @@ 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);
+               /*
+                * We do not commit tx power settings while channel changing,
+                * do it now if after settings changed.
+                */
+               iwl_set_tx_power(priv, priv->tx_power_next, false);
                return 0;
        }
 
@@ -219,7 +384,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"
@@ -236,93 +401,12 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
         * AP station must be done after the BSSID is set to correctly
         * 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);
-               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);
+       ret = iwlagn_rxon_disconn(priv, ctx);
+       if (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;
 }
@@ -595,6 +679,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;
                }
        }