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: 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>
This commit is contained in:
parent
681988653e
commit
0924e519a3
6 changed files with 68 additions and 25 deletions
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue