diff options
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 226 |
1 files changed, 111 insertions, 115 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bb7e5189e27e..30259a73195c 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 | /* | 174 | static 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 | */ | ||
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) | ||
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, |