diff options
author | Luca Coelho <luciano.coelho@intel.com> | 2018-06-09 02:14:44 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2018-06-18 16:40:32 -0400 |
commit | 41cbb0f5a29592874355e4159489eb08337cd50e (patch) | |
tree | ee56f9b9754452ae89a6f9b89efc63d2c360a50a /net/mac80211 | |
parent | b8042b3da925f390c1482bf9dc0898dc0b3ea7b5 (diff) |
mac80211: add support for HE
Add support for HE in mac80211 conforming with P802.11ax_D1.4.
Johannes: Fix another bug with the buf_size comparison in agg-rx.c.
Signed-off-by: Liad Kaufman <liad.kaufman@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Ido Yariv <idox.yariv@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Makefile | 1 | ||||
-rw-r--r-- | net/mac80211/agg-rx.c | 10 | ||||
-rw-r--r-- | net/mac80211/agg-tx.c | 19 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 5 | ||||
-rw-r--r-- | net/mac80211/he.c | 55 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 16 | ||||
-rw-r--r-- | net/mac80211/main.c | 19 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 288 | ||||
-rw-r--r-- | net/mac80211/rx.c | 127 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 15 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 20 | ||||
-rw-r--r-- | net/mac80211/trace.h | 2 | ||||
-rw-r--r-- | net/mac80211/util.c | 120 |
13 files changed, 661 insertions, 36 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index e3589ade62e0..bb707789ef2b 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -12,6 +12,7 @@ mac80211-y := \ | |||
12 | scan.o offchannel.o \ | 12 | scan.o offchannel.o \ |
13 | ht.o agg-tx.o agg-rx.o \ | 13 | ht.o agg-tx.o agg-rx.o \ |
14 | vht.o \ | 14 | vht.o \ |
15 | he.o \ | ||
15 | ibss.o \ | 16 | ibss.o \ |
16 | iface.o \ | 17 | iface.o \ |
17 | rate.o \ | 18 | rate.o \ |
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 3ffd853b483f..6a4f154c99f6 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c | |||
@@ -245,6 +245,7 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
245 | }; | 245 | }; |
246 | int i, ret = -EOPNOTSUPP; | 246 | int i, ret = -EOPNOTSUPP; |
247 | u16 status = WLAN_STATUS_REQUEST_DECLINED; | 247 | u16 status = WLAN_STATUS_REQUEST_DECLINED; |
248 | u16 max_buf_size; | ||
248 | 249 | ||
249 | if (tid >= IEEE80211_FIRST_TSPEC_TSID) { | 250 | if (tid >= IEEE80211_FIRST_TSPEC_TSID) { |
250 | ht_dbg(sta->sdata, | 251 | ht_dbg(sta->sdata, |
@@ -268,13 +269,18 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
268 | goto end; | 269 | goto end; |
269 | } | 270 | } |
270 | 271 | ||
272 | if (sta->sta.he_cap.has_he) | ||
273 | max_buf_size = IEEE80211_MAX_AMPDU_BUF; | ||
274 | else | ||
275 | max_buf_size = IEEE80211_MAX_AMPDU_BUF_HT; | ||
276 | |||
271 | /* sanity check for incoming parameters: | 277 | /* sanity check for incoming parameters: |
272 | * check if configuration can support the BA policy | 278 | * check if configuration can support the BA policy |
273 | * and if buffer size does not exceeds max value */ | 279 | * and if buffer size does not exceeds max value */ |
274 | /* XXX: check own ht delayed BA capability?? */ | 280 | /* XXX: check own ht delayed BA capability?? */ |
275 | if (((ba_policy != 1) && | 281 | if (((ba_policy != 1) && |
276 | (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) || | 282 | (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) || |
277 | (buf_size > IEEE80211_MAX_AMPDU_BUF_HT)) { | 283 | (buf_size > max_buf_size)) { |
278 | status = WLAN_STATUS_INVALID_QOS_PARAM; | 284 | status = WLAN_STATUS_INVALID_QOS_PARAM; |
279 | ht_dbg_ratelimited(sta->sdata, | 285 | ht_dbg_ratelimited(sta->sdata, |
280 | "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", | 286 | "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", |
@@ -283,7 +289,7 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, | |||
283 | } | 289 | } |
284 | /* determine default buffer size */ | 290 | /* determine default buffer size */ |
285 | if (buf_size == 0) | 291 | if (buf_size == 0) |
286 | buf_size = IEEE80211_MAX_AMPDU_BUF_HT; | 292 | buf_size = max_buf_size; |
287 | 293 | ||
288 | /* make sure the size doesn't exceed the maximum supported by the hw */ | 294 | /* make sure the size doesn't exceed the maximum supported by the hw */ |
289 | if (buf_size > sta->sta.max_rx_aggregation_subframes) | 295 | if (buf_size > sta->sta.max_rx_aggregation_subframes) |
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 86c6bc0432ba..69e831bc317b 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
@@ -463,6 +463,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) | |||
463 | .timeout = 0, | 463 | .timeout = 0, |
464 | }; | 464 | }; |
465 | int ret; | 465 | int ret; |
466 | u16 buf_size; | ||
466 | 467 | ||
467 | tid_tx = rcu_dereference_protected_tid_tx(sta, tid); | 468 | tid_tx = rcu_dereference_protected_tid_tx(sta, tid); |
468 | 469 | ||
@@ -511,11 +512,22 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) | |||
511 | sta->ampdu_mlme.addba_req_num[tid]++; | 512 | sta->ampdu_mlme.addba_req_num[tid]++; |
512 | spin_unlock_bh(&sta->lock); | 513 | spin_unlock_bh(&sta->lock); |
513 | 514 | ||
515 | if (sta->sta.he_cap.has_he) { | ||
516 | buf_size = local->hw.max_tx_aggregation_subframes; | ||
517 | } else { | ||
518 | /* | ||
519 | * We really should use what the driver told us it will | ||
520 | * transmit as the maximum, but certain APs (e.g. the | ||
521 | * LinkSys WRT120N with FW v1.0.07 build 002 Jun 18 2012) | ||
522 | * will crash when we use a lower number. | ||
523 | */ | ||
524 | buf_size = IEEE80211_MAX_AMPDU_BUF_HT; | ||
525 | } | ||
526 | |||
514 | /* send AddBA request */ | 527 | /* send AddBA request */ |
515 | ieee80211_send_addba_request(sdata, sta->sta.addr, tid, | 528 | ieee80211_send_addba_request(sdata, sta->sta.addr, tid, |
516 | tid_tx->dialog_token, params.ssn, | 529 | tid_tx->dialog_token, params.ssn, |
517 | IEEE80211_MAX_AMPDU_BUF_HT, | 530 | buf_size, tid_tx->timeout); |
518 | tid_tx->timeout); | ||
519 | } | 531 | } |
520 | 532 | ||
521 | /* | 533 | /* |
@@ -905,8 +917,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, | |||
905 | { | 917 | { |
906 | struct tid_ampdu_tx *tid_tx; | 918 | struct tid_ampdu_tx *tid_tx; |
907 | struct ieee80211_txq *txq; | 919 | struct ieee80211_txq *txq; |
908 | u16 capab, tid; | 920 | u16 capab, tid, buf_size; |
909 | u8 buf_size; | ||
910 | bool amsdu; | 921 | bool amsdu; |
911 | 922 | ||
912 | capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); | 923 | capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c4e2f7d2bcb8..02f3672e7b5e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1412,6 +1412,11 @@ static int sta_apply_parameters(struct ieee80211_local *local, | |||
1412 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, | 1412 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, |
1413 | params->vht_capa, sta); | 1413 | params->vht_capa, sta); |
1414 | 1414 | ||
1415 | if (params->he_capa) | ||
1416 | ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, | ||
1417 | (void *)params->he_capa, | ||
1418 | params->he_capa_len, sta); | ||
1419 | |||
1415 | if (params->opmode_notif_used) { | 1420 | if (params->opmode_notif_used) { |
1416 | /* returned value is only needed for rc update, but the | 1421 | /* returned value is only needed for rc update, but the |
1417 | * rc isn't initialized here yet, so ignore it | 1422 | * rc isn't initialized here yet, so ignore it |
diff --git a/net/mac80211/he.c b/net/mac80211/he.c new file mode 100644 index 000000000000..769078ed5a12 --- /dev/null +++ b/net/mac80211/he.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * HE handling | ||
3 | * | ||
4 | * Copyright(c) 2017 Intel Deutschland GmbH | ||
5 | * | ||
6 | * 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 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include "ieee80211_i.h" | ||
12 | |||
13 | void | ||
14 | ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, | ||
15 | struct ieee80211_supported_band *sband, | ||
16 | const u8 *he_cap_ie, u8 he_cap_len, | ||
17 | struct sta_info *sta) | ||
18 | { | ||
19 | struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap; | ||
20 | struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie; | ||
21 | u8 he_ppe_size; | ||
22 | u8 mcs_nss_size; | ||
23 | u8 he_total_size; | ||
24 | |||
25 | memset(he_cap, 0, sizeof(*he_cap)); | ||
26 | |||
27 | if (!he_cap_ie || !ieee80211_get_he_sta_cap(sband)) | ||
28 | return; | ||
29 | |||
30 | /* Make sure size is OK */ | ||
31 | mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap_ie_elem); | ||
32 | he_ppe_size = | ||
33 | ieee80211_he_ppe_size(he_cap_ie[sizeof(he_cap->he_cap_elem) + | ||
34 | mcs_nss_size], | ||
35 | he_cap_ie_elem->phy_cap_info); | ||
36 | he_total_size = sizeof(he_cap->he_cap_elem) + mcs_nss_size + | ||
37 | he_ppe_size; | ||
38 | if (he_cap_len < he_total_size) | ||
39 | return; | ||
40 | |||
41 | memcpy(&he_cap->he_cap_elem, he_cap_ie, sizeof(he_cap->he_cap_elem)); | ||
42 | |||
43 | /* HE Tx/Rx HE MCS NSS Support Field */ | ||
44 | memcpy(&he_cap->he_mcs_nss_supp, | ||
45 | &he_cap_ie[sizeof(he_cap->he_cap_elem)], mcs_nss_size); | ||
46 | |||
47 | /* Check if there are (optional) PPE Thresholds */ | ||
48 | if (he_cap->he_cap_elem.phy_cap_info[6] & | ||
49 | IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) | ||
50 | memcpy(he_cap->ppe_thres, | ||
51 | &he_cap_ie[sizeof(he_cap->he_cap_elem) + mcs_nss_size], | ||
52 | he_ppe_size); | ||
53 | |||
54 | he_cap->has_he = true; | ||
55 | } | ||
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a6c12c104c38..172aeae21ae9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -365,6 +365,7 @@ enum ieee80211_sta_flags { | |||
365 | IEEE80211_STA_DISABLE_160MHZ = BIT(13), | 365 | IEEE80211_STA_DISABLE_160MHZ = BIT(13), |
366 | IEEE80211_STA_DISABLE_WMM = BIT(14), | 366 | IEEE80211_STA_DISABLE_WMM = BIT(14), |
367 | IEEE80211_STA_ENABLE_RRM = BIT(15), | 367 | IEEE80211_STA_ENABLE_RRM = BIT(15), |
368 | IEEE80211_STA_DISABLE_HE = BIT(16), | ||
368 | }; | 369 | }; |
369 | 370 | ||
370 | struct ieee80211_mgd_auth_data { | 371 | struct ieee80211_mgd_auth_data { |
@@ -1454,6 +1455,10 @@ struct ieee802_11_elems { | |||
1454 | const struct ieee80211_vht_cap *vht_cap_elem; | 1455 | const struct ieee80211_vht_cap *vht_cap_elem; |
1455 | const struct ieee80211_vht_operation *vht_operation; | 1456 | const struct ieee80211_vht_operation *vht_operation; |
1456 | const struct ieee80211_meshconf_ie *mesh_config; | 1457 | const struct ieee80211_meshconf_ie *mesh_config; |
1458 | const u8 *he_cap; | ||
1459 | const struct ieee80211_he_operation *he_operation; | ||
1460 | const struct ieee80211_mu_edca_param_set *mu_edca_param_set; | ||
1461 | const u8 *uora_element; | ||
1457 | const u8 *mesh_id; | 1462 | const u8 *mesh_id; |
1458 | const u8 *peering; | 1463 | const u8 *peering; |
1459 | const __le16 *awake_window; | 1464 | const __le16 *awake_window; |
@@ -1483,6 +1488,7 @@ struct ieee802_11_elems { | |||
1483 | u8 ext_supp_rates_len; | 1488 | u8 ext_supp_rates_len; |
1484 | u8 wmm_info_len; | 1489 | u8 wmm_info_len; |
1485 | u8 wmm_param_len; | 1490 | u8 wmm_param_len; |
1491 | u8 he_cap_len; | ||
1486 | u8 mesh_id_len; | 1492 | u8 mesh_id_len; |
1487 | u8 peering_len; | 1493 | u8 peering_len; |
1488 | u8 preq_len; | 1494 | u8 preq_len; |
@@ -1825,6 +1831,13 @@ void ieee80211_get_vht_mask_from_cap(__le16 vht_cap, | |||
1825 | enum nl80211_chan_width | 1831 | enum nl80211_chan_width |
1826 | ieee80211_sta_rx_bw_to_chan_width(struct sta_info *sta); | 1832 | ieee80211_sta_rx_bw_to_chan_width(struct sta_info *sta); |
1827 | 1833 | ||
1834 | /* HE */ | ||
1835 | void | ||
1836 | ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, | ||
1837 | struct ieee80211_supported_band *sband, | ||
1838 | const u8 *he_cap_ie, u8 he_cap_len, | ||
1839 | struct sta_info *sta); | ||
1840 | |||
1828 | /* Spectrum management */ | 1841 | /* Spectrum management */ |
1829 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | 1842 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, |
1830 | struct ieee80211_mgmt *mgmt, | 1843 | struct ieee80211_mgmt *mgmt, |
@@ -2076,6 +2089,9 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, | |||
2076 | u32 cap); | 2089 | u32 cap); |
2077 | u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, | 2090 | u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, |
2078 | const struct cfg80211_chan_def *chandef); | 2091 | const struct cfg80211_chan_def *chandef); |
2092 | u8 *ieee80211_ie_build_he_cap(u8 *pos, | ||
2093 | const struct ieee80211_sta_he_cap *he_cap, | ||
2094 | u8 *end); | ||
2079 | int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, | 2095 | int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, |
2080 | const struct ieee80211_supported_band *sband, | 2096 | const struct ieee80211_supported_band *sband, |
2081 | const u8 *srates, int srates_len, u32 *rates); | 2097 | const u8 *srates, int srates_len, u32 *rates); |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 070f77862014..b33faba8cbbe 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.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 2013-2014 Intel Mobile Communications GmbH | 5 | * Copyright 2013-2014 Intel Mobile Communications GmbH |
6 | * Copyright (C) 2017 Intel Deutschland 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 |
@@ -825,7 +826,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
825 | int result, i; | 826 | int result, i; |
826 | enum nl80211_band band; | 827 | enum nl80211_band band; |
827 | int channels, max_bitrates; | 828 | int channels, max_bitrates; |
828 | bool supp_ht, supp_vht; | 829 | bool supp_ht, supp_vht, supp_he; |
829 | netdev_features_t feature_whitelist; | 830 | netdev_features_t feature_whitelist; |
830 | struct cfg80211_chan_def dflt_chandef = {}; | 831 | struct cfg80211_chan_def dflt_chandef = {}; |
831 | 832 | ||
@@ -905,6 +906,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
905 | max_bitrates = 0; | 906 | max_bitrates = 0; |
906 | supp_ht = false; | 907 | supp_ht = false; |
907 | supp_vht = false; | 908 | supp_vht = false; |
909 | supp_he = false; | ||
908 | for (band = 0; band < NUM_NL80211_BANDS; band++) { | 910 | for (band = 0; band < NUM_NL80211_BANDS; band++) { |
909 | struct ieee80211_supported_band *sband; | 911 | struct ieee80211_supported_band *sband; |
910 | 912 | ||
@@ -931,6 +933,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
931 | supp_ht = supp_ht || sband->ht_cap.ht_supported; | 933 | supp_ht = supp_ht || sband->ht_cap.ht_supported; |
932 | supp_vht = supp_vht || sband->vht_cap.vht_supported; | 934 | supp_vht = supp_vht || sband->vht_cap.vht_supported; |
933 | 935 | ||
936 | if (!supp_he) | ||
937 | supp_he = !!ieee80211_get_he_sta_cap(sband); | ||
938 | |||
934 | if (!sband->ht_cap.ht_supported) | 939 | if (!sband->ht_cap.ht_supported) |
935 | continue; | 940 | continue; |
936 | 941 | ||
@@ -1020,6 +1025,18 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
1020 | local->scan_ies_len += | 1025 | local->scan_ies_len += |
1021 | 2 + sizeof(struct ieee80211_vht_cap); | 1026 | 2 + sizeof(struct ieee80211_vht_cap); |
1022 | 1027 | ||
1028 | /* HE cap element is variable in size - set len to allow max size */ | ||
1029 | /* | ||
1030 | * TODO: 1 is added at the end of the calculation to accommodate for | ||
1031 | * the temporary placing of the HE capabilities IE under EXT. | ||
1032 | * Remove it once it is placed in the final place. | ||
1033 | */ | ||
1034 | if (supp_he) | ||
1035 | local->scan_ies_len += | ||
1036 | 2 + sizeof(struct ieee80211_he_cap_elem) + | ||
1037 | sizeof(struct ieee80211_he_mcs_nss_supp) + | ||
1038 | IEEE80211_HE_PPE_THRES_MAX_LEN + 1; | ||
1039 | |||
1023 | if (!local->ops->hw_scan) { | 1040 | if (!local->ops->hw_scan) { |
1024 | /* For hw_scan, driver needs to set these up. */ | 1041 | /* For hw_scan, driver needs to set these up. */ |
1025 | local->hw.wiphy->max_scan_ssids = 4; | 1042 | local->hw.wiphy->max_scan_ssids = 4; |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a44e5b4aaeda..0322d78007ad 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -149,6 +149,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | |||
149 | struct ieee80211_channel *channel, | 149 | struct ieee80211_channel *channel, |
150 | const struct ieee80211_ht_operation *ht_oper, | 150 | const struct ieee80211_ht_operation *ht_oper, |
151 | const struct ieee80211_vht_operation *vht_oper, | 151 | const struct ieee80211_vht_operation *vht_oper, |
152 | const struct ieee80211_he_operation *he_oper, | ||
152 | struct cfg80211_chan_def *chandef, bool tracking) | 153 | struct cfg80211_chan_def *chandef, bool tracking) |
153 | { | 154 | { |
154 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 155 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
@@ -207,7 +208,27 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | |||
207 | } | 208 | } |
208 | 209 | ||
209 | vht_chandef = *chandef; | 210 | vht_chandef = *chandef; |
210 | if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) { | 211 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && he_oper && |
212 | (le32_to_cpu(he_oper->he_oper_params) & | ||
213 | IEEE80211_HE_OPERATION_VHT_OPER_INFO)) { | ||
214 | struct ieee80211_vht_operation he_oper_vht_cap; | ||
215 | |||
216 | /* | ||
217 | * Set only first 3 bytes (other 2 aren't used in | ||
218 | * ieee80211_chandef_vht_oper() anyway) | ||
219 | */ | ||
220 | memcpy(&he_oper_vht_cap, he_oper->optional, 3); | ||
221 | he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0); | ||
222 | |||
223 | if (!ieee80211_chandef_vht_oper(&he_oper_vht_cap, | ||
224 | &vht_chandef)) { | ||
225 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) | ||
226 | sdata_info(sdata, | ||
227 | "HE AP VHT information is invalid, disable HE\n"); | ||
228 | ret = IEEE80211_STA_DISABLE_HE; | ||
229 | goto out; | ||
230 | } | ||
231 | } else if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) { | ||
211 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) | 232 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) |
212 | sdata_info(sdata, | 233 | sdata_info(sdata, |
213 | "AP VHT information is invalid, disable VHT\n"); | 234 | "AP VHT information is invalid, disable VHT\n"); |
@@ -300,12 +321,14 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, | |||
300 | const struct ieee80211_ht_cap *ht_cap, | 321 | const struct ieee80211_ht_cap *ht_cap, |
301 | const struct ieee80211_ht_operation *ht_oper, | 322 | const struct ieee80211_ht_operation *ht_oper, |
302 | const struct ieee80211_vht_operation *vht_oper, | 323 | const struct ieee80211_vht_operation *vht_oper, |
324 | const struct ieee80211_he_operation *he_oper, | ||
303 | const u8 *bssid, u32 *changed) | 325 | const u8 *bssid, u32 *changed) |
304 | { | 326 | { |
305 | struct ieee80211_local *local = sdata->local; | 327 | struct ieee80211_local *local = sdata->local; |
306 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 328 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
307 | struct ieee80211_supported_band *sband; | 329 | struct ieee80211_channel *chan = sdata->vif.bss_conf.chandef.chan; |
308 | struct ieee80211_channel *chan; | 330 | struct ieee80211_supported_band *sband = |
331 | local->hw.wiphy->bands[chan->band]; | ||
309 | struct cfg80211_chan_def chandef; | 332 | struct cfg80211_chan_def chandef; |
310 | u16 ht_opmode; | 333 | u16 ht_opmode; |
311 | u32 flags; | 334 | u32 flags; |
@@ -320,6 +343,11 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, | |||
320 | if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) | 343 | if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) |
321 | vht_oper = NULL; | 344 | vht_oper = NULL; |
322 | 345 | ||
346 | /* don't check HE if we associated as non-HE station */ | ||
347 | if (ifmgd->flags & IEEE80211_STA_DISABLE_HE || | ||
348 | !ieee80211_get_he_sta_cap(sband)) | ||
349 | he_oper = NULL; | ||
350 | |||
323 | if (WARN_ON_ONCE(!sta)) | 351 | if (WARN_ON_ONCE(!sta)) |
324 | return -EINVAL; | 352 | return -EINVAL; |
325 | 353 | ||
@@ -333,12 +361,9 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, | |||
333 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; | 361 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; |
334 | } | 362 | } |
335 | 363 | ||
336 | chan = sdata->vif.bss_conf.chandef.chan; | 364 | /* calculate new channel (type) based on HT/VHT/HE operation IEs */ |
337 | sband = local->hw.wiphy->bands[chan->band]; | ||
338 | |||
339 | /* calculate new channel (type) based on HT/VHT operation IEs */ | ||
340 | flags = ieee80211_determine_chantype(sdata, sband, chan, | 365 | flags = ieee80211_determine_chantype(sdata, sband, chan, |
341 | ht_oper, vht_oper, | 366 | ht_oper, vht_oper, he_oper, |
342 | &chandef, true); | 367 | &chandef, true); |
343 | 368 | ||
344 | /* | 369 | /* |
@@ -582,6 +607,34 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, | |||
582 | ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); | 607 | ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); |
583 | } | 608 | } |
584 | 609 | ||
610 | /* This function determines HE capability flags for the association | ||
611 | * and builds the IE. | ||
612 | */ | ||
613 | static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, | ||
614 | struct sk_buff *skb, | ||
615 | struct ieee80211_supported_band *sband) | ||
616 | { | ||
617 | u8 *pos; | ||
618 | const struct ieee80211_sta_he_cap *he_cap = NULL; | ||
619 | u8 he_cap_size; | ||
620 | |||
621 | he_cap = ieee80211_get_he_sta_cap(sband); | ||
622 | if (!he_cap) | ||
623 | return; | ||
624 | |||
625 | /* | ||
626 | * TODO: the 1 added is because this temporarily is under the EXTENSION | ||
627 | * IE. Get rid of it when it moves. | ||
628 | */ | ||
629 | he_cap_size = | ||
630 | 2 + 1 + sizeof(he_cap->he_cap_elem) + | ||
631 | ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) + | ||
632 | ieee80211_he_ppe_size(he_cap->ppe_thres[0], | ||
633 | he_cap->he_cap_elem.phy_cap_info); | ||
634 | pos = skb_put(skb, he_cap_size); | ||
635 | ieee80211_ie_build_he_cap(pos, he_cap, pos + he_cap_size); | ||
636 | } | ||
637 | |||
585 | static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | 638 | static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) |
586 | { | 639 | { |
587 | struct ieee80211_local *local = sdata->local; | 640 | struct ieee80211_local *local = sdata->local; |
@@ -643,6 +696,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
643 | 2 + 2 * sband->n_channels + /* supported channels */ | 696 | 2 + 2 * sband->n_channels + /* supported channels */ |
644 | 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ | 697 | 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ |
645 | 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ | 698 | 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ |
699 | 2 + 1 + sizeof(struct ieee80211_he_cap_elem) + /* HE */ | ||
700 | sizeof(struct ieee80211_he_mcs_nss_supp) + | ||
701 | IEEE80211_HE_PPE_THRES_MAX_LEN + | ||
646 | assoc_data->ie_len + /* extra IEs */ | 702 | assoc_data->ie_len + /* extra IEs */ |
647 | (assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) + | 703 | (assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) + |
648 | 9, /* WMM */ | 704 | 9, /* WMM */ |
@@ -827,11 +883,41 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
827 | offset = noffset; | 883 | offset = noffset; |
828 | } | 884 | } |
829 | 885 | ||
886 | /* if present, add any custom IEs that go before HE */ | ||
887 | if (assoc_data->ie_len) { | ||
888 | static const u8 before_he[] = { | ||
889 | /* | ||
890 | * no need to list the ones split off before VHT | ||
891 | * or generated here | ||
892 | */ | ||
893 | WLAN_EID_OPMODE_NOTIF, | ||
894 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FUTURE_CHAN_GUIDANCE, | ||
895 | /* 11ai elements */ | ||
896 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_SESSION, | ||
897 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_PUBLIC_KEY, | ||
898 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_KEY_CONFIRM, | ||
899 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_HLP_CONTAINER, | ||
900 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN, | ||
901 | /* TODO: add 11ah/11aj/11ak elements */ | ||
902 | }; | ||
903 | |||
904 | /* RIC already taken above, so no need to handle here anymore */ | ||
905 | noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, | ||
906 | before_he, ARRAY_SIZE(before_he), | ||
907 | offset); | ||
908 | pos = skb_put(skb, noffset - offset); | ||
909 | memcpy(pos, assoc_data->ie + offset, noffset - offset); | ||
910 | offset = noffset; | ||
911 | } | ||
912 | |||
830 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) | 913 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) |
831 | ieee80211_add_vht_ie(sdata, skb, sband, | 914 | ieee80211_add_vht_ie(sdata, skb, sband, |
832 | &assoc_data->ap_vht_cap); | 915 | &assoc_data->ap_vht_cap); |
833 | 916 | ||
834 | /* if present, add any custom non-vendor IEs that go after HT */ | 917 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) |
918 | ieee80211_add_he_ie(sdata, skb, sband); | ||
919 | |||
920 | /* if present, add any custom non-vendor IEs that go after HE */ | ||
835 | if (assoc_data->ie_len) { | 921 | if (assoc_data->ie_len) { |
836 | noffset = ieee80211_ie_split_vendor(assoc_data->ie, | 922 | noffset = ieee80211_ie_split_vendor(assoc_data->ie, |
837 | assoc_data->ie_len, | 923 | assoc_data->ie_len, |
@@ -898,6 +984,11 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, | |||
898 | struct ieee80211_hdr_3addr *nullfunc; | 984 | struct ieee80211_hdr_3addr *nullfunc; |
899 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 985 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
900 | 986 | ||
987 | /* Don't send NDPs when STA is connected HE */ | ||
988 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | ||
989 | !(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) | ||
990 | return; | ||
991 | |||
901 | skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif, | 992 | skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif, |
902 | !ieee80211_hw_check(&local->hw, DOESNT_SUPPORT_QOS_NDP)); | 993 | !ieee80211_hw_check(&local->hw, DOESNT_SUPPORT_QOS_NDP)); |
903 | if (!skb) | 994 | if (!skb) |
@@ -929,6 +1020,10 @@ static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, | |||
929 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) | 1020 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) |
930 | return; | 1021 | return; |
931 | 1022 | ||
1023 | /* Don't send NDPs when connected HE */ | ||
1024 | if (!(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE)) | ||
1025 | return; | ||
1026 | |||
932 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); | 1027 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); |
933 | if (!skb) | 1028 | if (!skb) |
934 | return; | 1029 | return; |
@@ -1700,9 +1795,11 @@ static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work) | |||
1700 | } | 1795 | } |
1701 | 1796 | ||
1702 | /* MLME */ | 1797 | /* MLME */ |
1703 | static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, | 1798 | static bool |
1704 | struct ieee80211_sub_if_data *sdata, | 1799 | ieee80211_sta_wmm_params(struct ieee80211_local *local, |
1705 | const u8 *wmm_param, size_t wmm_param_len) | 1800 | struct ieee80211_sub_if_data *sdata, |
1801 | const u8 *wmm_param, size_t wmm_param_len, | ||
1802 | const struct ieee80211_mu_edca_param_set *mu_edca) | ||
1706 | { | 1803 | { |
1707 | struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS]; | 1804 | struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS]; |
1708 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 1805 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
@@ -1749,6 +1846,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, | |||
1749 | sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ | 1846 | sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ |
1750 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) | 1847 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) |
1751 | uapsd = true; | 1848 | uapsd = true; |
1849 | params[ac].mu_edca = !!mu_edca; | ||
1850 | if (mu_edca) | ||
1851 | params[ac].mu_edca_param_rec = mu_edca->ac_bk; | ||
1752 | break; | 1852 | break; |
1753 | case 2: /* AC_VI */ | 1853 | case 2: /* AC_VI */ |
1754 | ac = IEEE80211_AC_VI; | 1854 | ac = IEEE80211_AC_VI; |
@@ -1756,6 +1856,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, | |||
1756 | sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ | 1856 | sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ |
1757 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) | 1857 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) |
1758 | uapsd = true; | 1858 | uapsd = true; |
1859 | params[ac].mu_edca = !!mu_edca; | ||
1860 | if (mu_edca) | ||
1861 | params[ac].mu_edca_param_rec = mu_edca->ac_vi; | ||
1759 | break; | 1862 | break; |
1760 | case 3: /* AC_VO */ | 1863 | case 3: /* AC_VO */ |
1761 | ac = IEEE80211_AC_VO; | 1864 | ac = IEEE80211_AC_VO; |
@@ -1763,6 +1866,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, | |||
1763 | sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ | 1866 | sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ |
1764 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) | 1867 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) |
1765 | uapsd = true; | 1868 | uapsd = true; |
1869 | params[ac].mu_edca = !!mu_edca; | ||
1870 | if (mu_edca) | ||
1871 | params[ac].mu_edca_param_rec = mu_edca->ac_vo; | ||
1766 | break; | 1872 | break; |
1767 | case 0: /* AC_BE */ | 1873 | case 0: /* AC_BE */ |
1768 | default: | 1874 | default: |
@@ -1771,6 +1877,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, | |||
1771 | sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ | 1877 | sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ |
1772 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) | 1878 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) |
1773 | uapsd = true; | 1879 | uapsd = true; |
1880 | params[ac].mu_edca = !!mu_edca; | ||
1881 | if (mu_edca) | ||
1882 | params[ac].mu_edca_param_rec = mu_edca->ac_be; | ||
1774 | break; | 1883 | break; |
1775 | } | 1884 | } |
1776 | 1885 | ||
@@ -3021,6 +3130,25 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | |||
3021 | goto out; | 3130 | goto out; |
3022 | } | 3131 | } |
3023 | 3132 | ||
3133 | /* | ||
3134 | * If AP doesn't support HT, or it doesn't have HE mandatory IEs, mark | ||
3135 | * HE as disabled. If on the 5GHz band, make sure it supports VHT. | ||
3136 | */ | ||
3137 | if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || | ||
3138 | (sband->band == NL80211_BAND_5GHZ && | ||
3139 | ifmgd->flags & IEEE80211_STA_DISABLE_VHT) || | ||
3140 | (!elems.he_cap && !elems.he_operation)) | ||
3141 | ifmgd->flags |= IEEE80211_STA_DISABLE_HE; | ||
3142 | |||
3143 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && | ||
3144 | (!elems.he_cap || !elems.he_operation)) { | ||
3145 | mutex_unlock(&sdata->local->sta_mtx); | ||
3146 | sdata_info(sdata, | ||
3147 | "HE AP is missing HE capability/operation\n"); | ||
3148 | ret = false; | ||
3149 | goto out; | ||
3150 | } | ||
3151 | |||
3024 | /* Set up internal HT/VHT capabilities */ | 3152 | /* Set up internal HT/VHT capabilities */ |
3025 | if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | 3153 | if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) |
3026 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | 3154 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, |
@@ -3030,6 +3158,48 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | |||
3030 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, | 3158 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, |
3031 | elems.vht_cap_elem, sta); | 3159 | elems.vht_cap_elem, sta); |
3032 | 3160 | ||
3161 | if (elems.he_operation && !(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && | ||
3162 | elems.he_cap) { | ||
3163 | ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, | ||
3164 | elems.he_cap, | ||
3165 | elems.he_cap_len, | ||
3166 | sta); | ||
3167 | |||
3168 | bss_conf->he_support = sta->sta.he_cap.has_he; | ||
3169 | } else { | ||
3170 | bss_conf->he_support = false; | ||
3171 | } | ||
3172 | |||
3173 | if (bss_conf->he_support) { | ||
3174 | u32 he_oper_params = | ||
3175 | le32_to_cpu(elems.he_operation->he_oper_params); | ||
3176 | |||
3177 | bss_conf->bss_color = he_oper_params & | ||
3178 | IEEE80211_HE_OPERATION_BSS_COLOR_MASK; | ||
3179 | bss_conf->htc_trig_based_pkt_ext = | ||
3180 | (he_oper_params & | ||
3181 | IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK) << | ||
3182 | IEEE80211_HE_OPERATION_DFLT_PE_DURATION_OFFSET; | ||
3183 | bss_conf->frame_time_rts_th = | ||
3184 | (he_oper_params & | ||
3185 | IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK) << | ||
3186 | IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET; | ||
3187 | |||
3188 | bss_conf->multi_sta_back_32bit = | ||
3189 | sta->sta.he_cap.he_cap_elem.mac_cap_info[2] & | ||
3190 | IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP; | ||
3191 | |||
3192 | bss_conf->ack_enabled = | ||
3193 | sta->sta.he_cap.he_cap_elem.mac_cap_info[2] & | ||
3194 | IEEE80211_HE_MAC_CAP2_ACK_EN; | ||
3195 | |||
3196 | bss_conf->uora_exists = !!elems.uora_element; | ||
3197 | if (elems.uora_element) | ||
3198 | bss_conf->uora_ocw_range = elems.uora_element[0]; | ||
3199 | |||
3200 | /* TODO: OPEN: what happens if BSS color disable is set? */ | ||
3201 | } | ||
3202 | |||
3033 | /* | 3203 | /* |
3034 | * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data | 3204 | * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data |
3035 | * in their association response, so ignore that data for our own | 3205 | * in their association response, so ignore that data for our own |
@@ -3089,7 +3259,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | |||
3089 | if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) { | 3259 | if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) { |
3090 | ieee80211_set_wmm_default(sdata, false, false); | 3260 | ieee80211_set_wmm_default(sdata, false, false); |
3091 | } else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, | 3261 | } else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, |
3092 | elems.wmm_param_len)) { | 3262 | elems.wmm_param_len, |
3263 | elems.mu_edca_param_set)) { | ||
3093 | /* still enable QoS since we might have HT/VHT */ | 3264 | /* still enable QoS since we might have HT/VHT */ |
3094 | ieee80211_set_wmm_default(sdata, false, true); | 3265 | ieee80211_set_wmm_default(sdata, false, true); |
3095 | /* set the disable-WMM flag in this case to disable | 3266 | /* set the disable-WMM flag in this case to disable |
@@ -3603,7 +3774,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
3603 | 3774 | ||
3604 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && | 3775 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && |
3605 | ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, | 3776 | ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, |
3606 | elems.wmm_param_len)) | 3777 | elems.wmm_param_len, |
3778 | elems.mu_edca_param_set)) | ||
3607 | changed |= BSS_CHANGED_QOS; | 3779 | changed |= BSS_CHANGED_QOS; |
3608 | 3780 | ||
3609 | /* | 3781 | /* |
@@ -3642,7 +3814,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
3642 | 3814 | ||
3643 | if (ieee80211_config_bw(sdata, sta, | 3815 | if (ieee80211_config_bw(sdata, sta, |
3644 | elems.ht_cap_elem, elems.ht_operation, | 3816 | elems.ht_cap_elem, elems.ht_operation, |
3645 | elems.vht_operation, bssid, &changed)) { | 3817 | elems.vht_operation, elems.he_operation, |
3818 | bssid, &changed)) { | ||
3646 | mutex_unlock(&local->sta_mtx); | 3819 | mutex_unlock(&local->sta_mtx); |
3647 | sdata_info(sdata, | 3820 | sdata_info(sdata, |
3648 | "failed to follow AP %pM bandwidth change, disconnect\n", | 3821 | "failed to follow AP %pM bandwidth change, disconnect\n", |
@@ -4279,6 +4452,66 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, | |||
4279 | return chains; | 4452 | return chains; |
4280 | } | 4453 | } |
4281 | 4454 | ||
4455 | static bool | ||
4456 | ieee80211_verify_sta_he_mcs_support(struct ieee80211_supported_band *sband, | ||
4457 | const struct ieee80211_he_operation *he_op) | ||
4458 | { | ||
4459 | const struct ieee80211_sta_he_cap *sta_he_cap = | ||
4460 | ieee80211_get_he_sta_cap(sband); | ||
4461 | u16 ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); | ||
4462 | int i; | ||
4463 | |||
4464 | if (!sta_he_cap || !he_op) | ||
4465 | return false; | ||
4466 | |||
4467 | /* Need to go over for 80MHz, 160MHz and for 80+80 */ | ||
4468 | for (i = 0; i < 3; i++) { | ||
4469 | const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp = | ||
4470 | &sta_he_cap->he_mcs_nss_supp; | ||
4471 | u16 sta_mcs_map_rx = | ||
4472 | le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]); | ||
4473 | u16 sta_mcs_map_tx = | ||
4474 | le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]); | ||
4475 | u8 nss; | ||
4476 | bool verified = true; | ||
4477 | |||
4478 | /* | ||
4479 | * For each band there is a maximum of 8 spatial streams | ||
4480 | * possible. Each of the sta_mcs_map_* is a 16-bit struct built | ||
4481 | * of 2 bits per NSS (1-8), with the values defined in enum | ||
4482 | * ieee80211_he_mcs_support. Need to make sure STA TX and RX | ||
4483 | * capabilities aren't less than the AP's minimum requirements | ||
4484 | * for this HE BSS per SS. | ||
4485 | * It is enough to find one such band that meets the reqs. | ||
4486 | */ | ||
4487 | for (nss = 8; nss > 0; nss--) { | ||
4488 | u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3; | ||
4489 | u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3; | ||
4490 | u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; | ||
4491 | |||
4492 | if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED) | ||
4493 | continue; | ||
4494 | |||
4495 | /* | ||
4496 | * Make sure the HE AP doesn't require MCSs that aren't | ||
4497 | * supported by the client | ||
4498 | */ | ||
4499 | if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || | ||
4500 | sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || | ||
4501 | (ap_val > sta_rx_val) || (ap_val > sta_tx_val)) { | ||
4502 | verified = false; | ||
4503 | break; | ||
4504 | } | ||
4505 | } | ||
4506 | |||
4507 | if (verified) | ||
4508 | return true; | ||
4509 | } | ||
4510 | |||
4511 | /* If here, STA doesn't meet AP's HE min requirements */ | ||
4512 | return false; | ||
4513 | } | ||
4514 | |||
4282 | static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | 4515 | static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, |
4283 | struct cfg80211_bss *cbss) | 4516 | struct cfg80211_bss *cbss) |
4284 | { | 4517 | { |
@@ -4287,6 +4520,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | |||
4287 | const struct ieee80211_ht_cap *ht_cap = NULL; | 4520 | const struct ieee80211_ht_cap *ht_cap = NULL; |
4288 | const struct ieee80211_ht_operation *ht_oper = NULL; | 4521 | const struct ieee80211_ht_operation *ht_oper = NULL; |
4289 | const struct ieee80211_vht_operation *vht_oper = NULL; | 4522 | const struct ieee80211_vht_operation *vht_oper = NULL; |
4523 | const struct ieee80211_he_operation *he_oper = NULL; | ||
4290 | struct ieee80211_supported_band *sband; | 4524 | struct ieee80211_supported_band *sband; |
4291 | struct cfg80211_chan_def chandef; | 4525 | struct cfg80211_chan_def chandef; |
4292 | int ret; | 4526 | int ret; |
@@ -4342,6 +4576,25 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | |||
4342 | } | 4576 | } |
4343 | } | 4577 | } |
4344 | 4578 | ||
4579 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && | ||
4580 | ieee80211_get_he_sta_cap(sband)) { | ||
4581 | const struct cfg80211_bss_ies *ies; | ||
4582 | const u8 *he_oper_ie; | ||
4583 | |||
4584 | ies = rcu_dereference(cbss->ies); | ||
4585 | he_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_OPERATION, | ||
4586 | ies->data, ies->len); | ||
4587 | if (he_oper_ie && | ||
4588 | he_oper_ie[1] == ieee80211_he_oper_size(&he_oper_ie[3])) | ||
4589 | he_oper = (void *)(he_oper_ie + 3); | ||
4590 | else | ||
4591 | he_oper = NULL; | ||
4592 | |||
4593 | if (!he_oper || | ||
4594 | !ieee80211_verify_sta_he_mcs_support(sband, he_oper)) | ||
4595 | ifmgd->flags |= IEEE80211_STA_DISABLE_HE; | ||
4596 | } | ||
4597 | |||
4345 | /* Allow VHT if at least one channel on the sband supports 80 MHz */ | 4598 | /* Allow VHT if at least one channel on the sband supports 80 MHz */ |
4346 | have_80mhz = false; | 4599 | have_80mhz = false; |
4347 | for (i = 0; i < sband->n_channels; i++) { | 4600 | for (i = 0; i < sband->n_channels; i++) { |
@@ -4358,7 +4611,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | |||
4358 | 4611 | ||
4359 | ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, | 4612 | ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, |
4360 | cbss->channel, | 4613 | cbss->channel, |
4361 | ht_oper, vht_oper, | 4614 | ht_oper, vht_oper, he_oper, |
4362 | &chandef, false); | 4615 | &chandef, false); |
4363 | 4616 | ||
4364 | sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), | 4617 | sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), |
@@ -4764,8 +5017,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
4764 | req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { | 5017 | req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { |
4765 | ifmgd->flags |= IEEE80211_STA_DISABLE_HT; | 5018 | ifmgd->flags |= IEEE80211_STA_DISABLE_HT; |
4766 | ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; | 5019 | ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; |
5020 | ifmgd->flags |= IEEE80211_STA_DISABLE_HE; | ||
4767 | netdev_info(sdata->dev, | 5021 | netdev_info(sdata->dev, |
4768 | "disabling HT/VHT due to WEP/TKIP use\n"); | 5022 | "disabling HE/HT/VHT due to WEP/TKIP use\n"); |
4769 | } | 5023 | } |
4770 | } | 5024 | } |
4771 | 5025 | ||
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 756ba176db1e..a16ba568e2a3 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -175,6 +175,20 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, | |||
175 | len += 12; | 175 | len += 12; |
176 | } | 176 | } |
177 | 177 | ||
178 | if (status->encoding == RX_ENC_HE && | ||
179 | status->flag & RX_FLAG_RADIOTAP_HE) { | ||
180 | len = ALIGN(len, 2); | ||
181 | len += 12; | ||
182 | BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he) != 12); | ||
183 | } | ||
184 | |||
185 | if (status->encoding == RX_ENC_HE && | ||
186 | status->flag & RX_FLAG_RADIOTAP_HE_MU) { | ||
187 | len = ALIGN(len, 2); | ||
188 | len += 12; | ||
189 | BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he_mu) != 12); | ||
190 | } | ||
191 | |||
178 | if (status->chains) { | 192 | if (status->chains) { |
179 | /* antenna and antenna signal fields */ | 193 | /* antenna and antenna signal fields */ |
180 | len += 2 * hweight8(status->chains); | 194 | len += 2 * hweight8(status->chains); |
@@ -263,6 +277,19 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, | |||
263 | int mpdulen, chain; | 277 | int mpdulen, chain; |
264 | unsigned long chains = status->chains; | 278 | unsigned long chains = status->chains; |
265 | struct ieee80211_vendor_radiotap rtap = {}; | 279 | struct ieee80211_vendor_radiotap rtap = {}; |
280 | struct ieee80211_radiotap_he he = {}; | ||
281 | struct ieee80211_radiotap_he_mu he_mu = {}; | ||
282 | |||
283 | if (status->flag & RX_FLAG_RADIOTAP_HE) { | ||
284 | he = *(struct ieee80211_radiotap_he *)skb->data; | ||
285 | skb_pull(skb, sizeof(he)); | ||
286 | WARN_ON_ONCE(status->encoding != RX_ENC_HE); | ||
287 | } | ||
288 | |||
289 | if (status->flag & RX_FLAG_RADIOTAP_HE_MU) { | ||
290 | he_mu = *(struct ieee80211_radiotap_he_mu *)skb->data; | ||
291 | skb_pull(skb, sizeof(he_mu)); | ||
292 | } | ||
266 | 293 | ||
267 | if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { | 294 | if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { |
268 | rtap = *(struct ieee80211_vendor_radiotap *)skb->data; | 295 | rtap = *(struct ieee80211_vendor_radiotap *)skb->data; |
@@ -520,6 +547,89 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, | |||
520 | *pos++ = flags; | 547 | *pos++ = flags; |
521 | } | 548 | } |
522 | 549 | ||
550 | if (status->encoding == RX_ENC_HE && | ||
551 | status->flag & RX_FLAG_RADIOTAP_HE) { | ||
552 | #define HE_PREP(f, val) cpu_to_le16(FIELD_PREP(IEEE80211_RADIOTAP_HE_##f, val)) | ||
553 | |||
554 | if (status->enc_flags & RX_ENC_FLAG_STBC_MASK) { | ||
555 | he.data6 |= HE_PREP(DATA6_NSTS, | ||
556 | FIELD_GET(RX_ENC_FLAG_STBC_MASK, | ||
557 | status->enc_flags)); | ||
558 | he.data3 |= HE_PREP(DATA3_STBC, 1); | ||
559 | } else { | ||
560 | he.data6 |= HE_PREP(DATA6_NSTS, status->nss); | ||
561 | } | ||
562 | |||
563 | #define CHECK_GI(s) \ | ||
564 | BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_GI_##s != \ | ||
565 | (int)NL80211_RATE_INFO_HE_GI_##s) | ||
566 | |||
567 | CHECK_GI(0_8); | ||
568 | CHECK_GI(1_6); | ||
569 | CHECK_GI(3_2); | ||
570 | |||
571 | he.data3 |= HE_PREP(DATA3_DATA_MCS, status->rate_idx); | ||
572 | he.data3 |= HE_PREP(DATA3_DATA_DCM, status->he_dcm); | ||
573 | he.data3 |= HE_PREP(DATA3_CODING, | ||
574 | !!(status->enc_flags & RX_ENC_FLAG_LDPC)); | ||
575 | |||
576 | he.data5 |= HE_PREP(DATA5_GI, status->he_gi); | ||
577 | |||
578 | switch (status->bw) { | ||
579 | case RATE_INFO_BW_20: | ||
580 | he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, | ||
581 | IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ); | ||
582 | break; | ||
583 | case RATE_INFO_BW_40: | ||
584 | he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, | ||
585 | IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ); | ||
586 | break; | ||
587 | case RATE_INFO_BW_80: | ||
588 | he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, | ||
589 | IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ); | ||
590 | break; | ||
591 | case RATE_INFO_BW_160: | ||
592 | he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, | ||
593 | IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ); | ||
594 | break; | ||
595 | case RATE_INFO_BW_HE_RU: | ||
596 | #define CHECK_RU_ALLOC(s) \ | ||
597 | BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_##s##T != \ | ||
598 | NL80211_RATE_INFO_HE_RU_ALLOC_##s + 4) | ||
599 | |||
600 | CHECK_RU_ALLOC(26); | ||
601 | CHECK_RU_ALLOC(52); | ||
602 | CHECK_RU_ALLOC(106); | ||
603 | CHECK_RU_ALLOC(242); | ||
604 | CHECK_RU_ALLOC(484); | ||
605 | CHECK_RU_ALLOC(996); | ||
606 | CHECK_RU_ALLOC(2x996); | ||
607 | |||
608 | he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC, | ||
609 | status->he_ru + 4); | ||
610 | break; | ||
611 | default: | ||
612 | WARN_ONCE(1, "Invalid SU BW %d\n", status->bw); | ||
613 | } | ||
614 | |||
615 | /* ensure 2 byte alignment */ | ||
616 | while ((pos - (u8 *)rthdr) & 1) | ||
617 | pos++; | ||
618 | rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE); | ||
619 | memcpy(pos, &he, sizeof(he)); | ||
620 | pos += sizeof(he); | ||
621 | } | ||
622 | |||
623 | if (status->encoding == RX_ENC_HE && | ||
624 | status->flag & RX_FLAG_RADIOTAP_HE_MU) { | ||
625 | /* ensure 2 byte alignment */ | ||
626 | while ((pos - (u8 *)rthdr) & 1) | ||
627 | pos++; | ||
628 | rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE_MU); | ||
629 | memcpy(pos, &he_mu, sizeof(he_mu)); | ||
630 | pos += sizeof(he_mu); | ||
631 | } | ||
632 | |||
523 | for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) { | 633 | for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) { |
524 | *pos++ = status->chain_signal[chain]; | 634 | *pos++ = status->chain_signal[chain]; |
525 | *pos++ = chain; | 635 | *pos++ = chain; |
@@ -613,6 +723,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, | |||
613 | rcu_dereference(local->monitor_sdata); | 723 | rcu_dereference(local->monitor_sdata); |
614 | bool only_monitor = false; | 724 | bool only_monitor = false; |
615 | 725 | ||
726 | if (status->flag & RX_FLAG_RADIOTAP_HE) | ||
727 | rtap_space += sizeof(struct ieee80211_radiotap_he); | ||
728 | |||
729 | if (status->flag & RX_FLAG_RADIOTAP_HE_MU) | ||
730 | rtap_space += sizeof(struct ieee80211_radiotap_he_mu); | ||
731 | |||
616 | if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { | 732 | if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { |
617 | struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data; | 733 | struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data; |
618 | 734 | ||
@@ -3386,8 +3502,7 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, | |||
3386 | status = IEEE80211_SKB_RXCB((rx->skb)); | 3502 | status = IEEE80211_SKB_RXCB((rx->skb)); |
3387 | 3503 | ||
3388 | sband = rx->local->hw.wiphy->bands[status->band]; | 3504 | sband = rx->local->hw.wiphy->bands[status->band]; |
3389 | if (!(status->encoding == RX_ENC_HT) && | 3505 | if (status->encoding == RX_ENC_LEGACY) |
3390 | !(status->encoding == RX_ENC_VHT)) | ||
3391 | rate = &sband->bitrates[status->rate_idx]; | 3506 | rate = &sband->bitrates[status->rate_idx]; |
3392 | 3507 | ||
3393 | ieee80211_rx_cooked_monitor(rx, rate); | 3508 | ieee80211_rx_cooked_monitor(rx, rate); |
@@ -4386,6 +4501,14 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, | |||
4386 | status->rate_idx, status->nss)) | 4501 | status->rate_idx, status->nss)) |
4387 | goto drop; | 4502 | goto drop; |
4388 | break; | 4503 | break; |
4504 | case RX_ENC_HE: | ||
4505 | if (WARN_ONCE(status->rate_idx > 11 || | ||
4506 | !status->nss || | ||
4507 | status->nss > 8, | ||
4508 | "Rate marked as an HE rate but data is invalid: MCS: %d, NSS: %d\n", | ||
4509 | status->rate_idx, status->nss)) | ||
4510 | goto drop; | ||
4511 | break; | ||
4389 | default: | 4512 | default: |
4390 | WARN_ON_ONCE(1); | 4513 | WARN_ON_ONCE(1); |
4391 | /* fall through */ | 4514 | /* fall through */ |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aa96fddfbfc2..aa8fe771a8db 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -1323,6 +1323,11 @@ static void ieee80211_send_null_response(struct sta_info *sta, int tid, | |||
1323 | struct ieee80211_tx_info *info; | 1323 | struct ieee80211_tx_info *info; |
1324 | struct ieee80211_chanctx_conf *chanctx_conf; | 1324 | struct ieee80211_chanctx_conf *chanctx_conf; |
1325 | 1325 | ||
1326 | /* Don't send NDPs when STA is connected HE */ | ||
1327 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | ||
1328 | !(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE)) | ||
1329 | return; | ||
1330 | |||
1326 | if (qos) { | 1331 | if (qos) { |
1327 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | | 1332 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | |
1328 | IEEE80211_STYPE_QOS_NULLFUNC | | 1333 | IEEE80211_STYPE_QOS_NULLFUNC | |
@@ -1968,7 +1973,7 @@ sta_get_last_rx_stats(struct sta_info *sta) | |||
1968 | return stats; | 1973 | return stats; |
1969 | } | 1974 | } |
1970 | 1975 | ||
1971 | static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, | 1976 | static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate, |
1972 | struct rate_info *rinfo) | 1977 | struct rate_info *rinfo) |
1973 | { | 1978 | { |
1974 | rinfo->bw = STA_STATS_GET(BW, rate); | 1979 | rinfo->bw = STA_STATS_GET(BW, rate); |
@@ -2005,6 +2010,14 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, | |||
2005 | rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); | 2010 | rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); |
2006 | break; | 2011 | break; |
2007 | } | 2012 | } |
2013 | case STA_STATS_RATE_TYPE_HE: | ||
2014 | rinfo->flags = RATE_INFO_FLAGS_HE_MCS; | ||
2015 | rinfo->mcs = STA_STATS_GET(HE_MCS, rate); | ||
2016 | rinfo->nss = STA_STATS_GET(HE_NSS, rate); | ||
2017 | rinfo->he_gi = STA_STATS_GET(HE_GI, rate); | ||
2018 | rinfo->he_ru_alloc = STA_STATS_GET(HE_RU, rate); | ||
2019 | rinfo->he_dcm = STA_STATS_GET(HE_DCM, rate); | ||
2020 | break; | ||
2008 | } | 2021 | } |
2009 | } | 2022 | } |
2010 | 2023 | ||
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 81b35f623792..9a04327d71d1 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -170,7 +170,7 @@ struct tid_ampdu_tx { | |||
170 | u8 dialog_token; | 170 | u8 dialog_token; |
171 | u8 stop_initiator; | 171 | u8 stop_initiator; |
172 | bool tx_stop; | 172 | bool tx_stop; |
173 | u8 buf_size; | 173 | u16 buf_size; |
174 | 174 | ||
175 | u16 failed_bar_ssn; | 175 | u16 failed_bar_ssn; |
176 | bool bar_pending; | 176 | bool bar_pending; |
@@ -405,7 +405,7 @@ struct ieee80211_sta_rx_stats { | |||
405 | int last_signal; | 405 | int last_signal; |
406 | u8 chains; | 406 | u8 chains; |
407 | s8 chain_signal_last[IEEE80211_MAX_CHAINS]; | 407 | s8 chain_signal_last[IEEE80211_MAX_CHAINS]; |
408 | u16 last_rate; | 408 | u32 last_rate; |
409 | struct u64_stats_sync syncp; | 409 | struct u64_stats_sync syncp; |
410 | u64 bytes; | 410 | u64 bytes; |
411 | u64 msdu[IEEE80211_NUM_TIDS + 1]; | 411 | u64 msdu[IEEE80211_NUM_TIDS + 1]; |
@@ -764,6 +764,7 @@ enum sta_stats_type { | |||
764 | STA_STATS_RATE_TYPE_LEGACY, | 764 | STA_STATS_RATE_TYPE_LEGACY, |
765 | STA_STATS_RATE_TYPE_HT, | 765 | STA_STATS_RATE_TYPE_HT, |
766 | STA_STATS_RATE_TYPE_VHT, | 766 | STA_STATS_RATE_TYPE_VHT, |
767 | STA_STATS_RATE_TYPE_HE, | ||
767 | }; | 768 | }; |
768 | 769 | ||
769 | #define STA_STATS_FIELD_HT_MCS GENMASK( 7, 0) | 770 | #define STA_STATS_FIELD_HT_MCS GENMASK( 7, 0) |
@@ -771,9 +772,14 @@ enum sta_stats_type { | |||
771 | #define STA_STATS_FIELD_LEGACY_BAND GENMASK( 7, 4) | 772 | #define STA_STATS_FIELD_LEGACY_BAND GENMASK( 7, 4) |
772 | #define STA_STATS_FIELD_VHT_MCS GENMASK( 3, 0) | 773 | #define STA_STATS_FIELD_VHT_MCS GENMASK( 3, 0) |
773 | #define STA_STATS_FIELD_VHT_NSS GENMASK( 7, 4) | 774 | #define STA_STATS_FIELD_VHT_NSS GENMASK( 7, 4) |
775 | #define STA_STATS_FIELD_HE_MCS GENMASK( 3, 0) | ||
776 | #define STA_STATS_FIELD_HE_NSS GENMASK( 7, 4) | ||
774 | #define STA_STATS_FIELD_BW GENMASK(11, 8) | 777 | #define STA_STATS_FIELD_BW GENMASK(11, 8) |
775 | #define STA_STATS_FIELD_SGI GENMASK(12, 12) | 778 | #define STA_STATS_FIELD_SGI GENMASK(12, 12) |
776 | #define STA_STATS_FIELD_TYPE GENMASK(15, 13) | 779 | #define STA_STATS_FIELD_TYPE GENMASK(15, 13) |
780 | #define STA_STATS_FIELD_HE_RU GENMASK(18, 16) | ||
781 | #define STA_STATS_FIELD_HE_GI GENMASK(20, 19) | ||
782 | #define STA_STATS_FIELD_HE_DCM GENMASK(21, 21) | ||
777 | 783 | ||
778 | #define STA_STATS_FIELD(_n, _v) FIELD_PREP(STA_STATS_FIELD_ ## _n, _v) | 784 | #define STA_STATS_FIELD(_n, _v) FIELD_PREP(STA_STATS_FIELD_ ## _n, _v) |
779 | #define STA_STATS_GET(_n, _v) FIELD_GET(STA_STATS_FIELD_ ## _n, _v) | 785 | #define STA_STATS_GET(_n, _v) FIELD_GET(STA_STATS_FIELD_ ## _n, _v) |
@@ -782,7 +788,7 @@ enum sta_stats_type { | |||
782 | 788 | ||
783 | static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s) | 789 | static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s) |
784 | { | 790 | { |
785 | u16 r; | 791 | u32 r; |
786 | 792 | ||
787 | r = STA_STATS_FIELD(BW, s->bw); | 793 | r = STA_STATS_FIELD(BW, s->bw); |
788 | 794 | ||
@@ -804,6 +810,14 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s) | |||
804 | r |= STA_STATS_FIELD(LEGACY_BAND, s->band); | 810 | r |= STA_STATS_FIELD(LEGACY_BAND, s->band); |
805 | r |= STA_STATS_FIELD(LEGACY_IDX, s->rate_idx); | 811 | r |= STA_STATS_FIELD(LEGACY_IDX, s->rate_idx); |
806 | break; | 812 | break; |
813 | case RX_ENC_HE: | ||
814 | r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_HE); | ||
815 | r |= STA_STATS_FIELD(HE_NSS, s->nss); | ||
816 | r |= STA_STATS_FIELD(HE_MCS, s->rate_idx); | ||
817 | r |= STA_STATS_FIELD(HE_GI, s->he_gi); | ||
818 | r |= STA_STATS_FIELD(HE_RU, s->he_ru); | ||
819 | r |= STA_STATS_FIELD(HE_DCM, s->he_dcm); | ||
820 | break; | ||
807 | default: | 821 | default: |
808 | WARN_ON(1); | 822 | WARN_ON(1); |
809 | return STA_STATS_RATE_INVALID; | 823 | return STA_STATS_RATE_INVALID; |
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 80a7edf8d314..0ab69a1964f8 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h | |||
@@ -92,7 +92,7 @@ | |||
92 | STA_ENTRY \ | 92 | STA_ENTRY \ |
93 | __field(u16, tid) \ | 93 | __field(u16, tid) \ |
94 | __field(u16, ssn) \ | 94 | __field(u16, ssn) \ |
95 | __field(u8, buf_size) \ | 95 | __field(u16, buf_size) \ |
96 | __field(bool, amsdu) \ | 96 | __field(bool, amsdu) \ |
97 | __field(u16, timeout) \ | 97 | __field(u16, timeout) \ |
98 | __field(u16, action) | 98 | __field(u16, action) |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index b744b10465c3..c77c84325348 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -1095,6 +1095,21 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, | |||
1095 | if (elen >= sizeof(*elems->max_idle_period_ie)) | 1095 | if (elen >= sizeof(*elems->max_idle_period_ie)) |
1096 | elems->max_idle_period_ie = (void *)pos; | 1096 | elems->max_idle_period_ie = (void *)pos; |
1097 | break; | 1097 | break; |
1098 | case WLAN_EID_EXTENSION: | ||
1099 | if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA && | ||
1100 | elen >= (sizeof(*elems->mu_edca_param_set) + 1)) { | ||
1101 | elems->mu_edca_param_set = (void *)&pos[1]; | ||
1102 | } else if (pos[0] == WLAN_EID_EXT_HE_CAPABILITY) { | ||
1103 | elems->he_cap = (void *)&pos[1]; | ||
1104 | elems->he_cap_len = elen - 1; | ||
1105 | } else if (pos[0] == WLAN_EID_EXT_HE_OPERATION && | ||
1106 | elen >= sizeof(*elems->he_operation) && | ||
1107 | elen >= ieee80211_he_oper_size(&pos[1])) { | ||
1108 | elems->he_operation = (void *)&pos[1]; | ||
1109 | } else if (pos[0] == WLAN_EID_EXT_UORA && elen >= 1) { | ||
1110 | elems->uora_element = (void *)&pos[1]; | ||
1111 | } | ||
1112 | break; | ||
1098 | default: | 1113 | default: |
1099 | break; | 1114 | break; |
1100 | } | 1115 | } |
@@ -1356,6 +1371,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local, | |||
1356 | size_t *offset, u32 flags) | 1371 | size_t *offset, u32 flags) |
1357 | { | 1372 | { |
1358 | struct ieee80211_supported_band *sband; | 1373 | struct ieee80211_supported_band *sband; |
1374 | const struct ieee80211_sta_he_cap *he_cap; | ||
1359 | u8 *pos = buffer, *end = buffer + buffer_len; | 1375 | u8 *pos = buffer, *end = buffer + buffer_len; |
1360 | size_t noffset; | 1376 | size_t noffset; |
1361 | int supp_rates_len, i; | 1377 | int supp_rates_len, i; |
@@ -1463,11 +1479,6 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local, | |||
1463 | sband->ht_cap.cap); | 1479 | sband->ht_cap.cap); |
1464 | } | 1480 | } |
1465 | 1481 | ||
1466 | /* | ||
1467 | * If adding more here, adjust code in main.c | ||
1468 | * that calculates local->scan_ies_len. | ||
1469 | */ | ||
1470 | |||
1471 | /* insert custom IEs that go before VHT */ | 1482 | /* insert custom IEs that go before VHT */ |
1472 | if (ie && ie_len) { | 1483 | if (ie && ie_len) { |
1473 | static const u8 before_vht[] = { | 1484 | static const u8 before_vht[] = { |
@@ -1510,6 +1521,39 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local, | |||
1510 | sband->vht_cap.cap); | 1521 | sband->vht_cap.cap); |
1511 | } | 1522 | } |
1512 | 1523 | ||
1524 | /* insert custom IEs that go before HE */ | ||
1525 | if (ie && ie_len) { | ||
1526 | static const u8 before_he[] = { | ||
1527 | /* | ||
1528 | * no need to list the ones split off before VHT | ||
1529 | * or generated here | ||
1530 | */ | ||
1531 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS, | ||
1532 | WLAN_EID_AP_CSN, | ||
1533 | /* TODO: add 11ah/11aj/11ak elements */ | ||
1534 | }; | ||
1535 | noffset = ieee80211_ie_split(ie, ie_len, | ||
1536 | before_he, ARRAY_SIZE(before_he), | ||
1537 | *offset); | ||
1538 | if (end - pos < noffset - *offset) | ||
1539 | goto out_err; | ||
1540 | memcpy(pos, ie + *offset, noffset - *offset); | ||
1541 | pos += noffset - *offset; | ||
1542 | *offset = noffset; | ||
1543 | } | ||
1544 | |||
1545 | he_cap = ieee80211_get_he_sta_cap(sband); | ||
1546 | if (he_cap) { | ||
1547 | pos = ieee80211_ie_build_he_cap(pos, he_cap, end); | ||
1548 | if (!pos) | ||
1549 | goto out_err; | ||
1550 | } | ||
1551 | |||
1552 | /* | ||
1553 | * If adding more here, adjust code in main.c | ||
1554 | * that calculates local->scan_ies_len. | ||
1555 | */ | ||
1556 | |||
1513 | return pos - buffer; | 1557 | return pos - buffer; |
1514 | out_err: | 1558 | out_err: |
1515 | WARN_ONCE(1, "not enough space for preq IEs\n"); | 1559 | WARN_ONCE(1, "not enough space for preq IEs\n"); |
@@ -2396,6 +2440,72 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, | |||
2396 | return pos; | 2440 | return pos; |
2397 | } | 2441 | } |
2398 | 2442 | ||
2443 | u8 *ieee80211_ie_build_he_cap(u8 *pos, | ||
2444 | const struct ieee80211_sta_he_cap *he_cap, | ||
2445 | u8 *end) | ||
2446 | { | ||
2447 | u8 n; | ||
2448 | u8 ie_len; | ||
2449 | u8 *orig_pos = pos; | ||
2450 | |||
2451 | /* Make sure we have place for the IE */ | ||
2452 | /* | ||
2453 | * TODO: the 1 added is because this temporarily is under the EXTENSION | ||
2454 | * IE. Get rid of it when it moves. | ||
2455 | */ | ||
2456 | if (!he_cap) | ||
2457 | return orig_pos; | ||
2458 | |||
2459 | n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); | ||
2460 | ie_len = 2 + 1 + | ||
2461 | sizeof(he_cap->he_cap_elem) + n + | ||
2462 | ieee80211_he_ppe_size(he_cap->ppe_thres[0], | ||
2463 | he_cap->he_cap_elem.phy_cap_info); | ||
2464 | |||
2465 | if ((end - pos) < ie_len) | ||
2466 | return orig_pos; | ||
2467 | |||
2468 | *pos++ = WLAN_EID_EXTENSION; | ||
2469 | pos++; /* We'll set the size later below */ | ||
2470 | *pos++ = WLAN_EID_EXT_HE_CAPABILITY; | ||
2471 | |||
2472 | /* Fixed data */ | ||
2473 | memcpy(pos, &he_cap->he_cap_elem, sizeof(he_cap->he_cap_elem)); | ||
2474 | pos += sizeof(he_cap->he_cap_elem); | ||
2475 | |||
2476 | memcpy(pos, &he_cap->he_mcs_nss_supp, n); | ||
2477 | pos += n; | ||
2478 | |||
2479 | /* Check if PPE Threshold should be present */ | ||
2480 | if ((he_cap->he_cap_elem.phy_cap_info[6] & | ||
2481 | IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0) | ||
2482 | goto end; | ||
2483 | |||
2484 | /* | ||
2485 | * Calculate how many PPET16/PPET8 pairs are to come. Algorithm: | ||
2486 | * (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK) | ||
2487 | */ | ||
2488 | n = hweight8(he_cap->ppe_thres[0] & | ||
2489 | IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK); | ||
2490 | n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >> | ||
2491 | IEEE80211_PPE_THRES_NSS_POS)); | ||
2492 | |||
2493 | /* | ||
2494 | * Each pair is 6 bits, and we need to add the 7 "header" bits to the | ||
2495 | * total size. | ||
2496 | */ | ||
2497 | n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7; | ||
2498 | n = DIV_ROUND_UP(n, 8); | ||
2499 | |||
2500 | /* Copy PPE Thresholds */ | ||
2501 | memcpy(pos, &he_cap->ppe_thres, n); | ||
2502 | pos += n; | ||
2503 | |||
2504 | end: | ||
2505 | orig_pos[1] = (pos - orig_pos) - 2; | ||
2506 | return pos; | ||
2507 | } | ||
2508 | |||
2399 | u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, | 2509 | u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, |
2400 | const struct cfg80211_chan_def *chandef, | 2510 | const struct cfg80211_chan_def *chandef, |
2401 | u16 prot_mode, bool rifs_mode) | 2511 | u16 prot_mode, bool rifs_mode) |