Commit 0924e519 authored by Wey-Yi Guy's avatar Wey-Yi Guy Committed by John W. Linville
Browse files

iwlwifi: fix for channel switch



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: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: default avatarReinette Chatre <reinette.chatre@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 68198865
......@@ -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);
}
/**
......
......@@ -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);
}
......
......@@ -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);
}
......
......@@ -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. */
......
......@@ -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
......
......@@ -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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment