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/mlme.c | |
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/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 288 |
1 files changed, 271 insertions, 17 deletions
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 | ||