aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-05-03 03:35:35 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-05-16 16:39:40 -0400
commit04a161f4609dfa387313456fa7ea469fff12cc0d (patch)
tree5a2544eb41a476637efeb8a8e71c90013d572a2c
parent03f831a6f7dc02303df20718a4e96821e43b3d0c (diff)
mac80211: fix HT beacon-based channel switch handling
When an HT AP is advertising channel switch in a beacon, it doesn't (and shouldn't, according to 802.11-2012 Table 8-20) include a secondary channel offset element. The only possible interpretation is that the previous secondary channel offset remains valid, so use that when switching channel based only on beacon information. VHT requires the Wide Bandwidth Channel Switch subelement to be present in the Channel Switch Wrapper element, so the code for that is probably ok (see 802.11ac Draft 4, 8.4.2.165.) Reported-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/mac80211/mlme.c28
1 files changed, 22 insertions, 6 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 29620bfc7a69..a8016c02a757 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1015,7 +1015,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
1015 1015
1016static void 1016static void
1017ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, 1017ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1018 u64 timestamp, struct ieee802_11_elems *elems) 1018 u64 timestamp, struct ieee802_11_elems *elems,
1019 bool beacon)
1019{ 1020{
1020 struct ieee80211_local *local = sdata->local; 1021 struct ieee80211_local *local = sdata->local;
1021 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 1022 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1032,6 +1033,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1032 struct cfg80211_chan_def new_vht_chandef = {}; 1033 struct cfg80211_chan_def new_vht_chandef = {};
1033 const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; 1034 const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
1034 const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; 1035 const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
1036 const struct ieee80211_ht_operation *ht_oper;
1035 int secondary_channel_offset = -1; 1037 int secondary_channel_offset = -1;
1036 1038
1037 ASSERT_MGD_MTX(ifmgd); 1039 ASSERT_MGD_MTX(ifmgd);
@@ -1048,11 +1050,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1048 1050
1049 sec_chan_offs = elems->sec_chan_offs; 1051 sec_chan_offs = elems->sec_chan_offs;
1050 wide_bw_chansw_ie = elems->wide_bw_chansw_ie; 1052 wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
1053 ht_oper = elems->ht_operation;
1051 1054
1052 if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | 1055 if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
1053 IEEE80211_STA_DISABLE_40MHZ)) { 1056 IEEE80211_STA_DISABLE_40MHZ)) {
1054 sec_chan_offs = NULL; 1057 sec_chan_offs = NULL;
1055 wide_bw_chansw_ie = NULL; 1058 wide_bw_chansw_ie = NULL;
1059 /* only used for bandwidth here */
1060 ht_oper = NULL;
1056 } 1061 }
1057 1062
1058 if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) 1063 if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
@@ -1094,10 +1099,20 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1094 return; 1099 return;
1095 } 1100 }
1096 1101
1097 if (sec_chan_offs) { 1102 if (!beacon && sec_chan_offs) {
1098 secondary_channel_offset = sec_chan_offs->sec_chan_offs; 1103 secondary_channel_offset = sec_chan_offs->sec_chan_offs;
1104 } else if (beacon && ht_oper) {
1105 secondary_channel_offset =
1106 ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
1099 } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { 1107 } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
1100 /* if HT is enabled and the IE not present, it's still HT */ 1108 /*
1109 * If it's not a beacon, HT is enabled and the IE not present,
1110 * it's 20 MHz, 802.11-2012 8.5.2.6:
1111 * This element [the Secondary Channel Offset Element] is
1112 * present when switching to a 40 MHz channel. It may be
1113 * present when switching to a 20 MHz channel (in which
1114 * case the secondary channel offset is set to SCN).
1115 */
1101 secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; 1116 secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
1102 } 1117 }
1103 1118
@@ -2796,7 +2811,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
2796 mutex_unlock(&local->iflist_mtx); 2811 mutex_unlock(&local->iflist_mtx);
2797 } 2812 }
2798 2813
2799 ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); 2814 ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
2815 elems, true);
2800 2816
2801} 2817}
2802 2818
@@ -3210,7 +3226,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
3210 3226
3211 ieee80211_sta_process_chanswitch(sdata, 3227 ieee80211_sta_process_chanswitch(sdata,
3212 rx_status->mactime, 3228 rx_status->mactime,
3213 &elems); 3229 &elems, false);
3214 } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { 3230 } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
3215 ies_len = skb->len - 3231 ies_len = skb->len -
3216 offsetof(struct ieee80211_mgmt, 3232 offsetof(struct ieee80211_mgmt,
@@ -3232,7 +3248,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
3232 3248
3233 ieee80211_sta_process_chanswitch(sdata, 3249 ieee80211_sta_process_chanswitch(sdata,
3234 rx_status->mactime, 3250 rx_status->mactime,
3235 &elems); 3251 &elems, false);
3236 } 3252 }
3237 break; 3253 break;
3238 } 3254 }