aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-03-28 04:58:36 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-10 14:54:07 -0400
commit24398e39c8ee4a9d9123eed322b859ece4d16cac (patch)
tree28e054cd2feaf289bc4fbe279936d2d21ceaba5e /net
parent1d98fb122d8f0c33504576da4107bc807176be1d (diff)
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>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/ht.c9
-rw-r--r--net/mac80211/ieee80211_i.h10
-rw-r--r--net/mac80211/mlme.c226
3 files changed, 114 insertions, 131 deletions
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index f25fff7607d..9b603366943 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -19,15 +19,6 @@
19#include "ieee80211_i.h" 19#include "ieee80211_i.h"
20#include "rate.h" 20#include "rate.h"
21 21
22bool 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
31static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata, 22static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
32 struct ieee80211_sta_ht_cap *ht_cap, 23 struct ieee80211_sta_ht_cap *ht_cap,
33 u16 flag) 24 u16 flag)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 54f5b5b299d..a67ba7c85a1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -379,6 +379,7 @@ enum ieee80211_sta_flags {
379 IEEE80211_STA_UAPSD_ENABLED = BIT(7), 379 IEEE80211_STA_UAPSD_ENABLED = BIT(7),
380 IEEE80211_STA_NULLFUNC_ACKED = BIT(8), 380 IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
381 IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9), 381 IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
382 IEEE80211_STA_DISABLE_40MHZ = BIT(10),
382}; 383};
383 384
384struct ieee80211_mgd_auth_data { 385struct ieee80211_mgd_auth_data {
@@ -511,6 +512,8 @@ struct ieee80211_if_managed {
511 int rssi_min_thold, rssi_max_thold; 512 int rssi_min_thold, rssi_max_thold;
512 int last_ave_beacon_signal; 513 int last_ave_beacon_signal;
513 514
515 enum nl80211_channel_type tx_chantype;
516
514 struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */ 517 struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
515 struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */ 518 struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
516}; 519};
@@ -667,12 +670,6 @@ struct ieee80211_sub_if_data {
667 670
668 char name[IFNAMSIZ]; 671 char name[IFNAMSIZ];
669 672
670 /*
671 * keep track of whether the HT opmode (stored in
672 * vif.bss_info.ht_operation_mode) is valid.
673 */
674 bool ht_opmode_valid;
675
676 /* to detect idle changes */ 673 /* to detect idle changes */
677 bool old_idle; 674 bool old_idle;
678 675
@@ -1300,7 +1297,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
1300 struct net_device *dev); 1297 struct net_device *dev);
1301 1298
1302/* HT */ 1299/* HT */
1303bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata);
1304void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, 1300void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
1305 struct ieee80211_sta_ht_cap *ht_cap); 1301 struct ieee80211_sta_ht_cap *ht_cap);
1306void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, 1302void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index bb7e5189e27..30259a73195 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -171,110 +171,57 @@ static int ecw2cw(int ecw)
171 return (1 << ecw) - 1; 171 return (1 << ecw) - 1;
172} 172}
173 173
174/* 174static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
175 * ieee80211_enable_ht should be called only after the operating band 175 struct ieee80211_ht_operation *ht_oper,
176 * has been determined as ht configuration depends on the hw's 176 const u8 *bssid, bool reconfig)
177 * HT abilities for a specific band.
178 */
179static 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)
183{ 177{
184 struct ieee80211_local *local = sdata->local; 178 struct ieee80211_local *local = sdata->local;
185 struct ieee80211_supported_band *sband; 179 struct ieee80211_supported_band *sband;
186 struct sta_info *sta; 180 struct sta_info *sta;
187 u32 changed = 0; 181 u32 changed = 0;
188 int ht_cfreq;
189 u16 ht_opmode; 182 u16 ht_opmode;
190 bool enable_ht = true; 183 enum nl80211_channel_type channel_type;
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;
194 184
195 sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; 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, 191 channel_type = ieee80211_get_tx_channel_type(local, channel_type);
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 }
218
219 if (enable_ht) {
220 rx_channel_type = NL80211_CHAN_HT20;
221
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 192
243 if (!ieee80211_set_channel_type(local, sdata, rx_channel_type)) { 193 /* This can change during the lifetime of the BSS */
244 /* can only fail due to HT40+/- mismatch */ 194 if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
245 rx_channel_type = NL80211_CHAN_HT20; 195 channel_type = NL80211_CHAN_HT20;
246 WARN_ON(!ieee80211_set_channel_type(local, sdata,
247 rx_channel_type));
248 }
249 196
250 if (beacon_htcap_ie && (prev_chantype != rx_channel_type)) { 197 if (!reconfig || (sdata->u.mgd.tx_chantype != channel_type)) {
251 /* 198 if (reconfig) {
252 * Whenever the AP announces the HT mode change that can be 199 /*
253 * 40MHz intolerant or etc., it would be safer to stop tx 200 * Whenever the AP announces the HT mode changed
254 * queues before doing hw config to avoid buffer overflow. 201 * (e.g. 40 MHz intolerant) stop queues to avoid
255 */ 202 * sending out frames while the rate control is
256 ieee80211_stop_queues_by_reason(&sdata->local->hw, 203 * reconfiguring.
204 */
205 ieee80211_stop_queues_by_reason(&sdata->local->hw,
257 IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); 206 IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
258 207
259 /* flush out all packets */ 208 /* flush out all packets */
260 synchronize_net(); 209 synchronize_net();
261
262 drv_flush(local, false);
263 }
264 210
265 /* channel_type change automatically detected */ 211 drv_flush(local, false);
266 ieee80211_hw_config(local, 0); 212 }
267 213
268 if (prev_chantype != tx_channel_type) {
269 rcu_read_lock(); 214 rcu_read_lock();
270 sta = sta_info_get(sdata, bssid); 215 sta = sta_info_get(sdata, bssid);
271 if (sta) 216 if (sta)
272 rate_control_rate_update(local, sband, sta, 217 rate_control_rate_update(local, sband, sta,
273 IEEE80211_RC_HT_CHANGED, 218 IEEE80211_RC_HT_CHANGED,
274 tx_channel_type); 219 channel_type);
275 rcu_read_unlock(); 220 rcu_read_unlock();
276 221
277 if (beacon_htcap_ie) 222 sdata->u.mgd.tx_chantype = channel_type;
223
224 if (reconfig)
278 ieee80211_wake_queues_by_reason(&sdata->local->hw, 225 ieee80211_wake_queues_by_reason(&sdata->local->hw,
279 IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); 226 IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
280 } 227 }
@@ -282,12 +229,9 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
282 ht_opmode = le16_to_cpu(ht_oper->operation_mode); 229 ht_opmode = le16_to_cpu(ht_oper->operation_mode);
283 230
284 /* if bss configuration changed store the new one */ 231 /* if bss configuration changed store the new one */
285 if (sdata->ht_opmode_valid != enable_ht || 232 if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) {
286 sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
287 prev_chantype != rx_channel_type) {
288 changed |= BSS_CHANGED_HT; 233 changed |= BSS_CHANGED_HT;
289 sdata->vif.bss_conf.ht_operation_mode = ht_opmode; 234 sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
290 sdata->ht_opmode_valid = enable_ht;
291 } 235 }
292 236
293 return changed; 237 return changed;
@@ -359,6 +303,16 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
359 break; 303 break;
360 } 304 }
361 305
306 /*
307 * If 40 MHz was disabled associate as though we weren't
308 * capable of 40 MHz -- some broken APs will never fall
309 * back to trying to transmit in 20 MHz.
310 */
311 if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_40MHZ) {
312 cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
313 cap &= ~IEEE80211_HT_CAP_SGI_40;
314 }
315
362 /* set SM PS mode properly */ 316 /* set SM PS mode properly */
363 cap &= ~IEEE80211_HT_CAP_SM_PS; 317 cap &= ~IEEE80211_HT_CAP_SM_PS;
364 switch (smps) { 318 switch (smps) {
@@ -1436,7 +1390,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
1436 sdata->vif.bss_conf.assoc = false; 1390 sdata->vif.bss_conf.assoc = false;
1437 1391
1438 /* on the next assoc, re-program HT parameters */ 1392 /* on the next assoc, re-program HT parameters */
1439 sdata->ht_opmode_valid = false;
1440 memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); 1393 memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
1441 memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); 1394 memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
1442 1395
@@ -2003,7 +1956,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
2003 struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; 1956 struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
2004 u32 changed = 0; 1957 u32 changed = 0;
2005 int err; 1958 int err;
2006 u16 ap_ht_cap_flags;
2007 1959
2008 /* AssocResp and ReassocResp have identical structure */ 1960 /* AssocResp and ReassocResp have identical structure */
2009 1961
@@ -2054,8 +2006,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
2054 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 2006 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
2055 elems.ht_cap_elem, &sta->sta.ht_cap); 2007 elems.ht_cap_elem, &sta->sta.ht_cap);
2056 2008
2057 ap_ht_cap_flags = sta->sta.ht_cap.cap;
2058
2059 rate_control_rate_init(sta); 2009 rate_control_rate_init(sta);
2060 2010
2061 if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) 2011 if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
@@ -2097,9 +2047,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
2097 2047
2098 if (elems.ht_operation && elems.wmm_param && 2048 if (elems.ht_operation && elems.wmm_param &&
2099 !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) 2049 !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
2100 changed |= ieee80211_enable_ht(sdata, elems.ht_operation, 2050 changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
2101 cbss->bssid, ap_ht_cap_flags, 2051 cbss->bssid, false);
2102 false);
2103 2052
2104 /* set AID and assoc capability, 2053 /* set AID and assoc capability,
2105 * ieee80211_set_associated() will tell the driver */ 2054 * ieee80211_set_associated() will tell the driver */
@@ -2511,29 +2460,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
2511 2460
2512 if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && 2461 if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
2513 !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { 2462 !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
2514 struct sta_info *sta;
2515 struct ieee80211_supported_band *sband; 2463 struct ieee80211_supported_band *sband;
2516 u16 ap_ht_cap_flags;
2517
2518 rcu_read_lock();
2519
2520 sta = sta_info_get(sdata, bssid);
2521 if (WARN_ON(!sta)) {
2522 rcu_read_unlock();
2523 return;
2524 }
2525 2464
2526 sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; 2465 sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
2527 2466
2528 ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 2467 changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
2529 elems.ht_cap_elem, &sta->sta.ht_cap); 2468 bssid, true);
2530
2531 ap_ht_cap_flags = sta->sta.ht_cap.cap;
2532
2533 rcu_read_unlock();
2534
2535 changed |= ieee80211_enable_ht(sdata, elems.ht_operation,
2536 bssid, ap_ht_cap_flags, true);
2537 } 2469 }
2538 2470
2539 /* Note: country IE parsing is done for us by cfg80211 */ 2471 /* Note: country IE parsing is done for us by cfg80211 */
@@ -3065,6 +2997,11 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
3065 struct sta_info *sta; 2997 struct sta_info *sta;
3066 bool have_sta = false; 2998 bool have_sta = false;
3067 int err; 2999 int err;
3000 int ht_cfreq;
3001 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
3002 const u8 *ht_oper_ie;
3003 const struct ieee80211_ht_operation *ht_oper = NULL;
3004 struct ieee80211_supported_band *sband;
3068 3005
3069 if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) 3006 if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
3070 return -EINVAL; 3007 return -EINVAL;
@@ -3086,17 +3023,76 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
3086 mutex_unlock(&local->mtx); 3023 mutex_unlock(&local->mtx);
3087 3024
3088 /* switch to the right channel */ 3025 /* switch to the right channel */
3026 sband = local->hw.wiphy->bands[cbss->channel->band];
3027
3028 ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
3029
3030 if (sband->ht_cap.ht_supported) {
3031 ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
3032 cbss->information_elements,
3033 cbss->len_information_elements);
3034 if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
3035 ht_oper = (void *)(ht_oper_ie + 2);
3036 }
3037
3038 if (ht_oper) {
3039 ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
3040 cbss->channel->band);
3041 /* check that channel matches the right operating channel */
3042 if (cbss->channel->center_freq != ht_cfreq) {
3043 /*
3044 * It's possible that some APs are confused here;
3045 * Netgear WNDR3700 sometimes reports 4 higher than
3046 * the actual channel in association responses, but
3047 * since we look at probe response/beacon data here
3048 * it should be OK.
3049 */
3050 printk(KERN_DEBUG
3051 "%s: Wrong control channel: center-freq: %d"
3052 " ht-cfreq: %d ht->primary_chan: %d"
3053 " band: %d. Disabling HT.\n",
3054 sdata->name, cbss->channel->center_freq,
3055 ht_cfreq, ht_oper->primary_chan,
3056 cbss->channel->band);
3057 ht_oper = NULL;
3058 }
3059 }
3060
3061 if (ht_oper) {
3062 channel_type = NL80211_CHAN_HT20;
3063
3064 if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
3065 switch (ht_oper->ht_param &
3066 IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
3067 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
3068 channel_type = NL80211_CHAN_HT40PLUS;
3069 break;
3070 case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
3071 channel_type = NL80211_CHAN_HT40MINUS;
3072 break;
3073 }
3074 }
3075 }
3076
3077 if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
3078 /* can only fail due to HT40+/- mismatch */
3079 channel_type = NL80211_CHAN_HT20;
3080 printk(KERN_DEBUG
3081 "%s: disabling 40 MHz due to multi-vif mismatch\n",
3082 sdata->name);
3083 ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
3084 WARN_ON(!ieee80211_set_channel_type(local, sdata,
3085 channel_type));
3086 }
3087
3089 local->oper_channel = cbss->channel; 3088 local->oper_channel = cbss->channel;
3090 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); 3089 ieee80211_hw_config(local, 0);
3091 3090
3092 if (!have_sta) { 3091 if (!have_sta) {
3093 struct ieee80211_supported_band *sband;
3094 u32 rates = 0, basic_rates = 0; 3092 u32 rates = 0, basic_rates = 0;
3095 bool have_higher_than_11mbit; 3093 bool have_higher_than_11mbit;
3096 int min_rate = INT_MAX, min_rate_index = -1; 3094 int min_rate = INT_MAX, min_rate_index = -1;
3097 3095
3098 sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
3099
3100 ieee80211_get_rates(sband, bss->supp_rates, 3096 ieee80211_get_rates(sband, bss->supp_rates,
3101 bss->supp_rates_len, 3097 bss->supp_rates_len,
3102 &rates, &basic_rates, 3098 &rates, &basic_rates,