iwlwifi: fix for channel switch
authorWey-Yi Guy <wey-yi.w.guy@intel.com>
Fri, 6 Nov 2009 22:52:54 +0000 (14:52 -0800)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 11 Nov 2009 20:23:44 +0000 (15:23 -0500)
Different channel has different configuration, need to pass correct
configuration to uCode when send "channel switch" command to uCode.
Invalid configuration will cause sysassert in uCode and produce
un-expected result.

Even it is a very small windows, but we also need to consider and handle
the case if commit_rxon occurred before the "channel switch
announcement" notification received from uCode.

Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/iwlwifi/iwl-4965.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/iwlwifi/iwl-dev.h

index faa4c4e..1d22ea3 100644 (file)
@@ -1449,14 +1449,14 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
        is_ht40 = is_ht40_channel(priv->staging_rxon.flags);
 
        if (is_ht40 &&
-           (priv->active_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK))
+           (priv->staging_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK))
                ctrl_chan_high = 1;
 
        cmd.band = band;
        cmd.expect_beacon = 0;
        cmd.channel = cpu_to_le16(channel);
-       cmd.rxon_flags = priv->active_rxon.flags;
-       cmd.rxon_filter_flags = priv->active_rxon.filter_flags;
+       cmd.rxon_flags = priv->staging_rxon.flags;
+       cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
        cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
        if (ch_info)
                cmd.expect_beacon = is_channel_radar(ch_info);
@@ -1473,8 +1473,10 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
                return rc;
        }
 
-       rc = iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
-       return rc;
+       priv->switch_rxon.channel = cpu_to_le16(channel);
+       priv->switch_rxon.switch_in_progress = true;
+
+       return iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
 }
 
 /**
index 6ea5e2d..6eaf26b 100644 (file)
@@ -1391,8 +1391,8 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
                priv->active_rxon.channel, channel);
        cmd.band = priv->band == IEEE80211_BAND_2GHZ;
        cmd.channel = cpu_to_le16(channel);
-       cmd.rxon_flags = priv->active_rxon.flags;
-       cmd.rxon_filter_flags = priv->active_rxon.filter_flags;
+       cmd.rxon_flags = priv->staging_rxon.flags;
+       cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
        cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
        ch_info = iwl_get_channel_info(priv, priv->band, channel);
        if (ch_info)
@@ -1402,6 +1402,8 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
                        priv->active_rxon.channel, channel);
                return -EFAULT;
        }
+       priv->switch_rxon.channel = cpu_to_le16(channel);
+       priv->switch_rxon.switch_in_progress = true;
 
        return iwl_send_cmd_sync(priv, &hcmd);
 }
index 9ab7954..1f769ea 100644 (file)
@@ -186,8 +186,8 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
 
        cmd.band = priv->band == IEEE80211_BAND_2GHZ;
        cmd.channel = cpu_to_le16(channel);
-       cmd.rxon_flags = priv->active_rxon.flags;
-       cmd.rxon_filter_flags = priv->active_rxon.filter_flags;
+       cmd.rxon_flags = priv->staging_rxon.flags;
+       cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
        cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
        ch_info = iwl_get_channel_info(priv, priv->band, channel);
        if (ch_info)
@@ -197,6 +197,8 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
                        priv->active_rxon.channel, channel);
                return -EFAULT;
        }
+       priv->switch_rxon.channel = cpu_to_le16(channel);
+       priv->switch_rxon.switch_in_progress = true;
 
        return iwl_send_cmd_sync(priv, &hcmd);
 }
index 1710815..7bdedf6 100644 (file)
@@ -122,6 +122,17 @@ int iwl_commit_rxon(struct iwl_priv *priv)
                return -EINVAL;
        }
 
+       /*
+        * receive commit_rxon request
+        * abort any previous channel switch if still in process
+        */
+       if (priv->switch_rxon.switch_in_progress &&
+           (priv->switch_rxon.channel != priv->staging_rxon.channel)) {
+               IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
+                     le16_to_cpu(priv->switch_rxon.channel));
+               priv->switch_rxon.switch_in_progress = false;
+       }
+
        /* If we don't need to send a full RXON, we can use
         * iwl_rxon_assoc_cmd which is used to reconfigure filter
         * and other flags for the current radio configuration. */
index f88a1cb..e0edc60 100644 (file)
@@ -1316,14 +1316,19 @@ void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
        struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon;
        struct iwl_csa_notification *csa = &(pkt->u.csa_notif);
 
-       if (!le32_to_cpu(csa->status)) {
-               rxon->channel = csa->channel;
-               priv->staging_rxon.channel = csa->channel;
-               IWL_DEBUG_11H(priv, "CSA notif: channel %d\n",
-                     le16_to_cpu(csa->channel));
-       } else
-               IWL_ERR(priv, "CSA notif (fail) : channel %d\n",
-                     le16_to_cpu(csa->channel));
+       if (priv->switch_rxon.switch_in_progress) {
+               if (!le32_to_cpu(csa->status) &&
+                   (csa->channel == priv->switch_rxon.channel)) {
+                       rxon->channel = csa->channel;
+                       priv->staging_rxon.channel = csa->channel;
+                       IWL_DEBUG_11H(priv, "CSA notif: channel %d\n",
+                             le16_to_cpu(csa->channel));
+               } else
+                       IWL_ERR(priv, "CSA notif (fail) : channel %d\n",
+                             le16_to_cpu(csa->channel));
+
+               priv->switch_rxon.switch_in_progress = false;
+       }
 }
 EXPORT_SYMBOL(iwl_rx_csa);
 
@@ -2690,14 +2695,6 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                        goto set_ch_out;
                }
 
-               if (iwl_is_associated(priv) &&
-                   (le16_to_cpu(priv->active_rxon.channel) != ch) &&
-                   priv->cfg->ops->lib->set_channel_switch) {
-                       ret = priv->cfg->ops->lib->set_channel_switch(priv,
-                               ch);
-                       goto out;
-               }
-
                spin_lock_irqsave(&priv->lock, flags);
 
                /* Configure HT40 channels */
@@ -2732,6 +2729,22 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
 
                iwl_set_flags_for_band(priv, conf->channel->band);
                spin_unlock_irqrestore(&priv->lock, flags);
+               if (iwl_is_associated(priv) &&
+                   (le16_to_cpu(priv->active_rxon.channel) != ch) &&
+                   priv->cfg->ops->lib->set_channel_switch) {
+                       iwl_set_rate(priv);
+                       /*
+                        * at this point, staging_rxon has the
+                        * configuration for channel switch
+                        */
+                       ret = priv->cfg->ops->lib->set_channel_switch(priv,
+                               ch);
+                       if (!ret) {
+                               iwl_print_rx_config_cmd(priv);
+                               goto out;
+                       }
+                       priv->switch_rxon.switch_in_progress = false;
+               }
  set_ch_out:
                /* The list of supported rates and rate mask can be different
                 * for each band; since the band may have changed, reset
index 9975645..9a19a3d 100644 (file)
@@ -994,6 +994,17 @@ struct traffic_stats {
 };
 #endif
 
+/*
+ * iwl_switch_rxon: "channel switch" structure
+ *
+ * @ switch_in_progress: channel switch in progress
+ * @ channel: new channel
+ */
+struct iwl_switch_rxon {
+       bool switch_in_progress;
+       __le16 channel;
+};
+
 struct iwl_priv {
 
        /* ieee device used by generic ieee processing code */
@@ -1087,6 +1098,8 @@ struct iwl_priv {
        const struct iwl_rxon_cmd active_rxon;
        struct iwl_rxon_cmd staging_rxon;
 
+       struct iwl_switch_rxon switch_rxon;
+
        /* 1st responses from initialize and runtime uCode images.
         * 4965's initialize alive response contains some calibration data. */
        struct iwl_init_alive_resp card_alive_init;