Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac80211: set HT channel before association

Changing the channel type during operation is
confusing to some drivers and will be hard to
handle in multi-channel scenarios. Instead of
changing the channel, set it to the right HT
channel before authenticating/associating and
don't change it -- just update the 20/40 MHz
restrictions in rate control as needed when
changed by the AP.

This also fixes a problem that Paul missed in
his fix for the "regulatory makes us deaf"
issue -- when we couldn't use 40 MHz we still
associated saying we were using 40 MHz, which
could in similarly broken APs make us never
even connect successfully.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

authored by

Johannes Berg and committed by
John W. Linville
24398e39 1d98fb12

+117 -138
+3 -7
Documentation/networking/mac80211-auth-assoc-deauth.txt
··· 23 23 end note 24 24 end 25 25 26 - mac80211->driver: config(channel, non-HT) 26 + mac80211->driver: config(channel, channel type) 27 27 mac80211->driver: bss_info_changed(set BSSID, basic rate bitmap) 28 28 mac80211->driver: sta_state(AP, exists) 29 29 ··· 51 51 end 52 52 53 53 alt not previously authenticated (FT) 54 - mac80211->driver: config(channel, non-HT) 54 + mac80211->driver: config(channel, channel type) 55 55 mac80211->driver: bss_info_changed(set BSSID, basic rate bitmap) 56 56 mac80211->driver: sta_state(AP, exists) 57 57 mac80211->driver: sta_state(AP, authenticated) ··· 66 66 end 67 67 68 68 mac80211->driver: set up QoS parameters 69 - 70 - alt is HT channel 71 - mac80211->driver: config(channel, HT params) 72 - end 73 69 74 70 mac80211->driver: bss_info_changed(QoS, HT, associated with AID) 75 71 mac80211->userspace: associated ··· 91 95 mac80211->driver: sta_state(AP,not-exists) 92 96 mac80211->driver: turn off powersave 93 97 mac80211->driver: bss_info_changed(clear BSSID, not associated, no QoS, ...) 94 - mac80211->driver: config(non-HT channel type) 98 + mac80211->driver: config(channel type to non-HT) 95 99 mac80211->userspace: disconnected
-9
net/mac80211/ht.c
··· 19 19 #include "ieee80211_i.h" 20 20 #include "rate.h" 21 21 22 - bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata) 23 - { 24 - const __le16 flg = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40); 25 - if ((sdata->u.mgd.ht_capa_mask.cap_info & flg) && 26 - !(sdata->u.mgd.ht_capa.cap_info & flg)) 27 - return true; 28 - return false; 29 - } 30 - 31 22 static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata, 32 23 struct ieee80211_sta_ht_cap *ht_cap, 33 24 u16 flag)
+3 -7
net/mac80211/ieee80211_i.h
··· 379 379 IEEE80211_STA_UAPSD_ENABLED = BIT(7), 380 380 IEEE80211_STA_NULLFUNC_ACKED = BIT(8), 381 381 IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), 382 + IEEE80211_STA_DISABLE_40MHZ = BIT(10), 382 383 }; 383 384 384 385 struct ieee80211_mgd_auth_data { ··· 511 510 */ 512 511 int rssi_min_thold, rssi_max_thold; 513 512 int last_ave_beacon_signal; 513 + 514 + enum nl80211_channel_type tx_chantype; 514 515 515 516 struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */ 516 517 struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */ ··· 669 666 int drop_unencrypted; 670 667 671 668 char name[IFNAMSIZ]; 672 - 673 - /* 674 - * keep track of whether the HT opmode (stored in 675 - * vif.bss_info.ht_operation_mode) is valid. 676 - */ 677 - bool ht_opmode_valid; 678 669 679 670 /* to detect idle changes */ 680 671 bool old_idle; ··· 1297 1300 struct net_device *dev); 1298 1301 1299 1302 /* HT */ 1300 - bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata); 1301 1303 void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, 1302 1304 struct ieee80211_sta_ht_cap *ht_cap); 1303 1305 void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
+111 -115
net/mac80211/mlme.c
··· 171 171 return (1 << ecw) - 1; 172 172 } 173 173 174 - /* 175 - * ieee80211_enable_ht should be called only after the operating band 176 - * has been determined as ht configuration depends on the hw's 177 - * HT abilities for a specific band. 178 - */ 179 - static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, 180 - struct ieee80211_ht_operation *ht_oper, 181 - const u8 *bssid, u16 ap_ht_cap_flags, 182 - bool beacon_htcap_ie) 174 + static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, 175 + struct ieee80211_ht_operation *ht_oper, 176 + const u8 *bssid, bool reconfig) 183 177 { 184 178 struct ieee80211_local *local = sdata->local; 185 179 struct ieee80211_supported_band *sband; 186 180 struct sta_info *sta; 187 181 u32 changed = 0; 188 - int ht_cfreq; 189 182 u16 ht_opmode; 190 - bool enable_ht = true; 191 - enum nl80211_channel_type prev_chantype; 192 - enum nl80211_channel_type rx_channel_type = NL80211_CHAN_NO_HT; 193 - enum nl80211_channel_type tx_channel_type; 183 + enum nl80211_channel_type channel_type; 194 184 195 185 sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; 196 - prev_chantype = sdata->vif.bss_conf.channel_type; 186 + channel_type = local->hw.conf.channel_type; 197 187 188 + if (WARN_ON_ONCE(channel_type == NL80211_CHAN_NO_HT)) 189 + return 0; 198 190 199 - ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, 200 - sband->band); 201 - /* check that channel matches the right operating channel */ 202 - if (local->hw.conf.channel->center_freq != ht_cfreq) { 203 - /* Some APs mess this up, evidently. 204 - * Netgear WNDR3700 sometimes reports 4 higher than 205 - * the actual channel, for instance. 206 - */ 207 - printk(KERN_DEBUG 208 - "%s: Wrong control channel in association" 209 - " response: configured center-freq: %d" 210 - " ht-cfreq: %d ht->control_chan: %d" 211 - " band: %d. Disabling HT.\n", 212 - sdata->name, 213 - local->hw.conf.channel->center_freq, 214 - ht_cfreq, ht_oper->primary_chan, 215 - sband->band); 216 - enable_ht = false; 217 - } 191 + channel_type = ieee80211_get_tx_channel_type(local, channel_type); 218 192 219 - if (enable_ht) { 220 - rx_channel_type = NL80211_CHAN_HT20; 193 + /* This can change during the lifetime of the BSS */ 194 + if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) 195 + channel_type = NL80211_CHAN_HT20; 221 196 222 - if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) && 223 - !ieee80111_cfg_override_disables_ht40(sdata) && 224 - (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && 225 - (ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { 226 - switch (ht_oper->ht_param & 227 - IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 228 - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 229 - rx_channel_type = NL80211_CHAN_HT40PLUS; 230 - break; 231 - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 232 - rx_channel_type = NL80211_CHAN_HT40MINUS; 233 - break; 234 - } 235 - } 236 - } 237 - 238 - tx_channel_type = ieee80211_get_tx_channel_type(local, rx_channel_type); 239 - 240 - if (local->tmp_channel) 241 - local->tmp_channel_type = rx_channel_type; 242 - 243 - if (!ieee80211_set_channel_type(local, sdata, rx_channel_type)) { 244 - /* can only fail due to HT40+/- mismatch */ 245 - rx_channel_type = NL80211_CHAN_HT20; 246 - WARN_ON(!ieee80211_set_channel_type(local, sdata, 247 - rx_channel_type)); 248 - } 249 - 250 - if (beacon_htcap_ie && (prev_chantype != rx_channel_type)) { 251 - /* 252 - * Whenever the AP announces the HT mode change that can be 253 - * 40MHz intolerant or etc., it would be safer to stop tx 254 - * queues before doing hw config to avoid buffer overflow. 255 - */ 256 - ieee80211_stop_queues_by_reason(&sdata->local->hw, 197 + if (!reconfig || (sdata->u.mgd.tx_chantype != channel_type)) { 198 + if (reconfig) { 199 + /* 200 + * Whenever the AP announces the HT mode changed 201 + * (e.g. 40 MHz intolerant) stop queues to avoid 202 + * sending out frames while the rate control is 203 + * reconfiguring. 204 + */ 205 + ieee80211_stop_queues_by_reason(&sdata->local->hw, 257 206 IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); 258 207 259 - /* flush out all packets */ 260 - synchronize_net(); 208 + /* flush out all packets */ 209 + synchronize_net(); 261 210 262 - drv_flush(local, false); 263 - } 211 + drv_flush(local, false); 212 + } 264 213 265 - /* channel_type change automatically detected */ 266 - ieee80211_hw_config(local, 0); 267 - 268 - if (prev_chantype != tx_channel_type) { 269 214 rcu_read_lock(); 270 215 sta = sta_info_get(sdata, bssid); 271 216 if (sta) 272 217 rate_control_rate_update(local, sband, sta, 273 218 IEEE80211_RC_HT_CHANGED, 274 - tx_channel_type); 219 + channel_type); 275 220 rcu_read_unlock(); 276 221 277 - if (beacon_htcap_ie) 222 + sdata->u.mgd.tx_chantype = channel_type; 223 + 224 + if (reconfig) 278 225 ieee80211_wake_queues_by_reason(&sdata->local->hw, 279 226 IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); 280 227 } ··· 229 282 ht_opmode = le16_to_cpu(ht_oper->operation_mode); 230 283 231 284 /* if bss configuration changed store the new one */ 232 - if (sdata->ht_opmode_valid != enable_ht || 233 - sdata->vif.bss_conf.ht_operation_mode != ht_opmode || 234 - prev_chantype != rx_channel_type) { 285 + if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) { 235 286 changed |= BSS_CHANGED_HT; 236 287 sdata->vif.bss_conf.ht_operation_mode = ht_opmode; 237 - sdata->ht_opmode_valid = enable_ht; 238 288 } 239 289 240 290 return changed; ··· 301 357 cap &= ~IEEE80211_HT_CAP_SGI_40; 302 358 } 303 359 break; 360 + } 361 + 362 + /* 363 + * If 40 MHz was disabled associate as though we weren't 364 + * capable of 40 MHz -- some broken APs will never fall 365 + * back to trying to transmit in 20 MHz. 366 + */ 367 + if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_40MHZ) { 368 + cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 369 + cap &= ~IEEE80211_HT_CAP_SGI_40; 304 370 } 305 371 306 372 /* set SM PS mode properly */ ··· 1390 1436 sdata->vif.bss_conf.assoc = false; 1391 1437 1392 1438 /* on the next assoc, re-program HT parameters */ 1393 - sdata->ht_opmode_valid = false; 1394 1439 memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); 1395 1440 memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); 1396 1441 ··· 1956 2003 struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; 1957 2004 u32 changed = 0; 1958 2005 int err; 1959 - u16 ap_ht_cap_flags; 1960 2006 1961 2007 /* AssocResp and ReassocResp have identical structure */ 1962 2008 ··· 2006 2054 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 2007 2055 elems.ht_cap_elem, &sta->sta.ht_cap); 2008 2056 2009 - ap_ht_cap_flags = sta->sta.ht_cap.cap; 2010 - 2011 2057 rate_control_rate_init(sta); 2012 2058 2013 2059 if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) ··· 2047 2097 2048 2098 if (elems.ht_operation && elems.wmm_param && 2049 2099 !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) 2050 - changed |= ieee80211_enable_ht(sdata, elems.ht_operation, 2051 - cbss->bssid, ap_ht_cap_flags, 2052 - false); 2100 + changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, 2101 + cbss->bssid, false); 2053 2102 2054 2103 /* set AID and assoc capability, 2055 2104 * ieee80211_set_associated() will tell the driver */ ··· 2460 2511 2461 2512 if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && 2462 2513 !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { 2463 - struct sta_info *sta; 2464 2514 struct ieee80211_supported_band *sband; 2465 - u16 ap_ht_cap_flags; 2466 - 2467 - rcu_read_lock(); 2468 - 2469 - sta = sta_info_get(sdata, bssid); 2470 - if (WARN_ON(!sta)) { 2471 - rcu_read_unlock(); 2472 - return; 2473 - } 2474 2515 2475 2516 sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; 2476 2517 2477 - ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 2478 - elems.ht_cap_elem, &sta->sta.ht_cap); 2479 - 2480 - ap_ht_cap_flags = sta->sta.ht_cap.cap; 2481 - 2482 - rcu_read_unlock(); 2483 - 2484 - changed |= ieee80211_enable_ht(sdata, elems.ht_operation, 2485 - bssid, ap_ht_cap_flags, true); 2518 + changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, 2519 + bssid, true); 2486 2520 } 2487 2521 2488 2522 /* Note: country IE parsing is done for us by cfg80211 */ ··· 2997 3065 struct sta_info *sta; 2998 3066 bool have_sta = false; 2999 3067 int err; 3068 + int ht_cfreq; 3069 + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; 3070 + const u8 *ht_oper_ie; 3071 + const struct ieee80211_ht_operation *ht_oper = NULL; 3072 + struct ieee80211_supported_band *sband; 3000 3073 3001 3074 if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) 3002 3075 return -EINVAL; ··· 3023 3086 mutex_unlock(&local->mtx); 3024 3087 3025 3088 /* switch to the right channel */ 3089 + sband = local->hw.wiphy->bands[cbss->channel->band]; 3090 + 3091 + ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ; 3092 + 3093 + if (sband->ht_cap.ht_supported) { 3094 + ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION, 3095 + cbss->information_elements, 3096 + cbss->len_information_elements); 3097 + if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) 3098 + ht_oper = (void *)(ht_oper_ie + 2); 3099 + } 3100 + 3101 + if (ht_oper) { 3102 + ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, 3103 + cbss->channel->band); 3104 + /* check that channel matches the right operating channel */ 3105 + if (cbss->channel->center_freq != ht_cfreq) { 3106 + /* 3107 + * It's possible that some APs are confused here; 3108 + * Netgear WNDR3700 sometimes reports 4 higher than 3109 + * the actual channel in association responses, but 3110 + * since we look at probe response/beacon data here 3111 + * it should be OK. 3112 + */ 3113 + printk(KERN_DEBUG 3114 + "%s: Wrong control channel: center-freq: %d" 3115 + " ht-cfreq: %d ht->primary_chan: %d" 3116 + " band: %d. Disabling HT.\n", 3117 + sdata->name, cbss->channel->center_freq, 3118 + ht_cfreq, ht_oper->primary_chan, 3119 + cbss->channel->band); 3120 + ht_oper = NULL; 3121 + } 3122 + } 3123 + 3124 + if (ht_oper) { 3125 + channel_type = NL80211_CHAN_HT20; 3126 + 3127 + if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { 3128 + switch (ht_oper->ht_param & 3129 + IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 3130 + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 3131 + channel_type = NL80211_CHAN_HT40PLUS; 3132 + break; 3133 + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 3134 + channel_type = NL80211_CHAN_HT40MINUS; 3135 + break; 3136 + } 3137 + } 3138 + } 3139 + 3140 + if (!ieee80211_set_channel_type(local, sdata, channel_type)) { 3141 + /* can only fail due to HT40+/- mismatch */ 3142 + channel_type = NL80211_CHAN_HT20; 3143 + printk(KERN_DEBUG 3144 + "%s: disabling 40 MHz due to multi-vif mismatch\n", 3145 + sdata->name); 3146 + ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; 3147 + WARN_ON(!ieee80211_set_channel_type(local, sdata, 3148 + channel_type)); 3149 + } 3150 + 3026 3151 local->oper_channel = cbss->channel; 3027 - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 3152 + ieee80211_hw_config(local, 0); 3028 3153 3029 3154 if (!have_sta) { 3030 - struct ieee80211_supported_band *sband; 3031 3155 u32 rates = 0, basic_rates = 0; 3032 3156 bool have_higher_than_11mbit; 3033 3157 int min_rate = INT_MAX, min_rate_index = -1; 3034 - 3035 - sband = sdata->local->hw.wiphy->bands[cbss->channel->band]; 3036 3158 3037 3159 ieee80211_get_rates(sband, bss->supp_rates, 3038 3160 bss->supp_rates_len,