Commit a2b0f02e authored by Wey-Yi Guy's avatar Wey-Yi Guy Committed by John W. Linville

iwlwifi: support "pure 40MHz" in RXON command

Fix the bug when using 11n "pure 40MHz" mode cause uCode
crashing by adding support for "pure 40MHz" in RX_ON command flag.
the "mode" field (bits 25:26) has value of 0-3
    0 = 20 MHz only
    1 = 40MHz only
    2 = Mixed
    3 = Reserved
Control Channel ID (bit 22) is valid only in Mixed mode.
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 a9c146b3
...@@ -293,10 +293,12 @@ static void iwl4965_init_alive_start(struct iwl_priv *priv) ...@@ -293,10 +293,12 @@ static void iwl4965_init_alive_start(struct iwl_priv *priv)
queue_work(priv->workqueue, &priv->restart); queue_work(priv->workqueue, &priv->restart);
} }
static int is_fat_channel(__le32 rxon_flags) static bool is_fat_channel(__le32 rxon_flags)
{ {
return (rxon_flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) || int chan_mod = le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK)
(rxon_flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK); >> RXON_FLG_CHANNEL_MODE_POS;
return ((chan_mod == CHANNEL_MODE_PURE_40) ||
(chan_mod == CHANNEL_MODE_MIXED));
} }
/* /*
...@@ -1490,7 +1492,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) ...@@ -1490,7 +1492,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv)
struct iwl4965_txpowertable_cmd cmd = { 0 }; struct iwl4965_txpowertable_cmd cmd = { 0 };
int ret; int ret;
u8 band = 0; u8 band = 0;
u8 is_fat = 0; bool is_fat = false;
u8 ctrl_chan_high = 0; u8 ctrl_chan_high = 0;
if (test_bit(STATUS_SCANNING, &priv->status)) { if (test_bit(STATUS_SCANNING, &priv->status)) {
...@@ -1568,7 +1570,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) ...@@ -1568,7 +1570,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
{ {
int rc; int rc;
u8 band = 0; u8 band = 0;
u8 is_fat = 0; bool is_fat = false;
u8 ctrl_chan_high = 0; u8 ctrl_chan_high = 0;
struct iwl4965_channel_switch_cmd cmd = { 0 }; struct iwl4965_channel_switch_cmd cmd = { 0 };
const struct iwl_channel_info *ch_info; const struct iwl_channel_info *ch_info;
......
...@@ -614,8 +614,18 @@ enum { ...@@ -614,8 +614,18 @@ enum {
#define RXON_FLG_CHANNEL_MODE_POS (25) #define RXON_FLG_CHANNEL_MODE_POS (25)
#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) #define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25)
#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK cpu_to_le32(0x1 << 25)
#define RXON_FLG_CHANNEL_MODE_MIXED_MSK cpu_to_le32(0x2 << 25) /* channel mode */
enum {
CHANNEL_MODE_LEGACY = 0,
CHANNEL_MODE_PURE_40 = 1,
CHANNEL_MODE_MIXED = 2,
CHANNEL_MODE_RESERVED = 3,
};
#define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS)
#define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS)
#define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS)
/* CTS to self (if spec allows) flag */ /* CTS to self (if spec allows) flag */
#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) #define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30)
......
...@@ -617,19 +617,23 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, ...@@ -617,19 +617,23 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv,
struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config; struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config;
if ((!iwl_ht_conf->is_ht) || if ((!iwl_ht_conf->is_ht) ||
(iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) || (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ))
(iwl_ht_conf->extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE))
return 0; return 0;
/* We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
* the bit will not set if it is pure 40MHz case
*/
if (sta_ht_inf) { if (sta_ht_inf) {
if ((!sta_ht_inf->ht_supported) || if (!sta_ht_inf->ht_supported)
(!(sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)))
return 0; return 0;
} }
return iwl_is_channel_extension(priv, priv->band, if (iwl_ht_conf->ht_protection & IEEE80211_HT_OP_MODE_PROTECTION_20MHZ)
le16_to_cpu(priv->staging_rxon.channel), return 1;
iwl_ht_conf->extension_chan_offset); else
return iwl_is_channel_extension(priv, priv->band,
le16_to_cpu(priv->staging_rxon.channel),
iwl_ht_conf->extension_chan_offset);
} }
EXPORT_SYMBOL(iwl_is_fat_tx_allowed); EXPORT_SYMBOL(iwl_is_fat_tx_allowed);
...@@ -799,42 +803,51 @@ EXPORT_SYMBOL(iwl_rate_get_lowest_plcp); ...@@ -799,42 +803,51 @@ EXPORT_SYMBOL(iwl_rate_get_lowest_plcp);
void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
{ {
struct iwl_rxon_cmd *rxon = &priv->staging_rxon; struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
u32 val;
if (!ht_info->is_ht) { if (!ht_info->is_ht) {
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK | rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
RXON_FLG_CHANNEL_MODE_PURE_40_MSK |
RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
RXON_FLG_FAT_PROT_MSK | RXON_FLG_FAT_PROT_MSK |
RXON_FLG_HT_PROT_MSK); RXON_FLG_HT_PROT_MSK);
return; return;
} }
/* Set up channel bandwidth: 20 MHz only, or 20/40 mixed if fat ok */ /* FIXME: if the definition of ht_protection changed, the "translation"
if (iwl_is_fat_tx_allowed(priv, NULL)) * will be needed for rxon->flags
rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED_MSK; */
else rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS);
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
RXON_FLG_CHANNEL_MODE_PURE_40_MSK); /* Set up channel bandwidth:
* 20 MHz only, 20/40 mixed or pure 40 if fat ok */
/* Note: control channel is opposite of extension channel */ /* clear the HT channel mode before set the mode */
switch (ht_info->extension_chan_offset) { rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); if (iwl_is_fat_tx_allowed(priv, NULL)) {
break; /* pure 40 fat */
case IEEE80211_HT_PARAM_CHA_SEC_BELOW: if (rxon->flags & RXON_FLG_FAT_PROT_MSK)
rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
break; else {
case IEEE80211_HT_PARAM_CHA_SEC_NONE: /* Note: control channel is opposite of extension channel */
default: switch (ht_info->extension_chan_offset) {
rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK; case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
break; rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
break;
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
default:
/* channel location only valid if in Mixed mode */
IWL_ERR(priv, "invalid extension channel offset\n");
break;
}
}
} else {
rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
} }
val = ht_info->ht_protection;
rxon->flags |= cpu_to_le32(val << RXON_FLG_HT_OPERATING_MODE_POS);
if (priv->cfg->ops->hcmd->set_rxon_chain) if (priv->cfg->ops->hcmd->set_rxon_chain)
priv->cfg->ops->hcmd->set_rxon_chain(priv); priv->cfg->ops->hcmd->set_rxon_chain(priv);
...@@ -1122,8 +1135,9 @@ void iwl_connection_init_rx_config(struct iwl_priv *priv, int mode) ...@@ -1122,8 +1135,9 @@ void iwl_connection_init_rx_config(struct iwl_priv *priv, int mode)
priv->staging_rxon.cck_basic_rates = priv->staging_rxon.cck_basic_rates =
(IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF;
priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK | /* clear both MIX and PURE40 mode flag */
RXON_FLG_CHANNEL_MODE_PURE_40_MSK); priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED |
RXON_FLG_CHANNEL_MODE_PURE_40);
memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
memcpy(priv->staging_rxon.wlap_bssid_addr, priv->mac_addr, ETH_ALEN); memcpy(priv->staging_rxon.wlap_bssid_addr, priv->mac_addr, ETH_ALEN);
priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff; priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff;
......
...@@ -586,6 +586,7 @@ static void iwl_bg_request_scan(struct work_struct *data) ...@@ -586,6 +586,7 @@ static void iwl_bg_request_scan(struct work_struct *data)
u8 rx_ant = priv->hw_params.valid_rx_ant; u8 rx_ant = priv->hw_params.valid_rx_ant;
u8 rate; u8 rate;
bool is_active = false; bool is_active = false;
int chan_mod;
conf = ieee80211_get_hw_conf(priv->hw); conf = ieee80211_get_hw_conf(priv->hw);
...@@ -703,7 +704,9 @@ static void iwl_bg_request_scan(struct work_struct *data) ...@@ -703,7 +704,9 @@ static void iwl_bg_request_scan(struct work_struct *data)
if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) { if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
band = IEEE80211_BAND_2GHZ; band = IEEE80211_BAND_2GHZ;
scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) { chan_mod = le32_to_cpu(priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_MSK)
>> RXON_FLG_CHANNEL_MODE_POS;
if (chan_mod == CHANNEL_MODE_PURE_40) {
rate = IWL_RATE_6M_PLCP; rate = IWL_RATE_6M_PLCP;
} else { } else {
rate = IWL_RATE_1M_PLCP; rate = IWL_RATE_1M_PLCP;
......
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