aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-03-25 13:29:27 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-04-16 09:29:44 -0400
commit85220d71bf3ca1ba9129e0744247ae5f61bec559 (patch)
tree5bdabc71bc5da27ee71fb1272b86809ac2f95b0d
parentb4f286a1c0ad0b84c2d502b354d4d98d5a86c64b (diff)
mac80211: support secondary channel offset in CSA
Add support for the secondary channel offset IE in channel switch announcements. This is necessary for proper handling of CSA on HT access points. For this to work it is also necessary to convert everything here to use chandef structs instead of just channels. The driver updates aren't really correct though. In particular, the TI wl18xx driver update can't possibly be right since it just ignores the new channel width for lack of firmware API. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c32
-rw-r--r--drivers/net/wireless/iwlegacy/4965.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/devices.c10
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c20
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rxon.c2
-rw-r--r--drivers/net/wireless/ti/wl12xx/cmd.c2
-rw-r--r--drivers/net/wireless/ti/wl18xx/cmd.c6
-rw-r--r--include/linux/ieee80211.h11
-rw-r--r--include/net/mac80211.h4
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/mlme.c71
-rw-r--r--net/mac80211/trace.h8
-rw-r--r--net/mac80211/util.c8
13 files changed, 125 insertions, 54 deletions
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index c092fcbbe965..cb5882ea5f3a 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -6057,7 +6057,7 @@ il4965_mac_channel_switch(struct ieee80211_hw *hw,
6057 struct il_priv *il = hw->priv; 6057 struct il_priv *il = hw->priv;
6058 const struct il_channel_info *ch_info; 6058 const struct il_channel_info *ch_info;
6059 struct ieee80211_conf *conf = &hw->conf; 6059 struct ieee80211_conf *conf = &hw->conf;
6060 struct ieee80211_channel *channel = ch_switch->channel; 6060 struct ieee80211_channel *channel = ch_switch->chandef.chan;
6061 struct il_ht_config *ht_conf = &il->current_ht_config; 6061 struct il_ht_config *ht_conf = &il->current_ht_config;
6062 u16 ch; 6062 u16 ch;
6063 6063
@@ -6094,23 +6094,21 @@ il4965_mac_channel_switch(struct ieee80211_hw *hw,
6094 il->current_ht_config.smps = conf->smps_mode; 6094 il->current_ht_config.smps = conf->smps_mode;
6095 6095
6096 /* Configure HT40 channels */ 6096 /* Configure HT40 channels */
6097 il->ht.enabled = conf_is_ht(conf); 6097 switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
6098 if (il->ht.enabled) { 6098 case NL80211_CHAN_NO_HT:
6099 if (conf_is_ht40_minus(conf)) { 6099 case NL80211_CHAN_HT20:
6100 il->ht.extension_chan_offset =
6101 IEEE80211_HT_PARAM_CHA_SEC_BELOW;
6102 il->ht.is_40mhz = true;
6103 } else if (conf_is_ht40_plus(conf)) {
6104 il->ht.extension_chan_offset =
6105 IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
6106 il->ht.is_40mhz = true;
6107 } else {
6108 il->ht.extension_chan_offset =
6109 IEEE80211_HT_PARAM_CHA_SEC_NONE;
6110 il->ht.is_40mhz = false;
6111 }
6112 } else
6113 il->ht.is_40mhz = false; 6100 il->ht.is_40mhz = false;
6101 il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
6102 break;
6103 case NL80211_CHAN_HT40MINUS:
6104 il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
6105 il->ht.is_40mhz = true;
6106 break;
6107 case NL80211_CHAN_HT40PLUS:
6108 il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
6109 il->ht.is_40mhz = true;
6110 break;
6111 }
6114 6112
6115 if ((le16_to_cpu(il->staging.channel) != ch)) 6113 if ((le16_to_cpu(il->staging.channel) != ch))
6116 il->staging.flags = 0; 6114 il->staging.flags = 0;
diff --git a/drivers/net/wireless/iwlegacy/4965.c b/drivers/net/wireless/iwlegacy/4965.c
index 91eb2d07fdb8..777a578294bd 100644
--- a/drivers/net/wireless/iwlegacy/4965.c
+++ b/drivers/net/wireless/iwlegacy/4965.c
@@ -1493,7 +1493,7 @@ il4965_hw_channel_switch(struct il_priv *il,
1493 1493
1494 cmd.band = band; 1494 cmd.band = band;
1495 cmd.expect_beacon = 0; 1495 cmd.expect_beacon = 0;
1496 ch = ch_switch->channel->hw_value; 1496 ch = ch_switch->chandef.chan->hw_value;
1497 cmd.channel = cpu_to_le16(ch); 1497 cmd.channel = cpu_to_le16(ch);
1498 cmd.rxon_flags = il->staging.flags; 1498 cmd.rxon_flags = il->staging.flags;
1499 cmd.rxon_filter_flags = il->staging.filter_flags; 1499 cmd.rxon_filter_flags = il->staging.filter_flags;
diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c
index 15cca2ef9294..c48907c8ab43 100644
--- a/drivers/net/wireless/iwlwifi/dvm/devices.c
+++ b/drivers/net/wireless/iwlwifi/dvm/devices.c
@@ -379,7 +379,7 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
379 }; 379 };
380 380
381 cmd.band = priv->band == IEEE80211_BAND_2GHZ; 381 cmd.band = priv->band == IEEE80211_BAND_2GHZ;
382 ch = ch_switch->channel->hw_value; 382 ch = ch_switch->chandef.chan->hw_value;
383 IWL_DEBUG_11H(priv, "channel switch from %d to %d\n", 383 IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
384 ctx->active.channel, ch); 384 ctx->active.channel, ch);
385 cmd.channel = cpu_to_le16(ch); 385 cmd.channel = cpu_to_le16(ch);
@@ -414,7 +414,8 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
414 } 414 }
415 IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", 415 IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
416 cmd.switch_time); 416 cmd.switch_time);
417 cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; 417 cmd.expect_beacon =
418 ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR;
418 419
419 return iwl_dvm_send_cmd(priv, &hcmd); 420 return iwl_dvm_send_cmd(priv, &hcmd);
420} 421}
@@ -540,7 +541,7 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
540 hcmd.data[0] = cmd; 541 hcmd.data[0] = cmd;
541 542
542 cmd->band = priv->band == IEEE80211_BAND_2GHZ; 543 cmd->band = priv->band == IEEE80211_BAND_2GHZ;
543 ch = ch_switch->channel->hw_value; 544 ch = ch_switch->chandef.chan->hw_value;
544 IWL_DEBUG_11H(priv, "channel switch from %u to %u\n", 545 IWL_DEBUG_11H(priv, "channel switch from %u to %u\n",
545 ctx->active.channel, ch); 546 ctx->active.channel, ch);
546 cmd->channel = cpu_to_le16(ch); 547 cmd->channel = cpu_to_le16(ch);
@@ -575,7 +576,8 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
575 } 576 }
576 IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", 577 IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n",
577 cmd->switch_time); 578 cmd->switch_time);
578 cmd->expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; 579 cmd->expect_beacon =
580 ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR;
579 581
580 err = iwl_dvm_send_cmd(priv, &hcmd); 582 err = iwl_dvm_send_cmd(priv, &hcmd);
581 kfree(cmd); 583 kfree(cmd);
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index a7294fa4d7e5..2dc101fe0d24 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -967,7 +967,7 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
967{ 967{
968 struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 968 struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
969 struct ieee80211_conf *conf = &hw->conf; 969 struct ieee80211_conf *conf = &hw->conf;
970 struct ieee80211_channel *channel = ch_switch->channel; 970 struct ieee80211_channel *channel = ch_switch->chandef.chan;
971 struct iwl_ht_config *ht_conf = &priv->current_ht_config; 971 struct iwl_ht_config *ht_conf = &priv->current_ht_config;
972 /* 972 /*
973 * MULTI-FIXME 973 * MULTI-FIXME
@@ -1005,11 +1005,21 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
1005 priv->current_ht_config.smps = conf->smps_mode; 1005 priv->current_ht_config.smps = conf->smps_mode;
1006 1006
1007 /* Configure HT40 channels */ 1007 /* Configure HT40 channels */
1008 ctx->ht.enabled = conf_is_ht(conf); 1008 switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
1009 if (ctx->ht.enabled) 1009 case NL80211_CHAN_NO_HT:
1010 iwlagn_config_ht40(conf, ctx); 1010 case NL80211_CHAN_HT20:
1011 else
1012 ctx->ht.is_40mhz = false; 1011 ctx->ht.is_40mhz = false;
1012 ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
1013 break;
1014 case NL80211_CHAN_HT40MINUS:
1015 ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
1016 ctx->ht.is_40mhz = true;
1017 break;
1018 case NL80211_CHAN_HT40PLUS:
1019 ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
1020 ctx->ht.is_40mhz = true;
1021 break;
1022 }
1013 1023
1014 if ((le16_to_cpu(ctx->staging.channel) != ch)) 1024 if ((le16_to_cpu(ctx->staging.channel) != ch))
1015 ctx->staging.flags = 0; 1025 ctx->staging.flags = 0;
diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c
index 085c589e7149..acbb50b5f1e8 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rxon.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c
@@ -1160,7 +1160,7 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
1160} 1160}
1161 1161
1162void iwlagn_config_ht40(struct ieee80211_conf *conf, 1162void iwlagn_config_ht40(struct ieee80211_conf *conf,
1163 struct iwl_rxon_context *ctx) 1163 struct iwl_rxon_context *ctx)
1164{ 1164{
1165 if (conf_is_ht40_minus(conf)) { 1165 if (conf_is_ht40_minus(conf)) {
1166 ctx->ht.extension_chan_offset = 1166 ctx->ht.extension_chan_offset =
diff --git a/drivers/net/wireless/ti/wl12xx/cmd.c b/drivers/net/wireless/ti/wl12xx/cmd.c
index 7dc9f965037d..7485dbae8c4b 100644
--- a/drivers/net/wireless/ti/wl12xx/cmd.c
+++ b/drivers/net/wireless/ti/wl12xx/cmd.c
@@ -301,7 +301,7 @@ int wl12xx_cmd_channel_switch(struct wl1271 *wl,
301 } 301 }
302 302
303 cmd->role_id = wlvif->role_id; 303 cmd->role_id = wlvif->role_id;
304 cmd->channel = ch_switch->channel->hw_value; 304 cmd->channel = ch_switch->chandef.chan->hw_value;
305 cmd->switch_time = ch_switch->count; 305 cmd->switch_time = ch_switch->count;
306 cmd->stop_tx = ch_switch->block_tx; 306 cmd->stop_tx = ch_switch->block_tx;
307 307
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c
index 1d1f6cc7a50a..7649c75cd68d 100644
--- a/drivers/net/wireless/ti/wl18xx/cmd.c
+++ b/drivers/net/wireless/ti/wl18xx/cmd.c
@@ -42,11 +42,11 @@ int wl18xx_cmd_channel_switch(struct wl1271 *wl,
42 } 42 }
43 43
44 cmd->role_id = wlvif->role_id; 44 cmd->role_id = wlvif->role_id;
45 cmd->channel = ch_switch->channel->hw_value; 45 cmd->channel = ch_switch->chandef.chan->hw_value;
46 cmd->switch_time = ch_switch->count; 46 cmd->switch_time = ch_switch->count;
47 cmd->stop_tx = ch_switch->block_tx; 47 cmd->stop_tx = ch_switch->block_tx;
48 48
49 switch (ch_switch->channel->band) { 49 switch (ch_switch->chandef.chan->band) {
50 case IEEE80211_BAND_2GHZ: 50 case IEEE80211_BAND_2GHZ:
51 cmd->band = WLCORE_BAND_2_4GHZ; 51 cmd->band = WLCORE_BAND_2_4GHZ;
52 break; 52 break;
@@ -55,7 +55,7 @@ int wl18xx_cmd_channel_switch(struct wl1271 *wl,
55 break; 55 break;
56 default: 56 default:
57 wl1271_error("invalid channel switch band: %d", 57 wl1271_error("invalid channel switch band: %d",
58 ch_switch->channel->band); 58 ch_switch->chandef.chan->band);
59 ret = -EINVAL; 59 ret = -EINVAL;
60 goto out_free; 60 goto out_free;
61 } 61 }
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 2a10acc65a54..95621528436c 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -685,6 +685,16 @@ struct ieee80211_ext_chansw_ie {
685} __packed; 685} __packed;
686 686
687/** 687/**
688 * struct ieee80211_sec_chan_offs_ie - secondary channel offset IE
689 * @sec_chan_offs: secondary channel offset, uses IEEE80211_HT_PARAM_CHA_SEC_*
690 * values here
691 * This structure represents the "Secondary Channel Offset element"
692 */
693struct ieee80211_sec_chan_offs_ie {
694 u8 sec_chan_offs;
695} __packed;
696
697/**
688 * struct ieee80211_tim 698 * struct ieee80211_tim
689 * 699 *
690 * This structure refers to "Traffic Indication Map information element" 700 * This structure refers to "Traffic Indication Map information element"
@@ -1648,6 +1658,7 @@ enum ieee80211_eid {
1648 1658
1649 WLAN_EID_HT_CAPABILITY = 45, 1659 WLAN_EID_HT_CAPABILITY = 45,
1650 WLAN_EID_HT_OPERATION = 61, 1660 WLAN_EID_HT_OPERATION = 61,
1661 WLAN_EID_SECONDARY_CHANNEL_OFFSET = 62,
1651 1662
1652 WLAN_EID_RSN = 48, 1663 WLAN_EID_RSN = 48,
1653 WLAN_EID_MMIE = 76, 1664 WLAN_EID_MMIE = 76,
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 0dde213dd3b6..9ff10b33b711 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1017,13 +1017,13 @@ struct ieee80211_conf {
1017 * the driver passed into mac80211. 1017 * the driver passed into mac80211.
1018 * @block_tx: Indicates whether transmission must be blocked before the 1018 * @block_tx: Indicates whether transmission must be blocked before the
1019 * scheduled channel switch, as indicated by the AP. 1019 * scheduled channel switch, as indicated by the AP.
1020 * @channel: the new channel to switch to 1020 * @chandef: the new channel to switch to
1021 * @count: the number of TBTT's until the channel switch event 1021 * @count: the number of TBTT's until the channel switch event
1022 */ 1022 */
1023struct ieee80211_channel_switch { 1023struct ieee80211_channel_switch {
1024 u64 timestamp; 1024 u64 timestamp;
1025 bool block_tx; 1025 bool block_tx;
1026 struct ieee80211_channel *channel; 1026 struct cfg80211_chan_def chandef;
1027 u8 count; 1027 u8 count;
1028}; 1028};
1029 1029
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 10c3180b165e..8f240c0ec304 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1019,7 +1019,7 @@ struct ieee80211_local {
1019 enum mac80211_scan_state next_scan_state; 1019 enum mac80211_scan_state next_scan_state;
1020 struct delayed_work scan_work; 1020 struct delayed_work scan_work;
1021 struct ieee80211_sub_if_data __rcu *scan_sdata; 1021 struct ieee80211_sub_if_data __rcu *scan_sdata;
1022 struct ieee80211_channel *csa_channel; 1022 struct cfg80211_chan_def csa_chandef;
1023 /* For backward compatibility only -- do not use */ 1023 /* For backward compatibility only -- do not use */
1024 struct cfg80211_chan_def _oper_chandef; 1024 struct cfg80211_chan_def _oper_chandef;
1025 1025
@@ -1183,6 +1183,7 @@ struct ieee802_11_elems {
1183 const u8 *pwr_constr_elem; 1183 const u8 *pwr_constr_elem;
1184 const struct ieee80211_timeout_interval_ie *timeout_int; 1184 const struct ieee80211_timeout_interval_ie *timeout_int;
1185 const u8 *opmode_notif; 1185 const u8 *opmode_notif;
1186 const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
1186 1187
1187 /* length of them, respectively */ 1188 /* length of them, respectively */
1188 u8 ssid_len; 1189 u8 ssid_len;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index bc6f87edc624..bd581a80e4b7 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -289,6 +289,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
289 } else { 289 } else {
290 /* 40 MHz (and 80 MHz) must be supported for VHT */ 290 /* 40 MHz (and 80 MHz) must be supported for VHT */
291 ret = IEEE80211_STA_DISABLE_VHT; 291 ret = IEEE80211_STA_DISABLE_VHT;
292 /* also mark 40 MHz disabled */
293 ret |= IEEE80211_STA_DISABLE_40MHZ;
292 goto out; 294 goto out;
293 } 295 }
294 296
@@ -964,16 +966,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
964 if (!ifmgd->associated) 966 if (!ifmgd->associated)
965 goto out; 967 goto out;
966 968
967 /* 969 local->_oper_chandef = local->csa_chandef;
968 * FIXME: Here we are downgrading to NL80211_CHAN_WIDTH_20_NOHT
969 * and don't adjust our ht/vht settings
970 * This is wrong - we should behave according to the CSA params
971 */
972 local->_oper_chandef.chan = local->csa_channel;
973 local->_oper_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
974 local->_oper_chandef.center_freq1 =
975 local->_oper_chandef.chan->center_freq;
976 local->_oper_chandef.center_freq2 = 0;
977 970
978 if (!local->ops->channel_switch) { 971 if (!local->ops->channel_switch) {
979 /* call "hw_config" only if doing sw channel switch */ 972 /* call "hw_config" only if doing sw channel switch */
@@ -1028,13 +1021,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1028 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 1021 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
1029 struct cfg80211_bss *cbss = ifmgd->associated; 1022 struct cfg80211_bss *cbss = ifmgd->associated;
1030 struct ieee80211_bss *bss; 1023 struct ieee80211_bss *bss;
1031 struct ieee80211_channel *new_ch;
1032 struct ieee80211_chanctx *chanctx; 1024 struct ieee80211_chanctx *chanctx;
1033 enum ieee80211_band new_band; 1025 enum ieee80211_band new_band;
1034 int new_freq; 1026 int new_freq;
1035 u8 new_chan_no; 1027 u8 new_chan_no;
1036 u8 count; 1028 u8 count;
1037 u8 mode; 1029 u8 mode;
1030 struct cfg80211_chan_def new_chandef = {};
1031 int secondary_channel_offset = -1;
1038 1032
1039 ASSERT_MGD_MTX(ifmgd); 1033 ASSERT_MGD_MTX(ifmgd);
1040 1034
@@ -1048,6 +1042,19 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1048 if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) 1042 if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
1049 return; 1043 return;
1050 1044
1045 if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
1046 /* if HT is enabled and the IE not present, it's still HT */
1047 secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
1048 if (elems->sec_chan_offs)
1049 secondary_channel_offset =
1050 elems->sec_chan_offs->sec_chan_offs;
1051 }
1052
1053 if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
1054 (secondary_channel_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE ||
1055 secondary_channel_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW))
1056 secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
1057
1051 if (elems->ext_chansw_ie) { 1058 if (elems->ext_chansw_ie) {
1052 if (!ieee80211_operating_class_to_band( 1059 if (!ieee80211_operating_class_to_band(
1053 elems->ext_chansw_ie->new_operating_class, 1060 elems->ext_chansw_ie->new_operating_class,
@@ -1074,8 +1081,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1074 bss = (void *)cbss->priv; 1081 bss = (void *)cbss->priv;
1075 1082
1076 new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); 1083 new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
1077 new_ch = ieee80211_get_channel(local->hw.wiphy, new_freq); 1084 new_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
1078 if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { 1085 if (!new_chandef.chan ||
1086 new_chandef.chan->flags & IEEE80211_CHAN_DISABLED) {
1079 sdata_info(sdata, 1087 sdata_info(sdata,
1080 "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", 1088 "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
1081 ifmgd->associated->bssid, new_freq); 1089 ifmgd->associated->bssid, new_freq);
@@ -1084,6 +1092,39 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1084 return; 1092 return;
1085 } 1093 }
1086 1094
1095 switch (secondary_channel_offset) {
1096 default:
1097 /* secondary_channel_offset was present but is invalid */
1098 case IEEE80211_HT_PARAM_CHA_SEC_NONE:
1099 cfg80211_chandef_create(&new_chandef, new_chandef.chan,
1100 NL80211_CHAN_HT20);
1101 break;
1102 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
1103 cfg80211_chandef_create(&new_chandef, new_chandef.chan,
1104 NL80211_CHAN_HT40PLUS);
1105 break;
1106 case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
1107 cfg80211_chandef_create(&new_chandef, new_chandef.chan,
1108 NL80211_CHAN_HT40MINUS);
1109 break;
1110 case -1:
1111 cfg80211_chandef_create(&new_chandef, new_chandef.chan,
1112 NL80211_CHAN_NO_HT);
1113 break;
1114 }
1115
1116 if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
1117 IEEE80211_CHAN_DISABLED)) {
1118 sdata_info(sdata,
1119 "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
1120 ifmgd->associated->bssid, new_freq,
1121 new_chandef.width, new_chandef.center_freq1,
1122 new_chandef.center_freq2);
1123 ieee80211_queue_work(&local->hw,
1124 &ifmgd->csa_connection_drop_work);
1125 return;
1126 }
1127
1087 ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; 1128 ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
1088 1129
1089 if (local->use_chanctx) { 1130 if (local->use_chanctx) {
@@ -1111,7 +1152,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1111 } 1152 }
1112 mutex_unlock(&local->chanctx_mtx); 1153 mutex_unlock(&local->chanctx_mtx);
1113 1154
1114 local->csa_channel = new_ch; 1155 local->csa_chandef = new_chandef;
1115 1156
1116 if (mode) 1157 if (mode)
1117 ieee80211_stop_queues_by_reason(&local->hw, 1158 ieee80211_stop_queues_by_reason(&local->hw,
@@ -1123,7 +1164,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1123 struct ieee80211_channel_switch ch_switch = { 1164 struct ieee80211_channel_switch ch_switch = {
1124 .timestamp = timestamp, 1165 .timestamp = timestamp,
1125 .block_tx = mode, 1166 .block_tx = mode,
1126 .channel = new_ch, 1167 .chandef = new_chandef,
1127 .count = count, 1168 .count = count,
1128 }; 1169 };
1129 1170
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 8286dcef228b..c215fafd7a2f 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -990,23 +990,23 @@ TRACE_EVENT(drv_channel_switch,
990 990
991 TP_STRUCT__entry( 991 TP_STRUCT__entry(
992 LOCAL_ENTRY 992 LOCAL_ENTRY
993 CHANDEF_ENTRY
993 __field(u64, timestamp) 994 __field(u64, timestamp)
994 __field(bool, block_tx) 995 __field(bool, block_tx)
995 __field(u16, freq)
996 __field(u8, count) 996 __field(u8, count)
997 ), 997 ),
998 998
999 TP_fast_assign( 999 TP_fast_assign(
1000 LOCAL_ASSIGN; 1000 LOCAL_ASSIGN;
1001 CHANDEF_ASSIGN(&ch_switch->chandef)
1001 __entry->timestamp = ch_switch->timestamp; 1002 __entry->timestamp = ch_switch->timestamp;
1002 __entry->block_tx = ch_switch->block_tx; 1003 __entry->block_tx = ch_switch->block_tx;
1003 __entry->freq = ch_switch->channel->center_freq;
1004 __entry->count = ch_switch->count; 1004 __entry->count = ch_switch->count;
1005 ), 1005 ),
1006 1006
1007 TP_printk( 1007 TP_printk(
1008 LOCAL_PR_FMT " new freq:%u count:%d", 1008 LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
1009 LOCAL_PR_ARG, __entry->freq, __entry->count 1009 LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count
1010 ) 1010 )
1011); 1011);
1012 1012
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index e4a6d559372d..155056c90edf 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -716,6 +716,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
716 case WLAN_EID_COUNTRY: 716 case WLAN_EID_COUNTRY:
717 case WLAN_EID_PWR_CONSTRAINT: 717 case WLAN_EID_PWR_CONSTRAINT:
718 case WLAN_EID_TIMEOUT_INTERVAL: 718 case WLAN_EID_TIMEOUT_INTERVAL:
719 case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
719 if (test_bit(id, seen_elems)) { 720 if (test_bit(id, seen_elems)) {
720 elems->parse_error = true; 721 elems->parse_error = true;
721 left -= elen; 722 left -= elen;
@@ -870,6 +871,13 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
870 } 871 }
871 elems->ext_chansw_ie = (void *)pos; 872 elems->ext_chansw_ie = (void *)pos;
872 break; 873 break;
874 case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
875 if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) {
876 elem_parse_failed = true;
877 break;
878 }
879 elems->sec_chan_offs = (void *)pos;
880 break;
873 case WLAN_EID_COUNTRY: 881 case WLAN_EID_COUNTRY:
874 elems->country_elem = pos; 882 elems->country_elem = pos;
875 elems->country_elem_len = elen; 883 elems->country_elem_len = elen;