diff options
author | John W. Linville <linville@tuxdriver.com> | 2014-09-15 14:51:23 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-09-15 14:51:23 -0400 |
commit | 6bd2bd27baf12fa0f2e6d611509fc0e1bffb0f97 (patch) | |
tree | 1aa11eb70af1e43bc122cb516bab264ddda76050 /net | |
parent | eb2eacf77ec4828c9a2e451b06bc6fc44266f4c0 (diff) | |
parent | 0d8614b4b926d0f657d15d7eb5125bcb24b9fd41 (diff) |
Merge tag 'mac80211-next-for-john-2014-09-12' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg <johannes@sipsolutions.net> says:
"This time, I have some rate minstrel improvements, support for a very
small feature from CCX that Steinar reverse-engineered, dynamic ACK
timeout support, a number of changes for TDLS, early support for radio
resource measurement and many fixes. Also, I'm changing a number of
places to clear key memory when it's freed and Intel claims copyright
for code they developed."
Conflicts:
net/mac80211/iface.c
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
43 files changed, 945 insertions, 357 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index f0e84bc48038..a48bad468880 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c | |||
@@ -227,7 +227,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d | |||
227 | void __ieee80211_start_rx_ba_session(struct sta_info *sta, | 227 | void __ieee80211_start_rx_ba_session(struct sta_info *sta, |
228 | u8 dialog_token, u16 timeout, | 228 | u8 dialog_token, u16 timeout, |
229 | u16 start_seq_num, u16 ba_policy, u16 tid, | 229 | u16 start_seq_num, u16 ba_policy, u16 tid, |
230 | u16 buf_size, bool tx) | 230 | u16 buf_size, bool tx, bool auto_seq) |
231 | { | 231 | { |
232 | struct ieee80211_local *local = sta->sdata->local; | 232 | struct ieee80211_local *local = sta->sdata->local; |
233 | struct tid_ampdu_rx *tid_agg_rx; | 233 | struct tid_ampdu_rx *tid_agg_rx; |
@@ -326,6 +326,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
326 | tid_agg_rx->buf_size = buf_size; | 326 | tid_agg_rx->buf_size = buf_size; |
327 | tid_agg_rx->timeout = timeout; | 327 | tid_agg_rx->timeout = timeout; |
328 | tid_agg_rx->stored_mpdu_num = 0; | 328 | tid_agg_rx->stored_mpdu_num = 0; |
329 | tid_agg_rx->auto_seq = auto_seq; | ||
329 | status = WLAN_STATUS_SUCCESS; | 330 | status = WLAN_STATUS_SUCCESS; |
330 | 331 | ||
331 | /* activate it for RX */ | 332 | /* activate it for RX */ |
@@ -367,7 +368,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
367 | 368 | ||
368 | __ieee80211_start_rx_ba_session(sta, dialog_token, timeout, | 369 | __ieee80211_start_rx_ba_session(sta, dialog_token, timeout, |
369 | start_seq_num, ba_policy, tid, | 370 | start_seq_num, ba_policy, tid, |
370 | buf_size, true); | 371 | buf_size, true, false); |
371 | } | 372 | } |
372 | 373 | ||
373 | void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, | 374 | void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4d8989b87960..fb6a1502b6df 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * mac80211 configuration hooks for cfg80211 | 2 | * mac80211 configuration hooks for cfg80211 |
3 | * | 3 | * |
4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
5 | * | 6 | * |
6 | * This file is GPLv2 as found in COPYING. | 7 | * This file is GPLv2 as found in COPYING. |
7 | */ | 8 | */ |
@@ -682,8 +683,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, | |||
682 | if (old) | 683 | if (old) |
683 | return -EALREADY; | 684 | return -EALREADY; |
684 | 685 | ||
685 | /* TODO: make hostapd tell us what it wants */ | 686 | switch (params->smps_mode) { |
686 | sdata->smps_mode = IEEE80211_SMPS_OFF; | 687 | case NL80211_SMPS_OFF: |
688 | sdata->smps_mode = IEEE80211_SMPS_OFF; | ||
689 | break; | ||
690 | case NL80211_SMPS_STATIC: | ||
691 | sdata->smps_mode = IEEE80211_SMPS_STATIC; | ||
692 | break; | ||
693 | case NL80211_SMPS_DYNAMIC: | ||
694 | sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
695 | break; | ||
696 | default: | ||
697 | return -EINVAL; | ||
698 | } | ||
687 | sdata->needed_rx_chains = sdata->local->rx_chains; | 699 | sdata->needed_rx_chains = sdata->local->rx_chains; |
688 | 700 | ||
689 | mutex_lock(&local->mtx); | 701 | mutex_lock(&local->mtx); |
@@ -1977,8 +1989,13 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) | |||
1977 | return err; | 1989 | return err; |
1978 | } | 1990 | } |
1979 | 1991 | ||
1980 | if (changed & WIPHY_PARAM_COVERAGE_CLASS) { | 1992 | if ((changed & WIPHY_PARAM_COVERAGE_CLASS) || |
1981 | err = drv_set_coverage_class(local, wiphy->coverage_class); | 1993 | (changed & WIPHY_PARAM_DYN_ACK)) { |
1994 | s16 coverage_class; | ||
1995 | |||
1996 | coverage_class = changed & WIPHY_PARAM_COVERAGE_CLASS ? | ||
1997 | wiphy->coverage_class : -1; | ||
1998 | err = drv_set_coverage_class(local, coverage_class); | ||
1982 | 1999 | ||
1983 | if (err) | 2000 | if (err) |
1984 | return err; | 2001 | return err; |
@@ -2351,6 +2368,58 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, | |||
2351 | return 0; | 2368 | return 0; |
2352 | } | 2369 | } |
2353 | 2370 | ||
2371 | static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local, | ||
2372 | struct ieee80211_roc_work *new_roc, | ||
2373 | struct ieee80211_roc_work *cur_roc) | ||
2374 | { | ||
2375 | unsigned long j = jiffies; | ||
2376 | unsigned long cur_roc_end = cur_roc->hw_start_time + | ||
2377 | msecs_to_jiffies(cur_roc->duration); | ||
2378 | struct ieee80211_roc_work *next_roc; | ||
2379 | int new_dur; | ||
2380 | |||
2381 | if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun)) | ||
2382 | return false; | ||
2383 | |||
2384 | if (time_after(j + IEEE80211_ROC_MIN_LEFT, cur_roc_end)) | ||
2385 | return false; | ||
2386 | |||
2387 | ieee80211_handle_roc_started(new_roc); | ||
2388 | |||
2389 | new_dur = new_roc->duration - jiffies_to_msecs(cur_roc_end - j); | ||
2390 | |||
2391 | /* cur_roc is long enough - add new_roc to the dependents list. */ | ||
2392 | if (new_dur <= 0) { | ||
2393 | list_add_tail(&new_roc->list, &cur_roc->dependents); | ||
2394 | return true; | ||
2395 | } | ||
2396 | |||
2397 | new_roc->duration = new_dur; | ||
2398 | |||
2399 | /* | ||
2400 | * if cur_roc was already coalesced before, we might | ||
2401 | * want to extend the next roc instead of adding | ||
2402 | * a new one. | ||
2403 | */ | ||
2404 | next_roc = list_entry(cur_roc->list.next, | ||
2405 | struct ieee80211_roc_work, list); | ||
2406 | if (&next_roc->list != &local->roc_list && | ||
2407 | next_roc->chan == new_roc->chan && | ||
2408 | next_roc->sdata == new_roc->sdata && | ||
2409 | !WARN_ON(next_roc->started)) { | ||
2410 | list_add_tail(&new_roc->list, &next_roc->dependents); | ||
2411 | next_roc->duration = max(next_roc->duration, | ||
2412 | new_roc->duration); | ||
2413 | next_roc->type = max(next_roc->type, new_roc->type); | ||
2414 | return true; | ||
2415 | } | ||
2416 | |||
2417 | /* add right after cur_roc */ | ||
2418 | list_add(&new_roc->list, &cur_roc->list); | ||
2419 | |||
2420 | return true; | ||
2421 | } | ||
2422 | |||
2354 | static int ieee80211_start_roc_work(struct ieee80211_local *local, | 2423 | static int ieee80211_start_roc_work(struct ieee80211_local *local, |
2355 | struct ieee80211_sub_if_data *sdata, | 2424 | struct ieee80211_sub_if_data *sdata, |
2356 | struct ieee80211_channel *channel, | 2425 | struct ieee80211_channel *channel, |
@@ -2456,8 +2525,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, | |||
2456 | 2525 | ||
2457 | /* If it has already started, it's more difficult ... */ | 2526 | /* If it has already started, it's more difficult ... */ |
2458 | if (local->ops->remain_on_channel) { | 2527 | if (local->ops->remain_on_channel) { |
2459 | unsigned long j = jiffies; | ||
2460 | |||
2461 | /* | 2528 | /* |
2462 | * In the offloaded ROC case, if it hasn't begun, add | 2529 | * In the offloaded ROC case, if it hasn't begun, add |
2463 | * this new one to the dependent list to be handled | 2530 | * this new one to the dependent list to be handled |
@@ -2480,28 +2547,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, | |||
2480 | break; | 2547 | break; |
2481 | } | 2548 | } |
2482 | 2549 | ||
2483 | if (time_before(j + IEEE80211_ROC_MIN_LEFT, | 2550 | if (ieee80211_coalesce_started_roc(local, roc, tmp)) |
2484 | tmp->hw_start_time + | ||
2485 | msecs_to_jiffies(tmp->duration))) { | ||
2486 | int new_dur; | ||
2487 | |||
2488 | ieee80211_handle_roc_started(roc); | ||
2489 | |||
2490 | new_dur = roc->duration - | ||
2491 | jiffies_to_msecs(tmp->hw_start_time + | ||
2492 | msecs_to_jiffies( | ||
2493 | tmp->duration) - | ||
2494 | j); | ||
2495 | |||
2496 | if (new_dur > 0) { | ||
2497 | /* add right after tmp */ | ||
2498 | list_add(&roc->list, &tmp->list); | ||
2499 | } else { | ||
2500 | list_add_tail(&roc->list, | ||
2501 | &tmp->dependents); | ||
2502 | } | ||
2503 | queued = true; | 2551 | queued = true; |
2504 | } | ||
2505 | } else if (del_timer_sync(&tmp->work.timer)) { | 2552 | } else if (del_timer_sync(&tmp->work.timer)) { |
2506 | unsigned long new_end; | 2553 | unsigned long new_end; |
2507 | 2554 | ||
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 0e963bc1ceac..54a189f0393e 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * mac80211 debugfs for wireless PHYs | 3 | * mac80211 debugfs for wireless PHYs |
4 | * | 4 | * |
5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * GPLv2 | 8 | * GPLv2 |
8 | * | 9 | * |
@@ -302,11 +303,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, | |||
302 | sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); | 303 | sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); |
303 | if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) | 304 | if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) |
304 | sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); | 305 | sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); |
305 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) | ||
306 | sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); | ||
307 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) | ||
308 | sf += scnprintf(buf + sf, mxln - sf, | ||
309 | "SUPPORTS_DYNAMIC_SMPS\n"); | ||
310 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) | 306 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) |
311 | sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); | 307 | sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); |
312 | if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) | 308 | if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) |
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index e205ebabfa50..c68896adfa96 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
@@ -226,12 +226,12 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, | |||
226 | struct ieee80211_local *local = sdata->local; | 226 | struct ieee80211_local *local = sdata->local; |
227 | int err; | 227 | int err; |
228 | 228 | ||
229 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) && | 229 | if (!(local->hw.wiphy->features & NL80211_FEATURE_STATIC_SMPS) && |
230 | smps_mode == IEEE80211_SMPS_STATIC) | 230 | smps_mode == IEEE80211_SMPS_STATIC) |
231 | return -EINVAL; | 231 | return -EINVAL; |
232 | 232 | ||
233 | /* auto should be dynamic if in PS mode */ | 233 | /* auto should be dynamic if in PS mode */ |
234 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) && | 234 | if (!(local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS) && |
235 | (smps_mode == IEEE80211_SMPS_DYNAMIC || | 235 | (smps_mode == IEEE80211_SMPS_DYNAMIC || |
236 | smps_mode == IEEE80211_SMPS_AUTOMATIC)) | 236 | smps_mode == IEEE80211_SMPS_AUTOMATIC)) |
237 | return -EINVAL; | 237 | return -EINVAL; |
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 33eb4a43a2f3..bafe48916229 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Copyright 2003-2005 Devicescape Software, Inc. | 2 | * Copyright 2003-2005 Devicescape Software, Inc. |
3 | * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> | 3 | * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> |
4 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 11423958116a..196d48c68134 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h | |||
@@ -450,7 +450,7 @@ static inline int drv_set_rts_threshold(struct ieee80211_local *local, | |||
450 | } | 450 | } |
451 | 451 | ||
452 | static inline int drv_set_coverage_class(struct ieee80211_local *local, | 452 | static inline int drv_set_coverage_class(struct ieee80211_local *local, |
453 | u8 value) | 453 | s16 value) |
454 | { | 454 | { |
455 | int ret = 0; | 455 | int ret = 0; |
456 | might_sleep(); | 456 | might_sleep(); |
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 5f9654d31a8d..56b53571c807 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 6 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
7 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> | 7 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> |
8 | * Copyright 2009, Johannes Berg <johannes@sipsolutions.net> | 8 | * Copyright 2009, Johannes Berg <johannes@sipsolutions.net> |
9 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
9 | * | 10 | * |
10 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 12 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ffb20e5e6cf3..c2aaec4dfcf0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -3,6 +3,7 @@ | |||
3 | * Copyright 2005, Devicescape Software, Inc. | 3 | * Copyright 2005, Devicescape Software, Inc. |
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -354,6 +355,7 @@ enum ieee80211_sta_flags { | |||
354 | IEEE80211_STA_DISABLE_80P80MHZ = BIT(12), | 355 | IEEE80211_STA_DISABLE_80P80MHZ = BIT(12), |
355 | IEEE80211_STA_DISABLE_160MHZ = BIT(13), | 356 | IEEE80211_STA_DISABLE_160MHZ = BIT(13), |
356 | IEEE80211_STA_DISABLE_WMM = BIT(14), | 357 | IEEE80211_STA_DISABLE_WMM = BIT(14), |
358 | IEEE80211_STA_ENABLE_RRM = BIT(15), | ||
357 | }; | 359 | }; |
358 | 360 | ||
359 | struct ieee80211_mgd_auth_data { | 361 | struct ieee80211_mgd_auth_data { |
@@ -1367,6 +1369,7 @@ struct ieee802_11_elems { | |||
1367 | const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; | 1369 | const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; |
1368 | const u8 *country_elem; | 1370 | const u8 *country_elem; |
1369 | const u8 *pwr_constr_elem; | 1371 | const u8 *pwr_constr_elem; |
1372 | const u8 *cisco_dtpc_elem; | ||
1370 | const struct ieee80211_timeout_interval_ie *timeout_int; | 1373 | const struct ieee80211_timeout_interval_ie *timeout_int; |
1371 | const u8 *opmode_notif; | 1374 | const u8 *opmode_notif; |
1372 | const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; | 1375 | const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; |
@@ -1587,7 +1590,7 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | |||
1587 | void __ieee80211_start_rx_ba_session(struct sta_info *sta, | 1590 | void __ieee80211_start_rx_ba_session(struct sta_info *sta, |
1588 | u8 dialog_token, u16 timeout, | 1591 | u8 dialog_token, u16 timeout, |
1589 | u16 start_seq_num, u16 ba_policy, u16 tid, | 1592 | u16 start_seq_num, u16 ba_policy, u16 tid, |
1590 | u16 buf_size, bool tx); | 1593 | u16 buf_size, bool tx, bool auto_seq); |
1591 | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, | 1594 | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, |
1592 | enum ieee80211_agg_stop_reason reason); | 1595 | enum ieee80211_agg_stop_reason reason); |
1593 | void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, | 1596 | void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, |
@@ -1917,7 +1920,7 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, | |||
1917 | size_t extra_ies_len); | 1920 | size_t extra_ies_len); |
1918 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | 1921 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, |
1919 | const u8 *peer, enum nl80211_tdls_operation oper); | 1922 | const u8 *peer, enum nl80211_tdls_operation oper); |
1920 | 1923 | void ieee80211_tdls_peer_del_work(struct work_struct *wk); | |
1921 | 1924 | ||
1922 | extern const struct ethtool_ops ieee80211_ethtool_ops; | 1925 | extern const struct ethtool_ops ieee80211_ethtool_ops; |
1923 | 1926 | ||
@@ -1928,4 +1931,3 @@ extern const struct ethtool_ops ieee80211_ethtool_ops; | |||
1928 | #endif | 1931 | #endif |
1929 | 1932 | ||
1930 | #endif /* IEEE80211_I_H */ | 1933 | #endif /* IEEE80211_I_H */ |
1931 | void ieee80211_tdls_peer_del_work(struct work_struct *wk); | ||
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index f75e5f132c5a..af237223a8cd 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * Copyright 2005-2006, Devicescape Software, Inc. | 5 | * Copyright 2005-2006, Devicescape Software, Inc. |
6 | * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> | 6 | * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> |
7 | * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> | 7 | * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> |
8 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
8 | * | 9 | * |
9 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
@@ -1172,19 +1173,11 @@ static void ieee80211_iface_work(struct work_struct *work) | |||
1172 | rx_agg = (void *)&skb->cb; | 1173 | rx_agg = (void *)&skb->cb; |
1173 | mutex_lock(&local->sta_mtx); | 1174 | mutex_lock(&local->sta_mtx); |
1174 | sta = sta_info_get_bss(sdata, rx_agg->addr); | 1175 | sta = sta_info_get_bss(sdata, rx_agg->addr); |
1175 | if (sta) { | 1176 | if (sta) |
1176 | u16 last_seq; | ||
1177 | |||
1178 | last_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu( | ||
1179 | sta->last_seq_ctrl[rx_agg->tid])); | ||
1180 | |||
1181 | __ieee80211_start_rx_ba_session(sta, | 1177 | __ieee80211_start_rx_ba_session(sta, |
1182 | 0, 0, | 1178 | 0, 0, 0, 1, rx_agg->tid, |
1183 | ieee80211_sn_inc(last_seq), | ||
1184 | 1, rx_agg->tid, | ||
1185 | IEEE80211_MAX_AMPDU_BUF, | 1179 | IEEE80211_MAX_AMPDU_BUF, |
1186 | false); | 1180 | false, true); |
1187 | } | ||
1188 | mutex_unlock(&local->sta_mtx); | 1181 | mutex_unlock(&local->sta_mtx); |
1189 | } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) { | 1182 | } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) { |
1190 | rx_agg = (void *)&skb->cb; | 1183 | rx_agg = (void *)&skb->cb; |
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 6429d0e1d4a1..4712150dc210 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -421,7 +422,7 @@ static void ieee80211_key_free_common(struct ieee80211_key *key) | |||
421 | ieee80211_aes_key_free(key->u.ccmp.tfm); | 422 | ieee80211_aes_key_free(key->u.ccmp.tfm); |
422 | if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) | 423 | if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) |
423 | ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); | 424 | ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); |
424 | kfree(key); | 425 | kzfree(key); |
425 | } | 426 | } |
426 | 427 | ||
427 | static void __ieee80211_key_destroy(struct ieee80211_key *key, | 428 | static void __ieee80211_key_destroy(struct ieee80211_key *key, |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index e0ab4320a078..0de7c93bf62b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Copyright 2002-2005, Instant802 Networks, Inc. | 2 | * Copyright 2002-2005, Instant802 Networks, Inc. |
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8a73de6a5f5b..2de88704278b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * Copyright 2005, Devicescape Software, Inc. | 5 | * Copyright 2005, Devicescape Software, Inc. |
6 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 6 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
7 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> | 7 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> |
8 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
8 | * | 9 | * |
9 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
@@ -172,7 +173,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | |||
172 | 173 | ||
173 | if (!(ht_cap->cap_info & | 174 | if (!(ht_cap->cap_info & |
174 | cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) { | 175 | cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) { |
175 | ret = IEEE80211_STA_DISABLE_40MHZ | IEEE80211_STA_DISABLE_VHT; | 176 | ret = IEEE80211_STA_DISABLE_40MHZ; |
176 | goto out; | 177 | goto out; |
177 | } | 178 | } |
178 | 179 | ||
@@ -672,6 +673,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
672 | (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) | 673 | (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) |
673 | capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; | 674 | capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; |
674 | 675 | ||
676 | if (ifmgd->flags & IEEE80211_STA_ENABLE_RRM) | ||
677 | capab |= WLAN_CAPABILITY_RADIO_MEASURE; | ||
678 | |||
675 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); | 679 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); |
676 | memset(mgmt, 0, 24); | 680 | memset(mgmt, 0, 24); |
677 | memcpy(mgmt->da, assoc_data->bss->bssid, ETH_ALEN); | 681 | memcpy(mgmt->da, assoc_data->bss->bssid, ETH_ALEN); |
@@ -737,16 +741,17 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
737 | } | 741 | } |
738 | } | 742 | } |
739 | 743 | ||
740 | if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { | 744 | if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT || |
741 | /* 1. power capabilities */ | 745 | capab & WLAN_CAPABILITY_RADIO_MEASURE) { |
742 | pos = skb_put(skb, 4); | 746 | pos = skb_put(skb, 4); |
743 | *pos++ = WLAN_EID_PWR_CAPABILITY; | 747 | *pos++ = WLAN_EID_PWR_CAPABILITY; |
744 | *pos++ = 2; | 748 | *pos++ = 2; |
745 | *pos++ = 0; /* min tx power */ | 749 | *pos++ = 0; /* min tx power */ |
746 | /* max tx power */ | 750 | /* max tx power */ |
747 | *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def); | 751 | *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def); |
752 | } | ||
748 | 753 | ||
749 | /* 2. supported channels */ | 754 | if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { |
750 | /* TODO: get this in reg domain format */ | 755 | /* TODO: get this in reg domain format */ |
751 | pos = skb_put(skb, 2 * sband->n_channels + 2); | 756 | pos = skb_put(skb, 2 * sband->n_channels + 2); |
752 | *pos++ = WLAN_EID_SUPPORTED_CHANNELS; | 757 | *pos++ = WLAN_EID_SUPPORTED_CHANNELS; |
@@ -1166,19 +1171,21 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
1166 | TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval)); | 1171 | TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval)); |
1167 | } | 1172 | } |
1168 | 1173 | ||
1169 | static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | 1174 | static bool |
1170 | struct ieee80211_channel *channel, | 1175 | ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata, |
1171 | const u8 *country_ie, u8 country_ie_len, | 1176 | struct ieee80211_channel *channel, |
1172 | const u8 *pwr_constr_elem) | 1177 | const u8 *country_ie, u8 country_ie_len, |
1178 | const u8 *pwr_constr_elem, | ||
1179 | int *chan_pwr, int *pwr_reduction) | ||
1173 | { | 1180 | { |
1174 | struct ieee80211_country_ie_triplet *triplet; | 1181 | struct ieee80211_country_ie_triplet *triplet; |
1175 | int chan = ieee80211_frequency_to_channel(channel->center_freq); | 1182 | int chan = ieee80211_frequency_to_channel(channel->center_freq); |
1176 | int i, chan_pwr, chan_increment, new_ap_level; | 1183 | int i, chan_increment; |
1177 | bool have_chan_pwr = false; | 1184 | bool have_chan_pwr = false; |
1178 | 1185 | ||
1179 | /* Invalid IE */ | 1186 | /* Invalid IE */ |
1180 | if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | 1187 | if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) |
1181 | return 0; | 1188 | return false; |
1182 | 1189 | ||
1183 | triplet = (void *)(country_ie + 3); | 1190 | triplet = (void *)(country_ie + 3); |
1184 | country_ie_len -= 3; | 1191 | country_ie_len -= 3; |
@@ -1206,7 +1213,7 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | |||
1206 | for (i = 0; i < triplet->chans.num_channels; i++) { | 1213 | for (i = 0; i < triplet->chans.num_channels; i++) { |
1207 | if (first_channel + i * chan_increment == chan) { | 1214 | if (first_channel + i * chan_increment == chan) { |
1208 | have_chan_pwr = true; | 1215 | have_chan_pwr = true; |
1209 | chan_pwr = triplet->chans.max_power; | 1216 | *chan_pwr = triplet->chans.max_power; |
1210 | break; | 1217 | break; |
1211 | } | 1218 | } |
1212 | } | 1219 | } |
@@ -1218,18 +1225,76 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | |||
1218 | country_ie_len -= 3; | 1225 | country_ie_len -= 3; |
1219 | } | 1226 | } |
1220 | 1227 | ||
1221 | if (!have_chan_pwr) | 1228 | if (have_chan_pwr) |
1229 | *pwr_reduction = *pwr_constr_elem; | ||
1230 | return have_chan_pwr; | ||
1231 | } | ||
1232 | |||
1233 | static void ieee80211_find_cisco_dtpc(struct ieee80211_sub_if_data *sdata, | ||
1234 | struct ieee80211_channel *channel, | ||
1235 | const u8 *cisco_dtpc_ie, | ||
1236 | int *pwr_level) | ||
1237 | { | ||
1238 | /* From practical testing, the first data byte of the DTPC element | ||
1239 | * seems to contain the requested dBm level, and the CLI on Cisco | ||
1240 | * APs clearly state the range is -127 to 127 dBm, which indicates | ||
1241 | * a signed byte, although it seemingly never actually goes negative. | ||
1242 | * The other byte seems to always be zero. | ||
1243 | */ | ||
1244 | *pwr_level = (__s8)cisco_dtpc_ie[4]; | ||
1245 | } | ||
1246 | |||
1247 | static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | ||
1248 | struct ieee80211_channel *channel, | ||
1249 | struct ieee80211_mgmt *mgmt, | ||
1250 | const u8 *country_ie, u8 country_ie_len, | ||
1251 | const u8 *pwr_constr_ie, | ||
1252 | const u8 *cisco_dtpc_ie) | ||
1253 | { | ||
1254 | bool has_80211h_pwr = false, has_cisco_pwr = false; | ||
1255 | int chan_pwr = 0, pwr_reduction_80211h = 0; | ||
1256 | int pwr_level_cisco, pwr_level_80211h; | ||
1257 | int new_ap_level; | ||
1258 | |||
1259 | if (country_ie && pwr_constr_ie && | ||
1260 | mgmt->u.probe_resp.capab_info & | ||
1261 | cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) { | ||
1262 | has_80211h_pwr = ieee80211_find_80211h_pwr_constr( | ||
1263 | sdata, channel, country_ie, country_ie_len, | ||
1264 | pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h); | ||
1265 | pwr_level_80211h = | ||
1266 | max_t(int, 0, chan_pwr - pwr_reduction_80211h); | ||
1267 | } | ||
1268 | |||
1269 | if (cisco_dtpc_ie) { | ||
1270 | ieee80211_find_cisco_dtpc( | ||
1271 | sdata, channel, cisco_dtpc_ie, &pwr_level_cisco); | ||
1272 | has_cisco_pwr = true; | ||
1273 | } | ||
1274 | |||
1275 | if (!has_80211h_pwr && !has_cisco_pwr) | ||
1222 | return 0; | 1276 | return 0; |
1223 | 1277 | ||
1224 | new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem); | 1278 | /* If we have both 802.11h and Cisco DTPC, apply both limits |
1279 | * by picking the smallest of the two power levels advertised. | ||
1280 | */ | ||
1281 | if (has_80211h_pwr && | ||
1282 | (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) { | ||
1283 | sdata_info(sdata, | ||
1284 | "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", | ||
1285 | pwr_level_80211h, chan_pwr, pwr_reduction_80211h, | ||
1286 | sdata->u.mgd.bssid); | ||
1287 | new_ap_level = pwr_level_80211h; | ||
1288 | } else { /* has_cisco_pwr is always true here. */ | ||
1289 | sdata_info(sdata, | ||
1290 | "Limiting TX power to %d dBm as advertised by %pM\n", | ||
1291 | pwr_level_cisco, sdata->u.mgd.bssid); | ||
1292 | new_ap_level = pwr_level_cisco; | ||
1293 | } | ||
1225 | 1294 | ||
1226 | if (sdata->ap_power_level == new_ap_level) | 1295 | if (sdata->ap_power_level == new_ap_level) |
1227 | return 0; | 1296 | return 0; |
1228 | 1297 | ||
1229 | sdata_info(sdata, | ||
1230 | "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", | ||
1231 | new_ap_level, chan_pwr, *pwr_constr_elem, | ||
1232 | sdata->u.mgd.bssid); | ||
1233 | sdata->ap_power_level = new_ap_level; | 1298 | sdata->ap_power_level = new_ap_level; |
1234 | if (__ieee80211_recalc_txpower(sdata)) | 1299 | if (__ieee80211_recalc_txpower(sdata)) |
1235 | return BSS_CHANGED_TXPOWER; | 1300 | return BSS_CHANGED_TXPOWER; |
@@ -2752,6 +2817,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, | |||
2752 | struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; | 2817 | struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; |
2753 | u16 capab_info, status_code, aid; | 2818 | u16 capab_info, status_code, aid; |
2754 | struct ieee802_11_elems elems; | 2819 | struct ieee802_11_elems elems; |
2820 | int ac, uapsd_queues = -1; | ||
2755 | u8 *pos; | 2821 | u8 *pos; |
2756 | bool reassoc; | 2822 | bool reassoc; |
2757 | struct cfg80211_bss *bss; | 2823 | struct cfg80211_bss *bss; |
@@ -2821,9 +2887,15 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, | |||
2821 | * is set can cause the interface to go idle | 2887 | * is set can cause the interface to go idle |
2822 | */ | 2888 | */ |
2823 | ieee80211_destroy_assoc_data(sdata, true); | 2889 | ieee80211_destroy_assoc_data(sdata, true); |
2890 | |||
2891 | /* get uapsd queues configuration */ | ||
2892 | uapsd_queues = 0; | ||
2893 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) | ||
2894 | if (sdata->tx_conf[ac].uapsd) | ||
2895 | uapsd_queues |= BIT(ac); | ||
2824 | } | 2896 | } |
2825 | 2897 | ||
2826 | cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len); | 2898 | cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len, uapsd_queues); |
2827 | } | 2899 | } |
2828 | 2900 | ||
2829 | static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | 2901 | static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, |
@@ -2893,7 +2965,9 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, | |||
2893 | /* | 2965 | /* |
2894 | * This is the canonical list of information elements we care about, | 2966 | * This is the canonical list of information elements we care about, |
2895 | * the filter code also gives us all changes to the Microsoft OUI | 2967 | * the filter code also gives us all changes to the Microsoft OUI |
2896 | * (00:50:F2) vendor IE which is used for WMM which we need to track. | 2968 | * (00:50:F2) vendor IE which is used for WMM which we need to track, |
2969 | * as well as the DTPC IE (part of the Cisco OUI) used for signaling | ||
2970 | * changes to requested client power. | ||
2897 | * | 2971 | * |
2898 | * We implement beacon filtering in software since that means we can | 2972 | * We implement beacon filtering in software since that means we can |
2899 | * avoid processing the frame here and in cfg80211, and userspace | 2973 | * avoid processing the frame here and in cfg80211, and userspace |
@@ -3199,13 +3273,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
3199 | rx_status->band, true); | 3273 | rx_status->band, true); |
3200 | mutex_unlock(&local->sta_mtx); | 3274 | mutex_unlock(&local->sta_mtx); |
3201 | 3275 | ||
3202 | if (elems.country_elem && elems.pwr_constr_elem && | 3276 | changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt, |
3203 | mgmt->u.probe_resp.capab_info & | 3277 | elems.country_elem, |
3204 | cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) | 3278 | elems.country_elem_len, |
3205 | changed |= ieee80211_handle_pwr_constr(sdata, chan, | 3279 | elems.pwr_constr_elem, |
3206 | elems.country_elem, | 3280 | elems.cisco_dtpc_elem); |
3207 | elems.country_elem_len, | ||
3208 | elems.pwr_constr_elem); | ||
3209 | 3281 | ||
3210 | ieee80211_bss_info_change_notify(sdata, changed); | 3282 | ieee80211_bss_info_change_notify(sdata, changed); |
3211 | } | 3283 | } |
@@ -3733,7 +3805,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
3733 | ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len; | 3805 | ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len; |
3734 | ifmgd->p2p_noa_index = -1; | 3806 | ifmgd->p2p_noa_index = -1; |
3735 | 3807 | ||
3736 | if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) | 3808 | if (sdata->local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS) |
3737 | ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC; | 3809 | ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC; |
3738 | else | 3810 | else |
3739 | ifmgd->req_smps = IEEE80211_SMPS_OFF; | 3811 | ifmgd->req_smps = IEEE80211_SMPS_OFF; |
@@ -4408,6 +4480,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
4408 | ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED; | 4480 | ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED; |
4409 | } | 4481 | } |
4410 | 4482 | ||
4483 | if (req->flags & ASSOC_REQ_USE_RRM) | ||
4484 | ifmgd->flags |= IEEE80211_STA_ENABLE_RRM; | ||
4485 | else | ||
4486 | ifmgd->flags &= ~IEEE80211_STA_ENABLE_RRM; | ||
4487 | |||
4411 | if (req->crypto.control_port) | 4488 | if (req->crypto.control_port) |
4412 | ifmgd->flags |= IEEE80211_STA_CONTROL_PORT; | 4489 | ifmgd->flags |= IEEE80211_STA_CONTROL_PORT; |
4413 | else | 4490 | else |
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 1c1469c36dca..2baa7ed8789d 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c | |||
@@ -75,7 +75,7 @@ minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) | |||
75 | { | 75 | { |
76 | int j = MAX_THR_RATES; | 76 | int j = MAX_THR_RATES; |
77 | 77 | ||
78 | while (j > 0 && mi->r[i].cur_tp > mi->r[tp_list[j - 1]].cur_tp) | 78 | while (j > 0 && mi->r[i].stats.cur_tp > mi->r[tp_list[j - 1]].stats.cur_tp) |
79 | j--; | 79 | j--; |
80 | if (j < MAX_THR_RATES - 1) | 80 | if (j < MAX_THR_RATES - 1) |
81 | memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); | 81 | memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); |
@@ -92,7 +92,7 @@ minstrel_set_rate(struct minstrel_sta_info *mi, struct ieee80211_sta_rates *rate | |||
92 | ratetbl->rate[offset].idx = r->rix; | 92 | ratetbl->rate[offset].idx = r->rix; |
93 | ratetbl->rate[offset].count = r->adjusted_retry_count; | 93 | ratetbl->rate[offset].count = r->adjusted_retry_count; |
94 | ratetbl->rate[offset].count_cts = r->retry_count_cts; | 94 | ratetbl->rate[offset].count_cts = r->retry_count_cts; |
95 | ratetbl->rate[offset].count_rts = r->retry_count_rtscts; | 95 | ratetbl->rate[offset].count_rts = r->stats.retry_count_rtscts; |
96 | } | 96 | } |
97 | 97 | ||
98 | static void | 98 | static void |
@@ -140,44 +140,46 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) | |||
140 | 140 | ||
141 | for (i = 0; i < mi->n_rates; i++) { | 141 | for (i = 0; i < mi->n_rates; i++) { |
142 | struct minstrel_rate *mr = &mi->r[i]; | 142 | struct minstrel_rate *mr = &mi->r[i]; |
143 | struct minstrel_rate_stats *mrs = &mi->r[i].stats; | ||
143 | 144 | ||
144 | usecs = mr->perfect_tx_time; | 145 | usecs = mr->perfect_tx_time; |
145 | if (!usecs) | 146 | if (!usecs) |
146 | usecs = 1000000; | 147 | usecs = 1000000; |
147 | 148 | ||
148 | if (unlikely(mr->attempts > 0)) { | 149 | if (unlikely(mrs->attempts > 0)) { |
149 | mr->sample_skipped = 0; | 150 | mrs->sample_skipped = 0; |
150 | mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts); | 151 | mrs->cur_prob = MINSTREL_FRAC(mrs->success, |
151 | mr->succ_hist += mr->success; | 152 | mrs->attempts); |
152 | mr->att_hist += mr->attempts; | 153 | mrs->succ_hist += mrs->success; |
153 | mr->probability = minstrel_ewma(mr->probability, | 154 | mrs->att_hist += mrs->attempts; |
154 | mr->cur_prob, | 155 | mrs->probability = minstrel_ewma(mrs->probability, |
155 | EWMA_LEVEL); | 156 | mrs->cur_prob, |
157 | EWMA_LEVEL); | ||
156 | } else | 158 | } else |
157 | mr->sample_skipped++; | 159 | mrs->sample_skipped++; |
158 | 160 | ||
159 | mr->last_success = mr->success; | 161 | mrs->last_success = mrs->success; |
160 | mr->last_attempts = mr->attempts; | 162 | mrs->last_attempts = mrs->attempts; |
161 | mr->success = 0; | 163 | mrs->success = 0; |
162 | mr->attempts = 0; | 164 | mrs->attempts = 0; |
163 | 165 | ||
164 | /* Update throughput per rate, reset thr. below 10% success */ | 166 | /* Update throughput per rate, reset thr. below 10% success */ |
165 | if (mr->probability < MINSTREL_FRAC(10, 100)) | 167 | if (mrs->probability < MINSTREL_FRAC(10, 100)) |
166 | mr->cur_tp = 0; | 168 | mrs->cur_tp = 0; |
167 | else | 169 | else |
168 | mr->cur_tp = mr->probability * (1000000 / usecs); | 170 | mrs->cur_tp = mrs->probability * (1000000 / usecs); |
169 | 171 | ||
170 | /* Sample less often below the 10% chance of success. | 172 | /* Sample less often below the 10% chance of success. |
171 | * Sample less often above the 95% chance of success. */ | 173 | * Sample less often above the 95% chance of success. */ |
172 | if (mr->probability > MINSTREL_FRAC(95, 100) || | 174 | if (mrs->probability > MINSTREL_FRAC(95, 100) || |
173 | mr->probability < MINSTREL_FRAC(10, 100)) { | 175 | mrs->probability < MINSTREL_FRAC(10, 100)) { |
174 | mr->adjusted_retry_count = mr->retry_count >> 1; | 176 | mr->adjusted_retry_count = mrs->retry_count >> 1; |
175 | if (mr->adjusted_retry_count > 2) | 177 | if (mr->adjusted_retry_count > 2) |
176 | mr->adjusted_retry_count = 2; | 178 | mr->adjusted_retry_count = 2; |
177 | mr->sample_limit = 4; | 179 | mr->sample_limit = 4; |
178 | } else { | 180 | } else { |
179 | mr->sample_limit = -1; | 181 | mr->sample_limit = -1; |
180 | mr->adjusted_retry_count = mr->retry_count; | 182 | mr->adjusted_retry_count = mrs->retry_count; |
181 | } | 183 | } |
182 | if (!mr->adjusted_retry_count) | 184 | if (!mr->adjusted_retry_count) |
183 | mr->adjusted_retry_count = 2; | 185 | mr->adjusted_retry_count = 2; |
@@ -190,11 +192,11 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) | |||
190 | * choose the maximum throughput rate as max_prob_rate | 192 | * choose the maximum throughput rate as max_prob_rate |
191 | * (2) if all success probabilities < 95%, the rate with | 193 | * (2) if all success probabilities < 95%, the rate with |
192 | * highest success probability is choosen as max_prob_rate */ | 194 | * highest success probability is choosen as max_prob_rate */ |
193 | if (mr->probability >= MINSTREL_FRAC(95, 100)) { | 195 | if (mrs->probability >= MINSTREL_FRAC(95, 100)) { |
194 | if (mr->cur_tp >= mi->r[tmp_prob_rate].cur_tp) | 196 | if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp) |
195 | tmp_prob_rate = i; | 197 | tmp_prob_rate = i; |
196 | } else { | 198 | } else { |
197 | if (mr->probability >= mi->r[tmp_prob_rate].probability) | 199 | if (mrs->probability >= mi->r[tmp_prob_rate].stats.probability) |
198 | tmp_prob_rate = i; | 200 | tmp_prob_rate = i; |
199 | } | 201 | } |
200 | } | 202 | } |
@@ -240,14 +242,14 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, | |||
240 | if (ndx < 0) | 242 | if (ndx < 0) |
241 | continue; | 243 | continue; |
242 | 244 | ||
243 | mi->r[ndx].attempts += ar[i].count; | 245 | mi->r[ndx].stats.attempts += ar[i].count; |
244 | 246 | ||
245 | if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) | 247 | if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0)) |
246 | mi->r[ndx].success += success; | 248 | mi->r[ndx].stats.success += success; |
247 | } | 249 | } |
248 | 250 | ||
249 | if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0)) | 251 | if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0)) |
250 | mi->sample_count++; | 252 | mi->sample_packets++; |
251 | 253 | ||
252 | if (mi->sample_deferred > 0) | 254 | if (mi->sample_deferred > 0) |
253 | mi->sample_deferred--; | 255 | mi->sample_deferred--; |
@@ -265,7 +267,7 @@ minstrel_get_retry_count(struct minstrel_rate *mr, | |||
265 | unsigned int retry = mr->adjusted_retry_count; | 267 | unsigned int retry = mr->adjusted_retry_count; |
266 | 268 | ||
267 | if (info->control.use_rts) | 269 | if (info->control.use_rts) |
268 | retry = max(2U, min(mr->retry_count_rtscts, retry)); | 270 | retry = max(2U, min(mr->stats.retry_count_rtscts, retry)); |
269 | else if (info->control.use_cts_prot) | 271 | else if (info->control.use_cts_prot) |
270 | retry = max(2U, min(mr->retry_count_cts, retry)); | 272 | retry = max(2U, min(mr->retry_count_cts, retry)); |
271 | return retry; | 273 | return retry; |
@@ -317,15 +319,15 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, | |||
317 | sampling_ratio = mp->lookaround_rate; | 319 | sampling_ratio = mp->lookaround_rate; |
318 | 320 | ||
319 | /* increase sum packet counter */ | 321 | /* increase sum packet counter */ |
320 | mi->packet_count++; | 322 | mi->total_packets++; |
321 | 323 | ||
322 | #ifdef CONFIG_MAC80211_DEBUGFS | 324 | #ifdef CONFIG_MAC80211_DEBUGFS |
323 | if (mp->fixed_rate_idx != -1) | 325 | if (mp->fixed_rate_idx != -1) |
324 | return; | 326 | return; |
325 | #endif | 327 | #endif |
326 | 328 | ||
327 | delta = (mi->packet_count * sampling_ratio / 100) - | 329 | delta = (mi->total_packets * sampling_ratio / 100) - |
328 | (mi->sample_count + mi->sample_deferred / 2); | 330 | (mi->sample_packets + mi->sample_deferred / 2); |
329 | 331 | ||
330 | /* delta < 0: no sampling required */ | 332 | /* delta < 0: no sampling required */ |
331 | prev_sample = mi->prev_sample; | 333 | prev_sample = mi->prev_sample; |
@@ -333,10 +335,10 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, | |||
333 | if (delta < 0 || (!mrr_capable && prev_sample)) | 335 | if (delta < 0 || (!mrr_capable && prev_sample)) |
334 | return; | 336 | return; |
335 | 337 | ||
336 | if (mi->packet_count >= 10000) { | 338 | if (mi->total_packets >= 10000) { |
337 | mi->sample_deferred = 0; | 339 | mi->sample_deferred = 0; |
338 | mi->sample_count = 0; | 340 | mi->sample_packets = 0; |
339 | mi->packet_count = 0; | 341 | mi->total_packets = 0; |
340 | } else if (delta > mi->n_rates * 2) { | 342 | } else if (delta > mi->n_rates * 2) { |
341 | /* With multi-rate retry, not every planned sample | 343 | /* With multi-rate retry, not every planned sample |
342 | * attempt actually gets used, due to the way the retry | 344 | * attempt actually gets used, due to the way the retry |
@@ -347,7 +349,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, | |||
347 | * starts getting worse, minstrel would start bursting | 349 | * starts getting worse, minstrel would start bursting |
348 | * out lots of sampling frames, which would result | 350 | * out lots of sampling frames, which would result |
349 | * in a large throughput loss. */ | 351 | * in a large throughput loss. */ |
350 | mi->sample_count += (delta - mi->n_rates * 2); | 352 | mi->sample_packets += (delta - mi->n_rates * 2); |
351 | } | 353 | } |
352 | 354 | ||
353 | /* get next random rate sample */ | 355 | /* get next random rate sample */ |
@@ -361,7 +363,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, | |||
361 | */ | 363 | */ |
362 | if (mrr_capable && | 364 | if (mrr_capable && |
363 | msr->perfect_tx_time > mr->perfect_tx_time && | 365 | msr->perfect_tx_time > mr->perfect_tx_time && |
364 | msr->sample_skipped < 20) { | 366 | msr->stats.sample_skipped < 20) { |
365 | /* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark | 367 | /* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark |
366 | * packets that have the sampling rate deferred to the | 368 | * packets that have the sampling rate deferred to the |
367 | * second MRR stage. Increase the sample counter only | 369 | * second MRR stage. Increase the sample counter only |
@@ -375,7 +377,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, | |||
375 | if (!msr->sample_limit != 0) | 377 | if (!msr->sample_limit != 0) |
376 | return; | 378 | return; |
377 | 379 | ||
378 | mi->sample_count++; | 380 | mi->sample_packets++; |
379 | if (msr->sample_limit > 0) | 381 | if (msr->sample_limit > 0) |
380 | msr->sample_limit--; | 382 | msr->sample_limit--; |
381 | } | 383 | } |
@@ -384,7 +386,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, | |||
384 | * has a probability of >95%, we shouldn't be attempting | 386 | * has a probability of >95%, we shouldn't be attempting |
385 | * to use it, as this only wastes precious airtime */ | 387 | * to use it, as this only wastes precious airtime */ |
386 | if (!mrr_capable && | 388 | if (!mrr_capable && |
387 | (mi->r[ndx].probability > MINSTREL_FRAC(95, 100))) | 389 | (mi->r[ndx].stats.probability > MINSTREL_FRAC(95, 100))) |
388 | return; | 390 | return; |
389 | 391 | ||
390 | mi->prev_sample = true; | 392 | mi->prev_sample = true; |
@@ -459,6 +461,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, | |||
459 | 461 | ||
460 | for (i = 0; i < sband->n_bitrates; i++) { | 462 | for (i = 0; i < sband->n_bitrates; i++) { |
461 | struct minstrel_rate *mr = &mi->r[n]; | 463 | struct minstrel_rate *mr = &mi->r[n]; |
464 | struct minstrel_rate_stats *mrs = &mi->r[n].stats; | ||
462 | unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0; | 465 | unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0; |
463 | unsigned int tx_time_single; | 466 | unsigned int tx_time_single; |
464 | unsigned int cw = mp->cw_min; | 467 | unsigned int cw = mp->cw_min; |
@@ -471,6 +474,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, | |||
471 | 474 | ||
472 | n++; | 475 | n++; |
473 | memset(mr, 0, sizeof(*mr)); | 476 | memset(mr, 0, sizeof(*mr)); |
477 | memset(mrs, 0, sizeof(*mrs)); | ||
474 | 478 | ||
475 | mr->rix = i; | 479 | mr->rix = i; |
476 | shift = ieee80211_chandef_get_shift(chandef); | 480 | shift = ieee80211_chandef_get_shift(chandef); |
@@ -482,9 +486,9 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, | |||
482 | /* calculate maximum number of retransmissions before | 486 | /* calculate maximum number of retransmissions before |
483 | * fallback (based on maximum segment size) */ | 487 | * fallback (based on maximum segment size) */ |
484 | mr->sample_limit = -1; | 488 | mr->sample_limit = -1; |
485 | mr->retry_count = 1; | 489 | mrs->retry_count = 1; |
486 | mr->retry_count_cts = 1; | 490 | mr->retry_count_cts = 1; |
487 | mr->retry_count_rtscts = 1; | 491 | mrs->retry_count_rtscts = 1; |
488 | tx_time = mr->perfect_tx_time + mi->sp_ack_dur; | 492 | tx_time = mr->perfect_tx_time + mi->sp_ack_dur; |
489 | do { | 493 | do { |
490 | /* add one retransmission */ | 494 | /* add one retransmission */ |
@@ -501,13 +505,13 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, | |||
501 | (mr->retry_count_cts < mp->max_retry)) | 505 | (mr->retry_count_cts < mp->max_retry)) |
502 | mr->retry_count_cts++; | 506 | mr->retry_count_cts++; |
503 | if ((tx_time_rtscts < mp->segment_size) && | 507 | if ((tx_time_rtscts < mp->segment_size) && |
504 | (mr->retry_count_rtscts < mp->max_retry)) | 508 | (mrs->retry_count_rtscts < mp->max_retry)) |
505 | mr->retry_count_rtscts++; | 509 | mrs->retry_count_rtscts++; |
506 | } while ((tx_time < mp->segment_size) && | 510 | } while ((tx_time < mp->segment_size) && |
507 | (++mr->retry_count < mp->max_retry)); | 511 | (++mr->stats.retry_count < mp->max_retry)); |
508 | mr->adjusted_retry_count = mr->retry_count; | 512 | mr->adjusted_retry_count = mrs->retry_count; |
509 | if (!(sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)) | 513 | if (!(sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)) |
510 | mr->retry_count_cts = mr->retry_count; | 514 | mr->retry_count_cts = mrs->retry_count; |
511 | } | 515 | } |
512 | 516 | ||
513 | for (i = n; i < sband->n_bitrates; i++) { | 517 | for (i = n; i < sband->n_bitrates; i++) { |
@@ -665,7 +669,7 @@ static u32 minstrel_get_expected_throughput(void *priv_sta) | |||
665 | /* convert pkt per sec in kbps (1200 is the average pkt size used for | 669 | /* convert pkt per sec in kbps (1200 is the average pkt size used for |
666 | * computing cur_tp | 670 | * computing cur_tp |
667 | */ | 671 | */ |
668 | return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024; | 672 | return MINSTREL_TRUNC(mi->r[idx].stats.cur_tp) * 1200 * 8 / 1024; |
669 | } | 673 | } |
670 | 674 | ||
671 | const struct rate_control_ops mac80211_minstrel = { | 675 | const struct rate_control_ops mac80211_minstrel = { |
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 046d1bd598a8..97eca86a4af0 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h | |||
@@ -31,6 +31,27 @@ minstrel_ewma(int old, int new, int weight) | |||
31 | return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV; | 31 | return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV; |
32 | } | 32 | } |
33 | 33 | ||
34 | struct minstrel_rate_stats { | ||
35 | /* current / last sampling period attempts/success counters */ | ||
36 | unsigned int attempts, last_attempts; | ||
37 | unsigned int success, last_success; | ||
38 | |||
39 | /* total attempts/success counters */ | ||
40 | u64 att_hist, succ_hist; | ||
41 | |||
42 | /* current throughput */ | ||
43 | unsigned int cur_tp; | ||
44 | |||
45 | /* packet delivery probabilities */ | ||
46 | unsigned int cur_prob, probability; | ||
47 | |||
48 | /* maximum retry counts */ | ||
49 | unsigned int retry_count; | ||
50 | unsigned int retry_count_rtscts; | ||
51 | |||
52 | u8 sample_skipped; | ||
53 | bool retry_updated; | ||
54 | }; | ||
34 | 55 | ||
35 | struct minstrel_rate { | 56 | struct minstrel_rate { |
36 | int bitrate; | 57 | int bitrate; |
@@ -40,26 +61,10 @@ struct minstrel_rate { | |||
40 | unsigned int ack_time; | 61 | unsigned int ack_time; |
41 | 62 | ||
42 | int sample_limit; | 63 | int sample_limit; |
43 | unsigned int retry_count; | ||
44 | unsigned int retry_count_cts; | 64 | unsigned int retry_count_cts; |
45 | unsigned int retry_count_rtscts; | ||
46 | unsigned int adjusted_retry_count; | 65 | unsigned int adjusted_retry_count; |
47 | 66 | ||
48 | u32 success; | 67 | struct minstrel_rate_stats stats; |
49 | u32 attempts; | ||
50 | u32 last_attempts; | ||
51 | u32 last_success; | ||
52 | u8 sample_skipped; | ||
53 | |||
54 | /* parts per thousand */ | ||
55 | u32 cur_prob; | ||
56 | u32 probability; | ||
57 | |||
58 | /* per-rate throughput */ | ||
59 | u32 cur_tp; | ||
60 | |||
61 | u64 succ_hist; | ||
62 | u64 att_hist; | ||
63 | }; | 68 | }; |
64 | 69 | ||
65 | struct minstrel_sta_info { | 70 | struct minstrel_sta_info { |
@@ -73,8 +78,8 @@ struct minstrel_sta_info { | |||
73 | 78 | ||
74 | u8 max_tp_rate[MAX_THR_RATES]; | 79 | u8 max_tp_rate[MAX_THR_RATES]; |
75 | u8 max_prob_rate; | 80 | u8 max_prob_rate; |
76 | unsigned int packet_count; | 81 | unsigned int total_packets; |
77 | unsigned int sample_count; | 82 | unsigned int sample_packets; |
78 | int sample_deferred; | 83 | int sample_deferred; |
79 | 84 | ||
80 | unsigned int sample_row; | 85 | unsigned int sample_row; |
diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index fd0b9ca1570e..edde723f9f00 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c | |||
@@ -72,6 +72,7 @@ minstrel_stats_open(struct inode *inode, struct file *file) | |||
72 | "this succ/attempt success attempts\n"); | 72 | "this succ/attempt success attempts\n"); |
73 | for (i = 0; i < mi->n_rates; i++) { | 73 | for (i = 0; i < mi->n_rates; i++) { |
74 | struct minstrel_rate *mr = &mi->r[i]; | 74 | struct minstrel_rate *mr = &mi->r[i]; |
75 | struct minstrel_rate_stats *mrs = &mi->r[i].stats; | ||
75 | 76 | ||
76 | *(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' '; | 77 | *(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' '; |
77 | *(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' '; | 78 | *(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' '; |
@@ -81,24 +82,24 @@ minstrel_stats_open(struct inode *inode, struct file *file) | |||
81 | p += sprintf(p, "%3u%s", mr->bitrate / 2, | 82 | p += sprintf(p, "%3u%s", mr->bitrate / 2, |
82 | (mr->bitrate & 1 ? ".5" : " ")); | 83 | (mr->bitrate & 1 ? ".5" : " ")); |
83 | 84 | ||
84 | tp = MINSTREL_TRUNC(mr->cur_tp / 10); | 85 | tp = MINSTREL_TRUNC(mrs->cur_tp / 10); |
85 | prob = MINSTREL_TRUNC(mr->cur_prob * 1000); | 86 | prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); |
86 | eprob = MINSTREL_TRUNC(mr->probability * 1000); | 87 | eprob = MINSTREL_TRUNC(mrs->probability * 1000); |
87 | 88 | ||
88 | p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " | 89 | p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " |
89 | " %3u(%3u) %8llu %8llu\n", | 90 | " %3u(%3u) %8llu %8llu\n", |
90 | tp / 10, tp % 10, | 91 | tp / 10, tp % 10, |
91 | eprob / 10, eprob % 10, | 92 | eprob / 10, eprob % 10, |
92 | prob / 10, prob % 10, | 93 | prob / 10, prob % 10, |
93 | mr->last_success, | 94 | mrs->last_success, |
94 | mr->last_attempts, | 95 | mrs->last_attempts, |
95 | (unsigned long long)mr->succ_hist, | 96 | (unsigned long long)mrs->succ_hist, |
96 | (unsigned long long)mr->att_hist); | 97 | (unsigned long long)mrs->att_hist); |
97 | } | 98 | } |
98 | p += sprintf(p, "\nTotal packet count:: ideal %d " | 99 | p += sprintf(p, "\nTotal packet count:: ideal %d " |
99 | "lookaround %d\n\n", | 100 | "lookaround %d\n\n", |
100 | mi->packet_count - mi->sample_count, | 101 | mi->total_packets - mi->sample_packets, |
101 | mi->sample_count); | 102 | mi->sample_packets); |
102 | ms->len = p - ms->buf; | 103 | ms->len = p - ms->buf; |
103 | 104 | ||
104 | return 0; | 105 | return 0; |
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 85c1e74b7714..df90ce2db00c 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c | |||
@@ -135,7 +135,7 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); | |||
135 | static int | 135 | static int |
136 | minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) | 136 | minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) |
137 | { | 137 | { |
138 | return GROUP_IDX((rate->idx / 8) + 1, | 138 | return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1, |
139 | !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), | 139 | !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), |
140 | !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); | 140 | !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); |
141 | } | 141 | } |
@@ -233,12 +233,151 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) | |||
233 | } | 233 | } |
234 | 234 | ||
235 | /* | 235 | /* |
236 | * Find & sort topmost throughput rates | ||
237 | * | ||
238 | * If multiple rates provide equal throughput the sorting is based on their | ||
239 | * current success probability. Higher success probability is preferred among | ||
240 | * MCS groups, CCK rates do not provide aggregation and are therefore at last. | ||
241 | */ | ||
242 | static void | ||
243 | minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index, | ||
244 | u8 *tp_list) | ||
245 | { | ||
246 | int cur_group, cur_idx, cur_thr, cur_prob; | ||
247 | int tmp_group, tmp_idx, tmp_thr, tmp_prob; | ||
248 | int j = MAX_THR_RATES; | ||
249 | |||
250 | cur_group = index / MCS_GROUP_RATES; | ||
251 | cur_idx = index % MCS_GROUP_RATES; | ||
252 | cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp; | ||
253 | cur_prob = mi->groups[cur_group].rates[cur_idx].probability; | ||
254 | |||
255 | tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; | ||
256 | tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; | ||
257 | tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | ||
258 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; | ||
259 | |||
260 | while (j > 0 && (cur_thr > tmp_thr || | ||
261 | (cur_thr == tmp_thr && cur_prob > tmp_prob))) { | ||
262 | j--; | ||
263 | tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; | ||
264 | tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; | ||
265 | tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | ||
266 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; | ||
267 | } | ||
268 | |||
269 | if (j < MAX_THR_RATES - 1) { | ||
270 | memmove(&tp_list[j + 1], &tp_list[j], (sizeof(*tp_list) * | ||
271 | (MAX_THR_RATES - (j + 1)))); | ||
272 | } | ||
273 | if (j < MAX_THR_RATES) | ||
274 | tp_list[j] = index; | ||
275 | } | ||
276 | |||
277 | /* | ||
278 | * Find and set the topmost probability rate per sta and per group | ||
279 | */ | ||
280 | static void | ||
281 | minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index) | ||
282 | { | ||
283 | struct minstrel_mcs_group_data *mg; | ||
284 | struct minstrel_rate_stats *mr; | ||
285 | int tmp_group, tmp_idx, tmp_tp, tmp_prob, max_tp_group; | ||
286 | |||
287 | mg = &mi->groups[index / MCS_GROUP_RATES]; | ||
288 | mr = &mg->rates[index % MCS_GROUP_RATES]; | ||
289 | |||
290 | tmp_group = mi->max_prob_rate / MCS_GROUP_RATES; | ||
291 | tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES; | ||
292 | tmp_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | ||
293 | tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; | ||
294 | |||
295 | /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from | ||
296 | * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */ | ||
297 | max_tp_group = mi->max_tp_rate[0] / MCS_GROUP_RATES; | ||
298 | if((index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) && | ||
299 | (max_tp_group != MINSTREL_CCK_GROUP)) | ||
300 | return; | ||
301 | |||
302 | if (mr->probability > MINSTREL_FRAC(75, 100)) { | ||
303 | if (mr->cur_tp > tmp_tp) | ||
304 | mi->max_prob_rate = index; | ||
305 | if (mr->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp) | ||
306 | mg->max_group_prob_rate = index; | ||
307 | } else { | ||
308 | if (mr->probability > tmp_prob) | ||
309 | mi->max_prob_rate = index; | ||
310 | if (mr->probability > mg->rates[mg->max_group_prob_rate].probability) | ||
311 | mg->max_group_prob_rate = index; | ||
312 | } | ||
313 | } | ||
314 | |||
315 | |||
316 | /* | ||
317 | * Assign new rate set per sta and use CCK rates only if the fastest | ||
318 | * rate (max_tp_rate[0]) is from CCK group. This prohibits such sorted | ||
319 | * rate sets where MCS and CCK rates are mixed, because CCK rates can | ||
320 | * not use aggregation. | ||
321 | */ | ||
322 | static void | ||
323 | minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, | ||
324 | u8 tmp_mcs_tp_rate[MAX_THR_RATES], | ||
325 | u8 tmp_cck_tp_rate[MAX_THR_RATES]) | ||
326 | { | ||
327 | unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp; | ||
328 | int i; | ||
329 | |||
330 | tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES; | ||
331 | tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES; | ||
332 | tmp_cck_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | ||
333 | |||
334 | tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES; | ||
335 | tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES; | ||
336 | tmp_mcs_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; | ||
337 | |||
338 | if (tmp_cck_tp > tmp_mcs_tp) { | ||
339 | for(i = 0; i < MAX_THR_RATES; i++) { | ||
340 | minstrel_ht_sort_best_tp_rates(mi, tmp_cck_tp_rate[i], | ||
341 | tmp_mcs_tp_rate); | ||
342 | } | ||
343 | } | ||
344 | |||
345 | } | ||
346 | |||
347 | /* | ||
348 | * Try to increase robustness of max_prob rate by decrease number of | ||
349 | * streams if possible. | ||
350 | */ | ||
351 | static inline void | ||
352 | minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) | ||
353 | { | ||
354 | struct minstrel_mcs_group_data *mg; | ||
355 | struct minstrel_rate_stats *mr; | ||
356 | int tmp_max_streams, group; | ||
357 | int tmp_tp = 0; | ||
358 | |||
359 | tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / | ||
360 | MCS_GROUP_RATES].streams; | ||
361 | for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { | ||
362 | mg = &mi->groups[group]; | ||
363 | if (!mg->supported || group == MINSTREL_CCK_GROUP) | ||
364 | continue; | ||
365 | mr = minstrel_get_ratestats(mi, mg->max_group_prob_rate); | ||
366 | if (tmp_tp < mr->cur_tp && | ||
367 | (minstrel_mcs_groups[group].streams < tmp_max_streams)) { | ||
368 | mi->max_prob_rate = mg->max_group_prob_rate; | ||
369 | tmp_tp = mr->cur_tp; | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | |||
374 | /* | ||
236 | * Update rate statistics and select new primary rates | 375 | * Update rate statistics and select new primary rates |
237 | * | 376 | * |
238 | * Rules for rate selection: | 377 | * Rules for rate selection: |
239 | * - max_prob_rate must use only one stream, as a tradeoff between delivery | 378 | * - max_prob_rate must use only one stream, as a tradeoff between delivery |
240 | * probability and throughput during strong fluctuations | 379 | * probability and throughput during strong fluctuations |
241 | * - as long as the max prob rate has a probability of more than 3/4, pick | 380 | * - as long as the max prob rate has a probability of more than 75%, pick |
242 | * higher throughput rates, even if the probablity is a bit lower | 381 | * higher throughput rates, even if the probablity is a bit lower |
243 | */ | 382 | */ |
244 | static void | 383 | static void |
@@ -246,9 +385,9 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
246 | { | 385 | { |
247 | struct minstrel_mcs_group_data *mg; | 386 | struct minstrel_mcs_group_data *mg; |
248 | struct minstrel_rate_stats *mr; | 387 | struct minstrel_rate_stats *mr; |
249 | int cur_prob, cur_prob_tp, cur_tp, cur_tp2; | 388 | int group, i, j; |
250 | int group, i, index; | 389 | u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; |
251 | bool mi_rates_valid = false; | 390 | u8 tmp_cck_tp_rate[MAX_THR_RATES], index; |
252 | 391 | ||
253 | if (mi->ampdu_packets > 0) { | 392 | if (mi->ampdu_packets > 0) { |
254 | mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, | 393 | mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, |
@@ -260,13 +399,14 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
260 | mi->sample_slow = 0; | 399 | mi->sample_slow = 0; |
261 | mi->sample_count = 0; | 400 | mi->sample_count = 0; |
262 | 401 | ||
263 | for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { | 402 | /* Initialize global rate indexes */ |
264 | bool mg_rates_valid = false; | 403 | for(j = 0; j < MAX_THR_RATES; j++){ |
404 | tmp_mcs_tp_rate[j] = 0; | ||
405 | tmp_cck_tp_rate[j] = 0; | ||
406 | } | ||
265 | 407 | ||
266 | cur_prob = 0; | 408 | /* Find best rate sets within all MCS groups*/ |
267 | cur_prob_tp = 0; | 409 | for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { |
268 | cur_tp = 0; | ||
269 | cur_tp2 = 0; | ||
270 | 410 | ||
271 | mg = &mi->groups[group]; | 411 | mg = &mi->groups[group]; |
272 | if (!mg->supported) | 412 | if (!mg->supported) |
@@ -274,24 +414,16 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
274 | 414 | ||
275 | mi->sample_count++; | 415 | mi->sample_count++; |
276 | 416 | ||
417 | /* (re)Initialize group rate indexes */ | ||
418 | for(j = 0; j < MAX_THR_RATES; j++) | ||
419 | tmp_group_tp_rate[j] = group; | ||
420 | |||
277 | for (i = 0; i < MCS_GROUP_RATES; i++) { | 421 | for (i = 0; i < MCS_GROUP_RATES; i++) { |
278 | if (!(mg->supported & BIT(i))) | 422 | if (!(mg->supported & BIT(i))) |
279 | continue; | 423 | continue; |
280 | 424 | ||
281 | index = MCS_GROUP_RATES * group + i; | 425 | index = MCS_GROUP_RATES * group + i; |
282 | 426 | ||
283 | /* initialize rates selections starting indexes */ | ||
284 | if (!mg_rates_valid) { | ||
285 | mg->max_tp_rate = mg->max_tp_rate2 = | ||
286 | mg->max_prob_rate = i; | ||
287 | if (!mi_rates_valid) { | ||
288 | mi->max_tp_rate = mi->max_tp_rate2 = | ||
289 | mi->max_prob_rate = index; | ||
290 | mi_rates_valid = true; | ||
291 | } | ||
292 | mg_rates_valid = true; | ||
293 | } | ||
294 | |||
295 | mr = &mg->rates[i]; | 427 | mr = &mg->rates[i]; |
296 | mr->retry_updated = false; | 428 | mr->retry_updated = false; |
297 | minstrel_calc_rate_ewma(mr); | 429 | minstrel_calc_rate_ewma(mr); |
@@ -300,82 +432,47 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
300 | if (!mr->cur_tp) | 432 | if (!mr->cur_tp) |
301 | continue; | 433 | continue; |
302 | 434 | ||
303 | if ((mr->cur_tp > cur_prob_tp && mr->probability > | 435 | /* Find max throughput rate set */ |
304 | MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) { | 436 | if (group != MINSTREL_CCK_GROUP) { |
305 | mg->max_prob_rate = index; | 437 | minstrel_ht_sort_best_tp_rates(mi, index, |
306 | cur_prob = mr->probability; | 438 | tmp_mcs_tp_rate); |
307 | cur_prob_tp = mr->cur_tp; | 439 | } else if (group == MINSTREL_CCK_GROUP) { |
308 | } | 440 | minstrel_ht_sort_best_tp_rates(mi, index, |
309 | 441 | tmp_cck_tp_rate); | |
310 | if (mr->cur_tp > cur_tp) { | ||
311 | swap(index, mg->max_tp_rate); | ||
312 | cur_tp = mr->cur_tp; | ||
313 | mr = minstrel_get_ratestats(mi, index); | ||
314 | } | ||
315 | |||
316 | if (index >= mg->max_tp_rate) | ||
317 | continue; | ||
318 | |||
319 | if (mr->cur_tp > cur_tp2) { | ||
320 | mg->max_tp_rate2 = index; | ||
321 | cur_tp2 = mr->cur_tp; | ||
322 | } | 442 | } |
323 | } | ||
324 | } | ||
325 | 443 | ||
326 | /* try to sample all available rates during each interval */ | 444 | /* Find max throughput rate set within a group */ |
327 | mi->sample_count *= 8; | 445 | minstrel_ht_sort_best_tp_rates(mi, index, |
446 | tmp_group_tp_rate); | ||
328 | 447 | ||
329 | cur_prob = 0; | 448 | /* Find max probability rate per group and global */ |
330 | cur_prob_tp = 0; | 449 | minstrel_ht_set_best_prob_rate(mi, index); |
331 | cur_tp = 0; | ||
332 | cur_tp2 = 0; | ||
333 | for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { | ||
334 | mg = &mi->groups[group]; | ||
335 | if (!mg->supported) | ||
336 | continue; | ||
337 | |||
338 | mr = minstrel_get_ratestats(mi, mg->max_tp_rate); | ||
339 | if (cur_tp < mr->cur_tp) { | ||
340 | mi->max_tp_rate2 = mi->max_tp_rate; | ||
341 | cur_tp2 = cur_tp; | ||
342 | mi->max_tp_rate = mg->max_tp_rate; | ||
343 | cur_tp = mr->cur_tp; | ||
344 | mi->max_prob_streams = minstrel_mcs_groups[group].streams - 1; | ||
345 | } | 450 | } |
346 | 451 | ||
347 | mr = minstrel_get_ratestats(mi, mg->max_tp_rate2); | 452 | memcpy(mg->max_group_tp_rate, tmp_group_tp_rate, |
348 | if (cur_tp2 < mr->cur_tp) { | 453 | sizeof(mg->max_group_tp_rate)); |
349 | mi->max_tp_rate2 = mg->max_tp_rate2; | ||
350 | cur_tp2 = mr->cur_tp; | ||
351 | } | ||
352 | } | 454 | } |
353 | 455 | ||
354 | if (mi->max_prob_streams < 1) | 456 | /* Assign new rate set per sta */ |
355 | mi->max_prob_streams = 1; | 457 | minstrel_ht_assign_best_tp_rates(mi, tmp_mcs_tp_rate, tmp_cck_tp_rate); |
458 | memcpy(mi->max_tp_rate, tmp_mcs_tp_rate, sizeof(mi->max_tp_rate)); | ||
356 | 459 | ||
357 | for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { | 460 | /* Try to increase robustness of max_prob_rate*/ |
358 | mg = &mi->groups[group]; | 461 | minstrel_ht_prob_rate_reduce_streams(mi); |
359 | if (!mg->supported) | 462 | |
360 | continue; | 463 | /* try to sample all available rates during each interval */ |
361 | mr = minstrel_get_ratestats(mi, mg->max_prob_rate); | 464 | mi->sample_count *= 8; |
362 | if (cur_prob_tp < mr->cur_tp && | ||
363 | minstrel_mcs_groups[group].streams <= mi->max_prob_streams) { | ||
364 | mi->max_prob_rate = mg->max_prob_rate; | ||
365 | cur_prob = mr->cur_prob; | ||
366 | cur_prob_tp = mr->cur_tp; | ||
367 | } | ||
368 | } | ||
369 | 465 | ||
370 | #ifdef CONFIG_MAC80211_DEBUGFS | 466 | #ifdef CONFIG_MAC80211_DEBUGFS |
371 | /* use fixed index if set */ | 467 | /* use fixed index if set */ |
372 | if (mp->fixed_rate_idx != -1) { | 468 | if (mp->fixed_rate_idx != -1) { |
373 | mi->max_tp_rate = mp->fixed_rate_idx; | 469 | for (i = 0; i < 4; i++) |
374 | mi->max_tp_rate2 = mp->fixed_rate_idx; | 470 | mi->max_tp_rate[i] = mp->fixed_rate_idx; |
375 | mi->max_prob_rate = mp->fixed_rate_idx; | 471 | mi->max_prob_rate = mp->fixed_rate_idx; |
376 | } | 472 | } |
377 | #endif | 473 | #endif |
378 | 474 | ||
475 | /* Reset update timer */ | ||
379 | mi->stats_update = jiffies; | 476 | mi->stats_update = jiffies; |
380 | } | 477 | } |
381 | 478 | ||
@@ -420,8 +517,7 @@ minstrel_next_sample_idx(struct minstrel_ht_sta *mi) | |||
420 | } | 517 | } |
421 | 518 | ||
422 | static void | 519 | static void |
423 | minstrel_downgrade_rate(struct minstrel_ht_sta *mi, unsigned int *idx, | 520 | minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary) |
424 | bool primary) | ||
425 | { | 521 | { |
426 | int group, orig_group; | 522 | int group, orig_group; |
427 | 523 | ||
@@ -437,9 +533,9 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, unsigned int *idx, | |||
437 | continue; | 533 | continue; |
438 | 534 | ||
439 | if (primary) | 535 | if (primary) |
440 | *idx = mi->groups[group].max_tp_rate; | 536 | *idx = mi->groups[group].max_group_tp_rate[0]; |
441 | else | 537 | else |
442 | *idx = mi->groups[group].max_tp_rate2; | 538 | *idx = mi->groups[group].max_group_tp_rate[1]; |
443 | break; | 539 | break; |
444 | } | 540 | } |
445 | } | 541 | } |
@@ -524,19 +620,19 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, | |||
524 | * check for sudden death of spatial multiplexing, | 620 | * check for sudden death of spatial multiplexing, |
525 | * downgrade to a lower number of streams if necessary. | 621 | * downgrade to a lower number of streams if necessary. |
526 | */ | 622 | */ |
527 | rate = minstrel_get_ratestats(mi, mi->max_tp_rate); | 623 | rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]); |
528 | if (rate->attempts > 30 && | 624 | if (rate->attempts > 30 && |
529 | MINSTREL_FRAC(rate->success, rate->attempts) < | 625 | MINSTREL_FRAC(rate->success, rate->attempts) < |
530 | MINSTREL_FRAC(20, 100)) { | 626 | MINSTREL_FRAC(20, 100)) { |
531 | minstrel_downgrade_rate(mi, &mi->max_tp_rate, true); | 627 | minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true); |
532 | update = true; | 628 | update = true; |
533 | } | 629 | } |
534 | 630 | ||
535 | rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2); | 631 | rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]); |
536 | if (rate2->attempts > 30 && | 632 | if (rate2->attempts > 30 && |
537 | MINSTREL_FRAC(rate2->success, rate2->attempts) < | 633 | MINSTREL_FRAC(rate2->success, rate2->attempts) < |
538 | MINSTREL_FRAC(20, 100)) { | 634 | MINSTREL_FRAC(20, 100)) { |
539 | minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false); | 635 | minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false); |
540 | update = true; | 636 | update = true; |
541 | } | 637 | } |
542 | 638 | ||
@@ -661,12 +757,12 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
661 | if (!rates) | 757 | if (!rates) |
662 | return; | 758 | return; |
663 | 759 | ||
664 | /* Start with max_tp_rate */ | 760 | /* Start with max_tp_rate[0] */ |
665 | minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate); | 761 | minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate[0]); |
666 | 762 | ||
667 | if (mp->hw->max_rates >= 3) { | 763 | if (mp->hw->max_rates >= 3) { |
668 | /* At least 3 tx rates supported, use max_tp_rate2 next */ | 764 | /* At least 3 tx rates supported, use max_tp_rate[1] next */ |
669 | minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate2); | 765 | minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate[1]); |
670 | } | 766 | } |
671 | 767 | ||
672 | if (mp->hw->max_rates >= 2) { | 768 | if (mp->hw->max_rates >= 2) { |
@@ -691,7 +787,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
691 | { | 787 | { |
692 | struct minstrel_rate_stats *mr; | 788 | struct minstrel_rate_stats *mr; |
693 | struct minstrel_mcs_group_data *mg; | 789 | struct minstrel_mcs_group_data *mg; |
694 | unsigned int sample_dur, sample_group; | 790 | unsigned int sample_dur, sample_group, cur_max_tp_streams; |
695 | int sample_idx = 0; | 791 | int sample_idx = 0; |
696 | 792 | ||
697 | if (mi->sample_wait > 0) { | 793 | if (mi->sample_wait > 0) { |
@@ -718,8 +814,8 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
718 | * to the frame. Hence, don't use sampling for the currently | 814 | * to the frame. Hence, don't use sampling for the currently |
719 | * used rates. | 815 | * used rates. |
720 | */ | 816 | */ |
721 | if (sample_idx == mi->max_tp_rate || | 817 | if (sample_idx == mi->max_tp_rate[0] || |
722 | sample_idx == mi->max_tp_rate2 || | 818 | sample_idx == mi->max_tp_rate[1] || |
723 | sample_idx == mi->max_prob_rate) | 819 | sample_idx == mi->max_prob_rate) |
724 | return -1; | 820 | return -1; |
725 | 821 | ||
@@ -734,9 +830,12 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) | |||
734 | * Make sure that lower rates get sampled only occasionally, | 830 | * Make sure that lower rates get sampled only occasionally, |
735 | * if the link is working perfectly. | 831 | * if the link is working perfectly. |
736 | */ | 832 | */ |
833 | |||
834 | cur_max_tp_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / | ||
835 | MCS_GROUP_RATES].streams; | ||
737 | sample_dur = minstrel_get_duration(sample_idx); | 836 | sample_dur = minstrel_get_duration(sample_idx); |
738 | if (sample_dur >= minstrel_get_duration(mi->max_tp_rate2) && | 837 | if (sample_dur >= minstrel_get_duration(mi->max_tp_rate[1]) && |
739 | (mi->max_prob_streams < | 838 | (cur_max_tp_streams - 1 < |
740 | minstrel_mcs_groups[sample_group].streams || | 839 | minstrel_mcs_groups[sample_group].streams || |
741 | sample_dur >= minstrel_get_duration(mi->max_prob_rate))) { | 840 | sample_dur >= minstrel_get_duration(mi->max_prob_rate))) { |
742 | if (mr->sample_skipped < 20) | 841 | if (mr->sample_skipped < 20) |
@@ -1041,8 +1140,8 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta) | |||
1041 | if (!msp->is_ht) | 1140 | if (!msp->is_ht) |
1042 | return mac80211_minstrel.get_expected_throughput(priv_sta); | 1141 | return mac80211_minstrel.get_expected_throughput(priv_sta); |
1043 | 1142 | ||
1044 | i = mi->max_tp_rate / MCS_GROUP_RATES; | 1143 | i = mi->max_tp_rate[0] / MCS_GROUP_RATES; |
1045 | j = mi->max_tp_rate % MCS_GROUP_RATES; | 1144 | j = mi->max_tp_rate[0] % MCS_GROUP_RATES; |
1046 | 1145 | ||
1047 | /* convert cur_tp from pkt per second in kbps */ | 1146 | /* convert cur_tp from pkt per second in kbps */ |
1048 | return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024; | 1147 | return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024; |
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index d655586773ac..01570e0e014b 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h | |||
@@ -26,28 +26,6 @@ struct mcs_group { | |||
26 | 26 | ||
27 | extern const struct mcs_group minstrel_mcs_groups[]; | 27 | extern const struct mcs_group minstrel_mcs_groups[]; |
28 | 28 | ||
29 | struct minstrel_rate_stats { | ||
30 | /* current / last sampling period attempts/success counters */ | ||
31 | unsigned int attempts, last_attempts; | ||
32 | unsigned int success, last_success; | ||
33 | |||
34 | /* total attempts/success counters */ | ||
35 | u64 att_hist, succ_hist; | ||
36 | |||
37 | /* current throughput */ | ||
38 | unsigned int cur_tp; | ||
39 | |||
40 | /* packet delivery probabilities */ | ||
41 | unsigned int cur_prob, probability; | ||
42 | |||
43 | /* maximum retry counts */ | ||
44 | unsigned int retry_count; | ||
45 | unsigned int retry_count_rtscts; | ||
46 | |||
47 | bool retry_updated; | ||
48 | u8 sample_skipped; | ||
49 | }; | ||
50 | |||
51 | struct minstrel_mcs_group_data { | 29 | struct minstrel_mcs_group_data { |
52 | u8 index; | 30 | u8 index; |
53 | u8 column; | 31 | u8 column; |
@@ -55,10 +33,9 @@ struct minstrel_mcs_group_data { | |||
55 | /* bitfield of supported MCS rates of this group */ | 33 | /* bitfield of supported MCS rates of this group */ |
56 | u8 supported; | 34 | u8 supported; |
57 | 35 | ||
58 | /* selected primary rates */ | 36 | /* sorted rate set within a MCS group*/ |
59 | unsigned int max_tp_rate; | 37 | u8 max_group_tp_rate[MAX_THR_RATES]; |
60 | unsigned int max_tp_rate2; | 38 | u8 max_group_prob_rate; |
61 | unsigned int max_prob_rate; | ||
62 | 39 | ||
63 | /* MCS rate statistics */ | 40 | /* MCS rate statistics */ |
64 | struct minstrel_rate_stats rates[MCS_GROUP_RATES]; | 41 | struct minstrel_rate_stats rates[MCS_GROUP_RATES]; |
@@ -74,15 +51,9 @@ struct minstrel_ht_sta { | |||
74 | /* ampdu length (EWMA) */ | 51 | /* ampdu length (EWMA) */ |
75 | unsigned int avg_ampdu_len; | 52 | unsigned int avg_ampdu_len; |
76 | 53 | ||
77 | /* best throughput rate */ | 54 | /* overall sorted rate set */ |
78 | unsigned int max_tp_rate; | 55 | u8 max_tp_rate[MAX_THR_RATES]; |
79 | 56 | u8 max_prob_rate; | |
80 | /* second best throughput rate */ | ||
81 | unsigned int max_tp_rate2; | ||
82 | |||
83 | /* best probability rate */ | ||
84 | unsigned int max_prob_rate; | ||
85 | unsigned int max_prob_streams; | ||
86 | 57 | ||
87 | /* time of last status update */ | 58 | /* time of last status update */ |
88 | unsigned long stats_update; | 59 | unsigned long stats_update; |
diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index 3e7d793de0c3..a72ad46f2a04 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c | |||
@@ -46,8 +46,10 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) | |||
46 | else | 46 | else |
47 | p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); | 47 | p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); |
48 | 48 | ||
49 | *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; | 49 | *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' '; |
50 | *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; | 50 | *(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' '; |
51 | *(p++) = (idx == mi->max_tp_rate[2]) ? 'C' : ' '; | ||
52 | *(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' '; | ||
51 | *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; | 53 | *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; |
52 | 54 | ||
53 | if (i == max_mcs) { | 55 | if (i == max_mcs) { |
@@ -100,8 +102,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) | |||
100 | 102 | ||
101 | file->private_data = ms; | 103 | file->private_data = ms; |
102 | p = ms->buf; | 104 | p = ms->buf; |
103 | p += sprintf(p, "type rate throughput ewma prob this prob " | 105 | p += sprintf(p, "type rate throughput ewma prob " |
104 | "retry this succ/attempt success attempts\n"); | 106 | "this prob retry this succ/attempt success attempts\n"); |
105 | 107 | ||
106 | p = minstrel_ht_stats_dump(mi, max_mcs, p); | 108 | p = minstrel_ht_stats_dump(mi, max_mcs, p); |
107 | for (i = 0; i < max_mcs; i++) | 109 | for (i = 0; i < max_mcs; i++) |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a8d862f9183c..b04ca4049c95 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -835,6 +836,16 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata | |||
835 | 836 | ||
836 | spin_lock(&tid_agg_rx->reorder_lock); | 837 | spin_lock(&tid_agg_rx->reorder_lock); |
837 | 838 | ||
839 | /* | ||
840 | * Offloaded BA sessions have no known starting sequence number so pick | ||
841 | * one from first Rxed frame for this tid after BA was started. | ||
842 | */ | ||
843 | if (unlikely(tid_agg_rx->auto_seq)) { | ||
844 | tid_agg_rx->auto_seq = false; | ||
845 | tid_agg_rx->ssn = mpdu_seq_num; | ||
846 | tid_agg_rx->head_seq_num = mpdu_seq_num; | ||
847 | } | ||
848 | |||
838 | buf_size = tid_agg_rx->buf_size; | 849 | buf_size = tid_agg_rx->buf_size; |
839 | head_seq_num = tid_agg_rx->head_seq_num; | 850 | head_seq_num = tid_agg_rx->head_seq_num; |
840 | 851 | ||
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index a9bb6eb8c3e0..af0d094b2f2f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright 2005, Devicescape Software, Inc. | 6 | * Copyright 2005, Devicescape Software, Inc. |
7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> | 8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> |
9 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
9 | * | 10 | * |
10 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 12 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 730030542024..90395c6b9757 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright 2002-2005, Instant802 Networks, Inc. | 2 | * Copyright 2002-2005, Instant802 Networks, Inc. |
3 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 3 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
4 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
4 | * | 5 | * |
5 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 89c40d5c0633..42f68cb8957e 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright 2002-2005, Devicescape Software, Inc. | 2 | * Copyright 2002-2005, Devicescape Software, Inc. |
3 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
3 | * | 4 | * |
4 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
@@ -167,6 +168,8 @@ struct tid_ampdu_tx { | |||
167 | * @dialog_token: dialog token for aggregation session | 168 | * @dialog_token: dialog token for aggregation session |
168 | * @rcu_head: RCU head used for freeing this struct | 169 | * @rcu_head: RCU head used for freeing this struct |
169 | * @reorder_lock: serializes access to reorder buffer, see below. | 170 | * @reorder_lock: serializes access to reorder buffer, see below. |
171 | * @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and | ||
172 | * and ssn. | ||
170 | * | 173 | * |
171 | * This structure's lifetime is managed by RCU, assignments to | 174 | * This structure's lifetime is managed by RCU, assignments to |
172 | * the array holding it must hold the aggregation mutex. | 175 | * the array holding it must hold the aggregation mutex. |
@@ -190,6 +193,7 @@ struct tid_ampdu_rx { | |||
190 | u16 buf_size; | 193 | u16 buf_size; |
191 | u16 timeout; | 194 | u16 timeout; |
192 | u8 dialog_token; | 195 | u8 dialog_token; |
196 | bool auto_seq; | ||
193 | }; | 197 | }; |
194 | 198 | ||
195 | /** | 199 | /** |
@@ -446,6 +450,9 @@ struct sta_info { | |||
446 | enum ieee80211_smps_mode known_smps_mode; | 450 | enum ieee80211_smps_mode known_smps_mode; |
447 | const struct ieee80211_cipher_scheme *cipher_scheme; | 451 | const struct ieee80211_cipher_scheme *cipher_scheme; |
448 | 452 | ||
453 | /* TDLS timeout data */ | ||
454 | unsigned long last_tdls_pkt_time; | ||
455 | |||
449 | /* keep last! */ | 456 | /* keep last! */ |
450 | struct ieee80211_sta sta; | 457 | struct ieee80211_sta sta; |
451 | }; | 458 | }; |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index aa06dcad336e..89290e33dafe 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2008-2010 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2008-2010 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -537,6 +538,8 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local, | |||
537 | * - current throughput (higher value for higher tpt)? | 538 | * - current throughput (higher value for higher tpt)? |
538 | */ | 539 | */ |
539 | #define STA_LOST_PKT_THRESHOLD 50 | 540 | #define STA_LOST_PKT_THRESHOLD 50 |
541 | #define STA_LOST_TDLS_PKT_THRESHOLD 10 | ||
542 | #define STA_LOST_TDLS_PKT_TIME (10*HZ) /* 10secs since last ACK */ | ||
540 | 543 | ||
541 | static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb) | 544 | static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb) |
542 | { | 545 | { |
@@ -547,7 +550,20 @@ static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb) | |||
547 | !(info->flags & IEEE80211_TX_STAT_AMPDU)) | 550 | !(info->flags & IEEE80211_TX_STAT_AMPDU)) |
548 | return; | 551 | return; |
549 | 552 | ||
550 | if (++sta->lost_packets < STA_LOST_PKT_THRESHOLD) | 553 | sta->lost_packets++; |
554 | if (!sta->sta.tdls && sta->lost_packets < STA_LOST_PKT_THRESHOLD) | ||
555 | return; | ||
556 | |||
557 | /* | ||
558 | * If we're in TDLS mode, make sure that all STA_LOST_TDLS_PKT_THRESHOLD | ||
559 | * of the last packets were lost, and that no ACK was received in the | ||
560 | * last STA_LOST_TDLS_PKT_TIME ms, before triggering the CQM packet-loss | ||
561 | * mechanism. | ||
562 | */ | ||
563 | if (sta->sta.tdls && | ||
564 | (sta->lost_packets < STA_LOST_TDLS_PKT_THRESHOLD || | ||
565 | time_before(jiffies, | ||
566 | sta->last_tdls_pkt_time + STA_LOST_TDLS_PKT_TIME))) | ||
551 | return; | 567 | return; |
552 | 568 | ||
553 | cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, | 569 | cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, |
@@ -694,6 +710,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
694 | if (info->flags & IEEE80211_TX_STAT_ACK) { | 710 | if (info->flags & IEEE80211_TX_STAT_ACK) { |
695 | if (sta->lost_packets) | 711 | if (sta->lost_packets) |
696 | sta->lost_packets = 0; | 712 | sta->lost_packets = 0; |
713 | |||
714 | /* Track when last TDLS packet was ACKed */ | ||
715 | if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) | ||
716 | sta->last_tdls_pkt_time = jiffies; | ||
697 | } else { | 717 | } else { |
698 | ieee80211_lost_packet(sta, skb); | 718 | ieee80211_lost_packet(sta, skb); |
699 | } | 719 | } |
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index f2cb3b6c1871..4ea25dec0698 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright 2014, Intel Corporation | 5 | * Copyright 2014, Intel Corporation |
6 | * Copyright 2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * This file is GPLv2 as found in COPYING. | 8 | * This file is GPLv2 as found in COPYING. |
8 | */ | 9 | */ |
@@ -411,6 +412,9 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, | |||
411 | tf->ether_type = cpu_to_be16(ETH_P_TDLS); | 412 | tf->ether_type = cpu_to_be16(ETH_P_TDLS); |
412 | tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; | 413 | tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; |
413 | 414 | ||
415 | /* network header is after the ethernet header */ | ||
416 | skb_set_network_header(skb, ETH_HLEN); | ||
417 | |||
414 | switch (action_code) { | 418 | switch (action_code) { |
415 | case WLAN_TDLS_SETUP_REQUEST: | 419 | case WLAN_TDLS_SETUP_REQUEST: |
416 | tf->category = WLAN_CATEGORY_TDLS; | 420 | tf->category = WLAN_CATEGORY_TDLS; |
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 02ac535d1274..38fae7ebe984 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h | |||
@@ -672,13 +672,13 @@ DEFINE_EVENT(local_u32_evt, drv_set_rts_threshold, | |||
672 | ); | 672 | ); |
673 | 673 | ||
674 | TRACE_EVENT(drv_set_coverage_class, | 674 | TRACE_EVENT(drv_set_coverage_class, |
675 | TP_PROTO(struct ieee80211_local *local, u8 value), | 675 | TP_PROTO(struct ieee80211_local *local, s16 value), |
676 | 676 | ||
677 | TP_ARGS(local, value), | 677 | TP_ARGS(local, value), |
678 | 678 | ||
679 | TP_STRUCT__entry( | 679 | TP_STRUCT__entry( |
680 | LOCAL_ENTRY | 680 | LOCAL_ENTRY |
681 | __field(u8, value) | 681 | __field(s16, value) |
682 | ), | 682 | ), |
683 | 683 | ||
684 | TP_fast_assign( | 684 | TP_fast_assign( |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 925c39f4099e..2f7754ca59d2 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -1788,9 +1789,8 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local, | |||
1788 | * @skb: packet to be sent | 1789 | * @skb: packet to be sent |
1789 | * @dev: incoming interface | 1790 | * @dev: incoming interface |
1790 | * | 1791 | * |
1791 | * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will | 1792 | * Returns: NETDEV_TX_OK both on success and on failure. On failure skb will |
1792 | * not be freed, and caller is responsible for either retrying later or freeing | 1793 | * be freed. |
1793 | * skb). | ||
1794 | * | 1794 | * |
1795 | * This function takes in an Ethernet header and encapsulates it with suitable | 1795 | * This function takes in an Ethernet header and encapsulates it with suitable |
1796 | * IEEE 802.11 header based on which interface the packet is coming in. The | 1796 | * IEEE 802.11 header based on which interface the packet is coming in. The |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 725af7a468d2..3c61060a4d2b 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 4 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -1014,6 +1015,31 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, | |||
1014 | } | 1015 | } |
1015 | elems->pwr_constr_elem = pos; | 1016 | elems->pwr_constr_elem = pos; |
1016 | break; | 1017 | break; |
1018 | case WLAN_EID_CISCO_VENDOR_SPECIFIC: | ||
1019 | /* Lots of different options exist, but we only care | ||
1020 | * about the Dynamic Transmit Power Control element. | ||
1021 | * First check for the Cisco OUI, then for the DTPC | ||
1022 | * tag (0x00). | ||
1023 | */ | ||
1024 | if (elen < 4) { | ||
1025 | elem_parse_failed = true; | ||
1026 | break; | ||
1027 | } | ||
1028 | |||
1029 | if (pos[0] != 0x00 || pos[1] != 0x40 || | ||
1030 | pos[2] != 0x96 || pos[3] != 0x00) | ||
1031 | break; | ||
1032 | |||
1033 | if (elen != 6) { | ||
1034 | elem_parse_failed = true; | ||
1035 | break; | ||
1036 | } | ||
1037 | |||
1038 | if (calc_crc) | ||
1039 | crc = crc32_be(crc, pos - 2, elen + 2); | ||
1040 | |||
1041 | elems->cisco_dtpc_elem = pos; | ||
1042 | break; | ||
1017 | case WLAN_EID_TIMEOUT_INTERVAL: | 1043 | case WLAN_EID_TIMEOUT_INTERVAL: |
1018 | if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) | 1044 | if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) |
1019 | elems->timeout_int = (void *)pos; | 1045 | elems->timeout_int = (void *)pos; |
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 6459946f0b74..3b873989992c 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright 2004, Instant802 Networks, Inc. | 2 | * Copyright 2004, Instant802 Networks, Inc. |
3 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
3 | * | 4 | * |
4 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index f7d4ca4c46e0..983527a4c1ab 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c | |||
@@ -64,8 +64,11 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) | |||
64 | if (!info->control.hw_key) | 64 | if (!info->control.hw_key) |
65 | tail += IEEE80211_TKIP_ICV_LEN; | 65 | tail += IEEE80211_TKIP_ICV_LEN; |
66 | 66 | ||
67 | if (WARN_ON(skb_tailroom(skb) < tail || | 67 | if (WARN(skb_tailroom(skb) < tail || |
68 | skb_headroom(skb) < IEEE80211_TKIP_IV_LEN)) | 68 | skb_headroom(skb) < IEEE80211_TKIP_IV_LEN, |
69 | "mmic: not enough head/tail (%d/%d,%d/%d)\n", | ||
70 | skb_headroom(skb), IEEE80211_TKIP_IV_LEN, | ||
71 | skb_tailroom(skb), tail)) | ||
69 | return TX_DROP; | 72 | return TX_DROP; |
70 | 73 | ||
71 | key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]; | 74 | key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]; |
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 992b34070bcb..72d81e2154d5 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * any point in time. | 4 | * any point in time. |
5 | * | 5 | * |
6 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | 6 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> |
7 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
7 | */ | 8 | */ |
8 | 9 | ||
9 | #include <linux/export.h> | 10 | #include <linux/export.h> |
diff --git a/net/wireless/core.c b/net/wireless/core.c index c6620aa679e0..f52a4cd7017c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * This is the linux wireless configuration interface. | 2 | * This is the linux wireless configuration interface. |
3 | * | 3 | * |
4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
5 | */ | 6 | */ |
6 | 7 | ||
7 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
@@ -1005,7 +1006,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, | |||
1005 | rdev->devlist_generation++; | 1006 | rdev->devlist_generation++; |
1006 | cfg80211_mlme_purge_registrations(wdev); | 1007 | cfg80211_mlme_purge_registrations(wdev); |
1007 | #ifdef CONFIG_CFG80211_WEXT | 1008 | #ifdef CONFIG_CFG80211_WEXT |
1008 | kfree(wdev->wext.keys); | 1009 | kzfree(wdev->wext.keys); |
1009 | #endif | 1010 | #endif |
1010 | } | 1011 | } |
1011 | /* | 1012 | /* |
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 8f345da3ea5f..e24fc585c883 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c | |||
@@ -115,7 +115,7 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, | |||
115 | } | 115 | } |
116 | 116 | ||
117 | if (WARN_ON(wdev->connect_keys)) | 117 | if (WARN_ON(wdev->connect_keys)) |
118 | kfree(wdev->connect_keys); | 118 | kzfree(wdev->connect_keys); |
119 | wdev->connect_keys = connkeys; | 119 | wdev->connect_keys = connkeys; |
120 | 120 | ||
121 | wdev->ibss_fixed = params->channel_fixed; | 121 | wdev->ibss_fixed = params->channel_fixed; |
@@ -161,7 +161,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) | |||
161 | 161 | ||
162 | ASSERT_WDEV_LOCK(wdev); | 162 | ASSERT_WDEV_LOCK(wdev); |
163 | 163 | ||
164 | kfree(wdev->connect_keys); | 164 | kzfree(wdev->connect_keys); |
165 | wdev->connect_keys = NULL; | 165 | wdev->connect_keys = NULL; |
166 | 166 | ||
167 | rdev_set_qos_map(rdev, dev, NULL); | 167 | rdev_set_qos_map(rdev, dev, NULL); |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 369fc334fdad..2c52b59e43f3 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -19,7 +19,7 @@ | |||
19 | 19 | ||
20 | 20 | ||
21 | void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, | 21 | void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, |
22 | const u8 *buf, size_t len) | 22 | const u8 *buf, size_t len, int uapsd_queues) |
23 | { | 23 | { |
24 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 24 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
25 | struct wiphy *wiphy = wdev->wiphy; | 25 | struct wiphy *wiphy = wdev->wiphy; |
@@ -43,7 +43,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, | |||
43 | return; | 43 | return; |
44 | } | 44 | } |
45 | 45 | ||
46 | nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); | 46 | nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues); |
47 | /* update current_bss etc., consumes the bss reference */ | 47 | /* update current_bss etc., consumes the bss reference */ |
48 | __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, | 48 | __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, |
49 | status_code, | 49 | status_code, |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3011401f52c0..4cce3e17964d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * This is the new netlink-based wireless configuration interface. | 2 | * This is the new netlink-based wireless configuration interface. |
3 | * | 3 | * |
4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
5 | */ | 6 | */ |
6 | 7 | ||
7 | #include <linux/if.h> | 8 | #include <linux/if.h> |
@@ -225,6 +226,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
225 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, | 226 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, |
226 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, | 227 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, |
227 | [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, | 228 | [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, |
229 | [NL80211_ATTR_WIPHY_DYN_ACK] = { .type = NLA_FLAG }, | ||
228 | 230 | ||
229 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 231 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
230 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 232 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
@@ -388,6 +390,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
388 | [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, | 390 | [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, |
389 | [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG }, | 391 | [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG }, |
390 | [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY }, | 392 | [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY }, |
393 | [NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG }, | ||
394 | [NL80211_ATTR_TSID] = { .type = NLA_U8 }, | ||
395 | [NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 }, | ||
396 | [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 }, | ||
397 | [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 }, | ||
391 | }; | 398 | }; |
392 | 399 | ||
393 | /* policy for the key attributes */ | 400 | /* policy for the key attributes */ |
@@ -1507,6 +1514,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, | |||
1507 | if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) | 1514 | if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) |
1508 | CMD(channel_switch, CHANNEL_SWITCH); | 1515 | CMD(channel_switch, CHANNEL_SWITCH); |
1509 | CMD(set_qos_map, SET_QOS_MAP); | 1516 | CMD(set_qos_map, SET_QOS_MAP); |
1517 | if (rdev->wiphy.flags & | ||
1518 | WIPHY_FLAG_SUPPORTS_WMM_ADMISSION) | ||
1519 | CMD(add_tx_ts, ADD_TX_TS); | ||
1510 | } | 1520 | } |
1511 | /* add into the if now */ | 1521 | /* add into the if now */ |
1512 | #undef CMD | 1522 | #undef CMD |
@@ -2237,11 +2247,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
2237 | } | 2247 | } |
2238 | 2248 | ||
2239 | if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { | 2249 | if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { |
2250 | if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) | ||
2251 | return -EINVAL; | ||
2252 | |||
2240 | coverage_class = nla_get_u8( | 2253 | coverage_class = nla_get_u8( |
2241 | info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); | 2254 | info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); |
2242 | changed |= WIPHY_PARAM_COVERAGE_CLASS; | 2255 | changed |= WIPHY_PARAM_COVERAGE_CLASS; |
2243 | } | 2256 | } |
2244 | 2257 | ||
2258 | if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) { | ||
2259 | if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION)) | ||
2260 | return -EOPNOTSUPP; | ||
2261 | |||
2262 | changed |= WIPHY_PARAM_DYN_ACK; | ||
2263 | } | ||
2264 | |||
2245 | if (changed) { | 2265 | if (changed) { |
2246 | u8 old_retry_short, old_retry_long; | 2266 | u8 old_retry_short, old_retry_long; |
2247 | u32 old_frag_threshold, old_rts_threshold; | 2267 | u32 old_frag_threshold, old_rts_threshold; |
@@ -3326,6 +3346,29 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) | |||
3326 | return PTR_ERR(params.acl); | 3346 | return PTR_ERR(params.acl); |
3327 | } | 3347 | } |
3328 | 3348 | ||
3349 | if (info->attrs[NL80211_ATTR_SMPS_MODE]) { | ||
3350 | params.smps_mode = | ||
3351 | nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]); | ||
3352 | switch (params.smps_mode) { | ||
3353 | case NL80211_SMPS_OFF: | ||
3354 | break; | ||
3355 | case NL80211_SMPS_STATIC: | ||
3356 | if (!(rdev->wiphy.features & | ||
3357 | NL80211_FEATURE_STATIC_SMPS)) | ||
3358 | return -EINVAL; | ||
3359 | break; | ||
3360 | case NL80211_SMPS_DYNAMIC: | ||
3361 | if (!(rdev->wiphy.features & | ||
3362 | NL80211_FEATURE_DYNAMIC_SMPS)) | ||
3363 | return -EINVAL; | ||
3364 | break; | ||
3365 | default: | ||
3366 | return -EINVAL; | ||
3367 | } | ||
3368 | } else { | ||
3369 | params.smps_mode = NL80211_SMPS_OFF; | ||
3370 | } | ||
3371 | |||
3329 | wdev_lock(wdev); | 3372 | wdev_lock(wdev); |
3330 | err = rdev_start_ap(rdev, dev, ¶ms); | 3373 | err = rdev_start_ap(rdev, dev, ¶ms); |
3331 | if (!err) { | 3374 | if (!err) { |
@@ -6583,6 +6626,14 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
6583 | sizeof(req.vht_capa)); | 6626 | sizeof(req.vht_capa)); |
6584 | } | 6627 | } |
6585 | 6628 | ||
6629 | if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) { | ||
6630 | if (!(rdev->wiphy.features & | ||
6631 | NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) || | ||
6632 | !(rdev->wiphy.features & NL80211_FEATURE_QUIET)) | ||
6633 | return -EINVAL; | ||
6634 | req.flags |= ASSOC_REQ_USE_RRM; | ||
6635 | } | ||
6636 | |||
6586 | err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); | 6637 | err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); |
6587 | if (!err) { | 6638 | if (!err) { |
6588 | wdev_lock(dev->ieee80211_ptr); | 6639 | wdev_lock(dev->ieee80211_ptr); |
@@ -6845,7 +6896,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) | |||
6845 | 6896 | ||
6846 | err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); | 6897 | err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); |
6847 | if (err) | 6898 | if (err) |
6848 | kfree(connkeys); | 6899 | kzfree(connkeys); |
6849 | return err; | 6900 | return err; |
6850 | } | 6901 | } |
6851 | 6902 | ||
@@ -7214,7 +7265,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | |||
7214 | 7265 | ||
7215 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { | 7266 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { |
7216 | if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) { | 7267 | if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) { |
7217 | kfree(connkeys); | 7268 | kzfree(connkeys); |
7218 | return -EINVAL; | 7269 | return -EINVAL; |
7219 | } | 7270 | } |
7220 | memcpy(&connect.ht_capa, | 7271 | memcpy(&connect.ht_capa, |
@@ -7232,7 +7283,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | |||
7232 | 7283 | ||
7233 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { | 7284 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { |
7234 | if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) { | 7285 | if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) { |
7235 | kfree(connkeys); | 7286 | kzfree(connkeys); |
7236 | return -EINVAL; | 7287 | return -EINVAL; |
7237 | } | 7288 | } |
7238 | memcpy(&connect.vht_capa, | 7289 | memcpy(&connect.vht_capa, |
@@ -7240,11 +7291,19 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | |||
7240 | sizeof(connect.vht_capa)); | 7291 | sizeof(connect.vht_capa)); |
7241 | } | 7292 | } |
7242 | 7293 | ||
7294 | if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) { | ||
7295 | if (!(rdev->wiphy.features & | ||
7296 | NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) || | ||
7297 | !(rdev->wiphy.features & NL80211_FEATURE_QUIET)) | ||
7298 | return -EINVAL; | ||
7299 | connect.flags |= ASSOC_REQ_USE_RRM; | ||
7300 | } | ||
7301 | |||
7243 | wdev_lock(dev->ieee80211_ptr); | 7302 | wdev_lock(dev->ieee80211_ptr); |
7244 | err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); | 7303 | err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); |
7245 | wdev_unlock(dev->ieee80211_ptr); | 7304 | wdev_unlock(dev->ieee80211_ptr); |
7246 | if (err) | 7305 | if (err) |
7247 | kfree(connkeys); | 7306 | kzfree(connkeys); |
7248 | return err; | 7307 | return err; |
7249 | } | 7308 | } |
7250 | 7309 | ||
@@ -8930,13 +8989,9 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) | |||
8930 | if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN) | 8989 | if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN) |
8931 | return -ERANGE; | 8990 | return -ERANGE; |
8932 | 8991 | ||
8933 | memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]), | 8992 | rekey_data.kek = nla_data(tb[NL80211_REKEY_DATA_KEK]); |
8934 | NL80211_KEK_LEN); | 8993 | rekey_data.kck = nla_data(tb[NL80211_REKEY_DATA_KCK]); |
8935 | memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]), | 8994 | rekey_data.replay_ctr = nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]); |
8936 | NL80211_KCK_LEN); | ||
8937 | memcpy(rekey_data.replay_ctr, | ||
8938 | nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]), | ||
8939 | NL80211_REPLAY_CTR_LEN); | ||
8940 | 8995 | ||
8941 | wdev_lock(wdev); | 8996 | wdev_lock(wdev); |
8942 | if (!wdev->current_bss) { | 8997 | if (!wdev->current_bss) { |
@@ -9365,6 +9420,93 @@ static int nl80211_set_qos_map(struct sk_buff *skb, | |||
9365 | return ret; | 9420 | return ret; |
9366 | } | 9421 | } |
9367 | 9422 | ||
9423 | static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) | ||
9424 | { | ||
9425 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
9426 | struct net_device *dev = info->user_ptr[1]; | ||
9427 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
9428 | const u8 *peer; | ||
9429 | u8 tsid, up; | ||
9430 | u16 admitted_time = 0; | ||
9431 | int err; | ||
9432 | |||
9433 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)) | ||
9434 | return -EOPNOTSUPP; | ||
9435 | |||
9436 | if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] || | ||
9437 | !info->attrs[NL80211_ATTR_USER_PRIO]) | ||
9438 | return -EINVAL; | ||
9439 | |||
9440 | tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]); | ||
9441 | if (tsid >= IEEE80211_NUM_TIDS) | ||
9442 | return -EINVAL; | ||
9443 | |||
9444 | up = nla_get_u8(info->attrs[NL80211_ATTR_USER_PRIO]); | ||
9445 | if (up >= IEEE80211_NUM_UPS) | ||
9446 | return -EINVAL; | ||
9447 | |||
9448 | /* WMM uses TIDs 0-7 even for TSPEC */ | ||
9449 | if (tsid < IEEE80211_FIRST_TSPEC_TSID) { | ||
9450 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)) | ||
9451 | return -EINVAL; | ||
9452 | } else { | ||
9453 | /* TODO: handle 802.11 TSPEC/admission control | ||
9454 | * need more attributes for that (e.g. BA session requirement) | ||
9455 | */ | ||
9456 | return -EINVAL; | ||
9457 | } | ||
9458 | |||
9459 | peer = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
9460 | |||
9461 | if (info->attrs[NL80211_ATTR_ADMITTED_TIME]) { | ||
9462 | admitted_time = | ||
9463 | nla_get_u16(info->attrs[NL80211_ATTR_ADMITTED_TIME]); | ||
9464 | if (!admitted_time) | ||
9465 | return -EINVAL; | ||
9466 | } | ||
9467 | |||
9468 | wdev_lock(wdev); | ||
9469 | switch (wdev->iftype) { | ||
9470 | case NL80211_IFTYPE_STATION: | ||
9471 | case NL80211_IFTYPE_P2P_CLIENT: | ||
9472 | if (wdev->current_bss) | ||
9473 | break; | ||
9474 | err = -ENOTCONN; | ||
9475 | goto out; | ||
9476 | default: | ||
9477 | err = -EOPNOTSUPP; | ||
9478 | goto out; | ||
9479 | } | ||
9480 | |||
9481 | err = rdev_add_tx_ts(rdev, dev, tsid, peer, up, admitted_time); | ||
9482 | |||
9483 | out: | ||
9484 | wdev_unlock(wdev); | ||
9485 | return err; | ||
9486 | } | ||
9487 | |||
9488 | static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info) | ||
9489 | { | ||
9490 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
9491 | struct net_device *dev = info->user_ptr[1]; | ||
9492 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
9493 | const u8 *peer; | ||
9494 | u8 tsid; | ||
9495 | int err; | ||
9496 | |||
9497 | if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC]) | ||
9498 | return -EINVAL; | ||
9499 | |||
9500 | tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]); | ||
9501 | peer = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
9502 | |||
9503 | wdev_lock(wdev); | ||
9504 | err = rdev_del_tx_ts(rdev, dev, tsid, peer); | ||
9505 | wdev_unlock(wdev); | ||
9506 | |||
9507 | return err; | ||
9508 | } | ||
9509 | |||
9368 | #define NL80211_FLAG_NEED_WIPHY 0x01 | 9510 | #define NL80211_FLAG_NEED_WIPHY 0x01 |
9369 | #define NL80211_FLAG_NEED_NETDEV 0x02 | 9511 | #define NL80211_FLAG_NEED_NETDEV 0x02 |
9370 | #define NL80211_FLAG_NEED_RTNL 0x04 | 9512 | #define NL80211_FLAG_NEED_RTNL 0x04 |
@@ -9375,6 +9517,7 @@ static int nl80211_set_qos_map(struct sk_buff *skb, | |||
9375 | /* If a netdev is associated, it must be UP, P2P must be started */ | 9517 | /* If a netdev is associated, it must be UP, P2P must be started */ |
9376 | #define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\ | 9518 | #define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\ |
9377 | NL80211_FLAG_CHECK_NETDEV_UP) | 9519 | NL80211_FLAG_CHECK_NETDEV_UP) |
9520 | #define NL80211_FLAG_CLEAR_SKB 0x20 | ||
9378 | 9521 | ||
9379 | static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, | 9522 | static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, |
9380 | struct genl_info *info) | 9523 | struct genl_info *info) |
@@ -9458,8 +9601,20 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb, | |||
9458 | dev_put(info->user_ptr[1]); | 9601 | dev_put(info->user_ptr[1]); |
9459 | } | 9602 | } |
9460 | } | 9603 | } |
9604 | |||
9461 | if (ops->internal_flags & NL80211_FLAG_NEED_RTNL) | 9605 | if (ops->internal_flags & NL80211_FLAG_NEED_RTNL) |
9462 | rtnl_unlock(); | 9606 | rtnl_unlock(); |
9607 | |||
9608 | /* If needed, clear the netlink message payload from the SKB | ||
9609 | * as it might contain key data that shouldn't stick around on | ||
9610 | * the heap after the SKB is freed. The netlink message header | ||
9611 | * is still needed for further processing, so leave it intact. | ||
9612 | */ | ||
9613 | if (ops->internal_flags & NL80211_FLAG_CLEAR_SKB) { | ||
9614 | struct nlmsghdr *nlh = nlmsg_hdr(skb); | ||
9615 | |||
9616 | memset(nlmsg_data(nlh), 0, nlmsg_len(nlh)); | ||
9617 | } | ||
9463 | } | 9618 | } |
9464 | 9619 | ||
9465 | static const struct genl_ops nl80211_ops[] = { | 9620 | static const struct genl_ops nl80211_ops[] = { |
@@ -9527,7 +9682,8 @@ static const struct genl_ops nl80211_ops[] = { | |||
9527 | .policy = nl80211_policy, | 9682 | .policy = nl80211_policy, |
9528 | .flags = GENL_ADMIN_PERM, | 9683 | .flags = GENL_ADMIN_PERM, |
9529 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | 9684 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
9530 | NL80211_FLAG_NEED_RTNL, | 9685 | NL80211_FLAG_NEED_RTNL | |
9686 | NL80211_FLAG_CLEAR_SKB, | ||
9531 | }, | 9687 | }, |
9532 | { | 9688 | { |
9533 | .cmd = NL80211_CMD_NEW_KEY, | 9689 | .cmd = NL80211_CMD_NEW_KEY, |
@@ -9535,7 +9691,8 @@ static const struct genl_ops nl80211_ops[] = { | |||
9535 | .policy = nl80211_policy, | 9691 | .policy = nl80211_policy, |
9536 | .flags = GENL_ADMIN_PERM, | 9692 | .flags = GENL_ADMIN_PERM, |
9537 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | 9693 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
9538 | NL80211_FLAG_NEED_RTNL, | 9694 | NL80211_FLAG_NEED_RTNL | |
9695 | NL80211_FLAG_CLEAR_SKB, | ||
9539 | }, | 9696 | }, |
9540 | { | 9697 | { |
9541 | .cmd = NL80211_CMD_DEL_KEY, | 9698 | .cmd = NL80211_CMD_DEL_KEY, |
@@ -9713,7 +9870,8 @@ static const struct genl_ops nl80211_ops[] = { | |||
9713 | .policy = nl80211_policy, | 9870 | .policy = nl80211_policy, |
9714 | .flags = GENL_ADMIN_PERM, | 9871 | .flags = GENL_ADMIN_PERM, |
9715 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | 9872 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
9716 | NL80211_FLAG_NEED_RTNL, | 9873 | NL80211_FLAG_NEED_RTNL | |
9874 | NL80211_FLAG_CLEAR_SKB, | ||
9717 | }, | 9875 | }, |
9718 | { | 9876 | { |
9719 | .cmd = NL80211_CMD_ASSOCIATE, | 9877 | .cmd = NL80211_CMD_ASSOCIATE, |
@@ -9947,7 +10105,8 @@ static const struct genl_ops nl80211_ops[] = { | |||
9947 | .policy = nl80211_policy, | 10105 | .policy = nl80211_policy, |
9948 | .flags = GENL_ADMIN_PERM, | 10106 | .flags = GENL_ADMIN_PERM, |
9949 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | 10107 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
9950 | NL80211_FLAG_NEED_RTNL, | 10108 | NL80211_FLAG_NEED_RTNL | |
10109 | NL80211_FLAG_CLEAR_SKB, | ||
9951 | }, | 10110 | }, |
9952 | { | 10111 | { |
9953 | .cmd = NL80211_CMD_TDLS_MGMT, | 10112 | .cmd = NL80211_CMD_TDLS_MGMT, |
@@ -10105,6 +10264,22 @@ static const struct genl_ops nl80211_ops[] = { | |||
10105 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | 10264 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | |
10106 | NL80211_FLAG_NEED_RTNL, | 10265 | NL80211_FLAG_NEED_RTNL, |
10107 | }, | 10266 | }, |
10267 | { | ||
10268 | .cmd = NL80211_CMD_ADD_TX_TS, | ||
10269 | .doit = nl80211_add_tx_ts, | ||
10270 | .policy = nl80211_policy, | ||
10271 | .flags = GENL_ADMIN_PERM, | ||
10272 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
10273 | NL80211_FLAG_NEED_RTNL, | ||
10274 | }, | ||
10275 | { | ||
10276 | .cmd = NL80211_CMD_DEL_TX_TS, | ||
10277 | .doit = nl80211_del_tx_ts, | ||
10278 | .policy = nl80211_policy, | ||
10279 | .flags = GENL_ADMIN_PERM, | ||
10280 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
10281 | NL80211_FLAG_NEED_RTNL, | ||
10282 | }, | ||
10108 | }; | 10283 | }; |
10109 | 10284 | ||
10110 | /* notification functions */ | 10285 | /* notification functions */ |
@@ -10373,7 +10548,8 @@ nla_put_failure: | |||
10373 | static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, | 10548 | static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, |
10374 | struct net_device *netdev, | 10549 | struct net_device *netdev, |
10375 | const u8 *buf, size_t len, | 10550 | const u8 *buf, size_t len, |
10376 | enum nl80211_commands cmd, gfp_t gfp) | 10551 | enum nl80211_commands cmd, gfp_t gfp, |
10552 | int uapsd_queues) | ||
10377 | { | 10553 | { |
10378 | struct sk_buff *msg; | 10554 | struct sk_buff *msg; |
10379 | void *hdr; | 10555 | void *hdr; |
@@ -10393,6 +10569,19 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, | |||
10393 | nla_put(msg, NL80211_ATTR_FRAME, len, buf)) | 10569 | nla_put(msg, NL80211_ATTR_FRAME, len, buf)) |
10394 | goto nla_put_failure; | 10570 | goto nla_put_failure; |
10395 | 10571 | ||
10572 | if (uapsd_queues >= 0) { | ||
10573 | struct nlattr *nla_wmm = | ||
10574 | nla_nest_start(msg, NL80211_ATTR_STA_WME); | ||
10575 | if (!nla_wmm) | ||
10576 | goto nla_put_failure; | ||
10577 | |||
10578 | if (nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES, | ||
10579 | uapsd_queues)) | ||
10580 | goto nla_put_failure; | ||
10581 | |||
10582 | nla_nest_end(msg, nla_wmm); | ||
10583 | } | ||
10584 | |||
10396 | genlmsg_end(msg, hdr); | 10585 | genlmsg_end(msg, hdr); |
10397 | 10586 | ||
10398 | genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, | 10587 | genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, |
@@ -10409,15 +10598,15 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, | |||
10409 | size_t len, gfp_t gfp) | 10598 | size_t len, gfp_t gfp) |
10410 | { | 10599 | { |
10411 | nl80211_send_mlme_event(rdev, netdev, buf, len, | 10600 | nl80211_send_mlme_event(rdev, netdev, buf, len, |
10412 | NL80211_CMD_AUTHENTICATE, gfp); | 10601 | NL80211_CMD_AUTHENTICATE, gfp, -1); |
10413 | } | 10602 | } |
10414 | 10603 | ||
10415 | void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, | 10604 | void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, |
10416 | struct net_device *netdev, const u8 *buf, | 10605 | struct net_device *netdev, const u8 *buf, |
10417 | size_t len, gfp_t gfp) | 10606 | size_t len, gfp_t gfp, int uapsd_queues) |
10418 | { | 10607 | { |
10419 | nl80211_send_mlme_event(rdev, netdev, buf, len, | 10608 | nl80211_send_mlme_event(rdev, netdev, buf, len, |
10420 | NL80211_CMD_ASSOCIATE, gfp); | 10609 | NL80211_CMD_ASSOCIATE, gfp, uapsd_queues); |
10421 | } | 10610 | } |
10422 | 10611 | ||
10423 | void nl80211_send_deauth(struct cfg80211_registered_device *rdev, | 10612 | void nl80211_send_deauth(struct cfg80211_registered_device *rdev, |
@@ -10425,7 +10614,7 @@ void nl80211_send_deauth(struct cfg80211_registered_device *rdev, | |||
10425 | size_t len, gfp_t gfp) | 10614 | size_t len, gfp_t gfp) |
10426 | { | 10615 | { |
10427 | nl80211_send_mlme_event(rdev, netdev, buf, len, | 10616 | nl80211_send_mlme_event(rdev, netdev, buf, len, |
10428 | NL80211_CMD_DEAUTHENTICATE, gfp); | 10617 | NL80211_CMD_DEAUTHENTICATE, gfp, -1); |
10429 | } | 10618 | } |
10430 | 10619 | ||
10431 | void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, | 10620 | void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, |
@@ -10433,7 +10622,7 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, | |||
10433 | size_t len, gfp_t gfp) | 10622 | size_t len, gfp_t gfp) |
10434 | { | 10623 | { |
10435 | nl80211_send_mlme_event(rdev, netdev, buf, len, | 10624 | nl80211_send_mlme_event(rdev, netdev, buf, len, |
10436 | NL80211_CMD_DISASSOCIATE, gfp); | 10625 | NL80211_CMD_DISASSOCIATE, gfp, -1); |
10437 | } | 10626 | } |
10438 | 10627 | ||
10439 | void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, | 10628 | void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, |
@@ -10454,7 +10643,7 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, | |||
10454 | cmd = NL80211_CMD_UNPROT_DISASSOCIATE; | 10643 | cmd = NL80211_CMD_UNPROT_DISASSOCIATE; |
10455 | 10644 | ||
10456 | trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len); | 10645 | trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len); |
10457 | nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC); | 10646 | nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1); |
10458 | } | 10647 | } |
10459 | EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt); | 10648 | EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt); |
10460 | 10649 | ||
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 49c9a482dd12..7ad70d6f0cc6 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -23,7 +23,8 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, | |||
23 | const u8 *buf, size_t len, gfp_t gfp); | 23 | const u8 *buf, size_t len, gfp_t gfp); |
24 | void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, | 24 | void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, |
25 | struct net_device *netdev, | 25 | struct net_device *netdev, |
26 | const u8 *buf, size_t len, gfp_t gfp); | 26 | const u8 *buf, size_t len, gfp_t gfp, |
27 | int uapsd_queues); | ||
27 | void nl80211_send_deauth(struct cfg80211_registered_device *rdev, | 28 | void nl80211_send_deauth(struct cfg80211_registered_device *rdev, |
28 | struct net_device *netdev, | 29 | struct net_device *netdev, |
29 | const u8 *buf, size_t len, gfp_t gfp); | 30 | const u8 *buf, size_t len, gfp_t gfp); |
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 56c2240c30ce..f6d457d6a558 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h | |||
@@ -915,4 +915,35 @@ rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev, | |||
915 | return ret; | 915 | return ret; |
916 | } | 916 | } |
917 | 917 | ||
918 | static inline int | ||
919 | rdev_add_tx_ts(struct cfg80211_registered_device *rdev, | ||
920 | struct net_device *dev, u8 tsid, const u8 *peer, | ||
921 | u8 user_prio, u16 admitted_time) | ||
922 | { | ||
923 | int ret = -EOPNOTSUPP; | ||
924 | |||
925 | trace_rdev_add_tx_ts(&rdev->wiphy, dev, tsid, peer, | ||
926 | user_prio, admitted_time); | ||
927 | if (rdev->ops->add_tx_ts) | ||
928 | ret = rdev->ops->add_tx_ts(&rdev->wiphy, dev, tsid, peer, | ||
929 | user_prio, admitted_time); | ||
930 | trace_rdev_return_int(&rdev->wiphy, ret); | ||
931 | |||
932 | return ret; | ||
933 | } | ||
934 | |||
935 | static inline int | ||
936 | rdev_del_tx_ts(struct cfg80211_registered_device *rdev, | ||
937 | struct net_device *dev, u8 tsid, const u8 *peer) | ||
938 | { | ||
939 | int ret = -EOPNOTSUPP; | ||
940 | |||
941 | trace_rdev_del_tx_ts(&rdev->wiphy, dev, tsid, peer); | ||
942 | if (rdev->ops->del_tx_ts) | ||
943 | ret = rdev->ops->del_tx_ts(&rdev->wiphy, dev, tsid, peer); | ||
944 | trace_rdev_return_int(&rdev->wiphy, ret); | ||
945 | |||
946 | return ret; | ||
947 | } | ||
948 | |||
918 | #endif /* __CFG80211_RDEV_OPS */ | 949 | #endif /* __CFG80211_RDEV_OPS */ |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1afdf45db38f..b725a31a4751 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> | 5 | * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> |
6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
6 | * | 7 | * |
7 | * Permission to use, copy, modify, and/or distribute this software for any | 8 | * Permission to use, copy, modify, and/or distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above | 9 | * purpose with or without fee is hereby granted, provided that the above |
@@ -798,6 +799,57 @@ static int reg_rules_intersect(const struct ieee80211_regdomain *rd1, | |||
798 | return 0; | 799 | return 0; |
799 | } | 800 | } |
800 | 801 | ||
802 | /* check whether old rule contains new rule */ | ||
803 | static bool rule_contains(struct ieee80211_reg_rule *r1, | ||
804 | struct ieee80211_reg_rule *r2) | ||
805 | { | ||
806 | /* for simplicity, currently consider only same flags */ | ||
807 | if (r1->flags != r2->flags) | ||
808 | return false; | ||
809 | |||
810 | /* verify r1 is more restrictive */ | ||
811 | if ((r1->power_rule.max_antenna_gain > | ||
812 | r2->power_rule.max_antenna_gain) || | ||
813 | r1->power_rule.max_eirp > r2->power_rule.max_eirp) | ||
814 | return false; | ||
815 | |||
816 | /* make sure r2's range is contained within r1 */ | ||
817 | if (r1->freq_range.start_freq_khz > r2->freq_range.start_freq_khz || | ||
818 | r1->freq_range.end_freq_khz < r2->freq_range.end_freq_khz) | ||
819 | return false; | ||
820 | |||
821 | /* and finally verify that r1.max_bw >= r2.max_bw */ | ||
822 | if (r1->freq_range.max_bandwidth_khz < | ||
823 | r2->freq_range.max_bandwidth_khz) | ||
824 | return false; | ||
825 | |||
826 | return true; | ||
827 | } | ||
828 | |||
829 | /* add or extend current rules. do nothing if rule is already contained */ | ||
830 | static void add_rule(struct ieee80211_reg_rule *rule, | ||
831 | struct ieee80211_reg_rule *reg_rules, u32 *n_rules) | ||
832 | { | ||
833 | struct ieee80211_reg_rule *tmp_rule; | ||
834 | int i; | ||
835 | |||
836 | for (i = 0; i < *n_rules; i++) { | ||
837 | tmp_rule = ®_rules[i]; | ||
838 | /* rule is already contained - do nothing */ | ||
839 | if (rule_contains(tmp_rule, rule)) | ||
840 | return; | ||
841 | |||
842 | /* extend rule if possible */ | ||
843 | if (rule_contains(rule, tmp_rule)) { | ||
844 | memcpy(tmp_rule, rule, sizeof(*rule)); | ||
845 | return; | ||
846 | } | ||
847 | } | ||
848 | |||
849 | memcpy(®_rules[*n_rules], rule, sizeof(*rule)); | ||
850 | (*n_rules)++; | ||
851 | } | ||
852 | |||
801 | /** | 853 | /** |
802 | * regdom_intersect - do the intersection between two regulatory domains | 854 | * regdom_intersect - do the intersection between two regulatory domains |
803 | * @rd1: first regulatory domain | 855 | * @rd1: first regulatory domain |
@@ -817,12 +869,10 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, | |||
817 | { | 869 | { |
818 | int r, size_of_regd; | 870 | int r, size_of_regd; |
819 | unsigned int x, y; | 871 | unsigned int x, y; |
820 | unsigned int num_rules = 0, rule_idx = 0; | 872 | unsigned int num_rules = 0; |
821 | const struct ieee80211_reg_rule *rule1, *rule2; | 873 | const struct ieee80211_reg_rule *rule1, *rule2; |
822 | struct ieee80211_reg_rule *intersected_rule; | 874 | struct ieee80211_reg_rule intersected_rule; |
823 | struct ieee80211_regdomain *rd; | 875 | struct ieee80211_regdomain *rd; |
824 | /* This is just a dummy holder to help us count */ | ||
825 | struct ieee80211_reg_rule dummy_rule; | ||
826 | 876 | ||
827 | if (!rd1 || !rd2) | 877 | if (!rd1 || !rd2) |
828 | return NULL; | 878 | return NULL; |
@@ -840,7 +890,7 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, | |||
840 | for (y = 0; y < rd2->n_reg_rules; y++) { | 890 | for (y = 0; y < rd2->n_reg_rules; y++) { |
841 | rule2 = &rd2->reg_rules[y]; | 891 | rule2 = &rd2->reg_rules[y]; |
842 | if (!reg_rules_intersect(rd1, rd2, rule1, rule2, | 892 | if (!reg_rules_intersect(rd1, rd2, rule1, rule2, |
843 | &dummy_rule)) | 893 | &intersected_rule)) |
844 | num_rules++; | 894 | num_rules++; |
845 | } | 895 | } |
846 | } | 896 | } |
@@ -855,34 +905,24 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, | |||
855 | if (!rd) | 905 | if (!rd) |
856 | return NULL; | 906 | return NULL; |
857 | 907 | ||
858 | for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) { | 908 | for (x = 0; x < rd1->n_reg_rules; x++) { |
859 | rule1 = &rd1->reg_rules[x]; | 909 | rule1 = &rd1->reg_rules[x]; |
860 | for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) { | 910 | for (y = 0; y < rd2->n_reg_rules; y++) { |
861 | rule2 = &rd2->reg_rules[y]; | 911 | rule2 = &rd2->reg_rules[y]; |
862 | /* | ||
863 | * This time around instead of using the stack lets | ||
864 | * write to the target rule directly saving ourselves | ||
865 | * a memcpy() | ||
866 | */ | ||
867 | intersected_rule = &rd->reg_rules[rule_idx]; | ||
868 | r = reg_rules_intersect(rd1, rd2, rule1, rule2, | 912 | r = reg_rules_intersect(rd1, rd2, rule1, rule2, |
869 | intersected_rule); | 913 | &intersected_rule); |
870 | /* | 914 | /* |
871 | * No need to memset here the intersected rule here as | 915 | * No need to memset here the intersected rule here as |
872 | * we're not using the stack anymore | 916 | * we're not using the stack anymore |
873 | */ | 917 | */ |
874 | if (r) | 918 | if (r) |
875 | continue; | 919 | continue; |
876 | rule_idx++; | ||
877 | } | ||
878 | } | ||
879 | 920 | ||
880 | if (rule_idx != num_rules) { | 921 | add_rule(&intersected_rule, rd->reg_rules, |
881 | kfree(rd); | 922 | &rd->n_reg_rules); |
882 | return NULL; | 923 | } |
883 | } | 924 | } |
884 | 925 | ||
885 | rd->n_reg_rules = num_rules; | ||
886 | rd->alpha2[0] = '9'; | 926 | rd->alpha2[0] = '9'; |
887 | rd->alpha2[1] = '8'; | 927 | rd->alpha2[1] = '8'; |
888 | rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region, | 928 | rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region, |
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 620a4b40d466..bda39f149810 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * cfg80211 scan result handling | 2 | * cfg80211 scan result handling |
3 | * | 3 | * |
4 | * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
5 | */ | 6 | */ |
6 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
7 | #include <linux/slab.h> | 8 | #include <linux/slab.h> |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 8bbeeb302216..dc1668ff543b 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -641,7 +641,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
641 | } | 641 | } |
642 | 642 | ||
643 | if (status != WLAN_STATUS_SUCCESS) { | 643 | if (status != WLAN_STATUS_SUCCESS) { |
644 | kfree(wdev->connect_keys); | 644 | kzfree(wdev->connect_keys); |
645 | wdev->connect_keys = NULL; | 645 | wdev->connect_keys = NULL; |
646 | wdev->ssid_len = 0; | 646 | wdev->ssid_len = 0; |
647 | if (bss) { | 647 | if (bss) { |
@@ -918,7 +918,7 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
918 | ASSERT_WDEV_LOCK(wdev); | 918 | ASSERT_WDEV_LOCK(wdev); |
919 | 919 | ||
920 | if (WARN_ON(wdev->connect_keys)) { | 920 | if (WARN_ON(wdev->connect_keys)) { |
921 | kfree(wdev->connect_keys); | 921 | kzfree(wdev->connect_keys); |
922 | wdev->connect_keys = NULL; | 922 | wdev->connect_keys = NULL; |
923 | } | 923 | } |
924 | 924 | ||
@@ -978,7 +978,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | |||
978 | 978 | ||
979 | ASSERT_WDEV_LOCK(wdev); | 979 | ASSERT_WDEV_LOCK(wdev); |
980 | 980 | ||
981 | kfree(wdev->connect_keys); | 981 | kzfree(wdev->connect_keys); |
982 | wdev->connect_keys = NULL; | 982 | wdev->connect_keys = NULL; |
983 | 983 | ||
984 | if (wdev->conn) | 984 | if (wdev->conn) |
diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 0c524cd76c83..625a6e6d1168 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h | |||
@@ -1896,6 +1896,51 @@ TRACE_EVENT(rdev_set_ap_chanwidth, | |||
1896 | WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG) | 1896 | WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG) |
1897 | ); | 1897 | ); |
1898 | 1898 | ||
1899 | TRACE_EVENT(rdev_add_tx_ts, | ||
1900 | TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, | ||
1901 | u8 tsid, const u8 *peer, u8 user_prio, u16 admitted_time), | ||
1902 | TP_ARGS(wiphy, netdev, tsid, peer, user_prio, admitted_time), | ||
1903 | TP_STRUCT__entry( | ||
1904 | WIPHY_ENTRY | ||
1905 | NETDEV_ENTRY | ||
1906 | MAC_ENTRY(peer) | ||
1907 | __field(u8, tsid) | ||
1908 | __field(u8, user_prio) | ||
1909 | __field(u16, admitted_time) | ||
1910 | ), | ||
1911 | TP_fast_assign( | ||
1912 | WIPHY_ASSIGN; | ||
1913 | NETDEV_ASSIGN; | ||
1914 | MAC_ASSIGN(peer, peer); | ||
1915 | __entry->tsid = tsid; | ||
1916 | __entry->user_prio = user_prio; | ||
1917 | __entry->admitted_time = admitted_time; | ||
1918 | ), | ||
1919 | TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", TSID %d, UP %d, time %d", | ||
1920 | WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), | ||
1921 | __entry->tsid, __entry->user_prio, __entry->admitted_time) | ||
1922 | ); | ||
1923 | |||
1924 | TRACE_EVENT(rdev_del_tx_ts, | ||
1925 | TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, | ||
1926 | u8 tsid, const u8 *peer), | ||
1927 | TP_ARGS(wiphy, netdev, tsid, peer), | ||
1928 | TP_STRUCT__entry( | ||
1929 | WIPHY_ENTRY | ||
1930 | NETDEV_ENTRY | ||
1931 | MAC_ENTRY(peer) | ||
1932 | __field(u8, tsid) | ||
1933 | ), | ||
1934 | TP_fast_assign( | ||
1935 | WIPHY_ASSIGN; | ||
1936 | NETDEV_ASSIGN; | ||
1937 | MAC_ASSIGN(peer, peer); | ||
1938 | __entry->tsid = tsid; | ||
1939 | ), | ||
1940 | TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", TSID %d", | ||
1941 | WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid) | ||
1942 | ); | ||
1943 | |||
1899 | /************************************************************* | 1944 | /************************************************************* |
1900 | * cfg80211 exported functions traces * | 1945 | * cfg80211 exported functions traces * |
1901 | *************************************************************/ | 1946 | *************************************************************/ |
diff --git a/net/wireless/util.c b/net/wireless/util.c index 728f1c0dc70d..5e233a577d0f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Wireless utility functions | 2 | * Wireless utility functions |
3 | * | 3 | * |
4 | * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright 2013-2014 Intel Mobile Communications GmbH | ||
5 | */ | 6 | */ |
6 | #include <linux/export.h> | 7 | #include <linux/export.h> |
7 | #include <linux/bitops.h> | 8 | #include <linux/bitops.h> |
@@ -796,7 +797,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) | |||
796 | netdev_err(dev, "failed to set mgtdef %d\n", i); | 797 | netdev_err(dev, "failed to set mgtdef %d\n", i); |
797 | } | 798 | } |
798 | 799 | ||
799 | kfree(wdev->connect_keys); | 800 | kzfree(wdev->connect_keys); |
800 | wdev->connect_keys = NULL; | 801 | wdev->connect_keys = NULL; |
801 | } | 802 | } |
802 | 803 | ||
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 11120bb14162..0f47948c572f 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c | |||
@@ -496,6 +496,8 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, | |||
496 | err = 0; | 496 | err = 0; |
497 | if (!err) { | 497 | if (!err) { |
498 | if (!addr) { | 498 | if (!addr) { |
499 | memset(wdev->wext.keys->data[idx], 0, | ||
500 | sizeof(wdev->wext.keys->data[idx])); | ||
499 | wdev->wext.keys->params[idx].key_len = 0; | 501 | wdev->wext.keys->params[idx].key_len = 0; |
500 | wdev->wext.keys->params[idx].cipher = 0; | 502 | wdev->wext.keys->params[idx].cipher = 0; |
501 | } | 503 | } |
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index c7e5c8eb4f24..368611c05739 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c | |||
@@ -57,7 +57,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, | |||
57 | err = cfg80211_connect(rdev, wdev->netdev, | 57 | err = cfg80211_connect(rdev, wdev->netdev, |
58 | &wdev->wext.connect, ck, prev_bssid); | 58 | &wdev->wext.connect, ck, prev_bssid); |
59 | if (err) | 59 | if (err) |
60 | kfree(ck); | 60 | kzfree(ck); |
61 | 61 | ||
62 | return err; | 62 | return err; |
63 | } | 63 | } |