ieee80211_eht_mcs_nss_size() reads the value of reserved bits to deduce the current band used assuming the reserved bits are set to 0. However, Mediatek's chipsets MT7927 and MT7925 with Windows driver 5.4.0.3044 (and earlier versions) set the bits in Supported Channel Width Set subfield of the HE PHY Capabilities Information field, regardless of the current band. This causes the kernel to miscalculate mcs_nss_size to 3 bytes, resulting in a incorrect rx/tx nss map, so the sta is believed to have 0 NSS for 160/320. Pass the band to ieee80211_eht_mcs_nss_size() to only read non reserved bits in the Supported Channel Width Set subfield. Signed-off-by: Pablo Martin-Gomez --- drivers/net/wireless/ath/ath12k/mac.c | 3 +- include/linux/ieee80211-eht.h | 58 ++++++++++++++++----------- net/mac80211/eht.c | 3 +- net/mac80211/util.c | 8 ++-- net/wireless/nl80211.c | 13 ++++-- 5 files changed, 53 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 553ec28b6aaa..6e4cfbb0e5bd 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -12537,7 +12537,8 @@ ath12k_mac_bitrate_mask_get_single_nss(struct ath12k *ar, mcs_nss_len = ieee80211_eht_mcs_nss_size(&data->he_cap.he_cap_elem, &data->eht_cap.eht_cap_elem, - false); + false, + sband->band); if (mcs_nss_len == 4) { /* 20 MHz only STA case */ const struct ieee80211_eht_mcs_nss_supp_20mhz_only *eht_mcs_nss = diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index 76e1cb80dcc7..d6f7072df5d6 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -17,6 +17,7 @@ #include #include +#include /* need HE definitions for the inlines here */ #include @@ -283,31 +284,41 @@ struct ieee80211_eht_operation_info { static inline u8 ieee80211_eht_mcs_nss_size(const struct ieee80211_he_cap_elem *he_cap, const struct ieee80211_eht_cap_elem_fixed *eht_cap, - bool from_ap) + bool from_ap, + enum nl80211_band band) { u8 count = 0; - /* on 2.4 GHz, if it supports 40 MHz, the result is 3 */ - if (he_cap->phy_cap_info[0] & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) - return 3; - - /* on 2.4 GHz, these three bits are reserved, so should be 0 */ - if (he_cap->phy_cap_info[0] & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) - count += 3; - - if (he_cap->phy_cap_info[0] & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) - count += 3; - - if (eht_cap->phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) - count += 3; - - if (count) - return count; + switch (band) { + case NL80211_BAND_2GHZ: + if (from_ap || he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) + count += 3; + else + count = 4; + break; + case NL80211_BAND_6GHZ: + if (eht_cap->phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) + count += 3; + fallthrough; + case NL80211_BAND_5GHZ: + if (he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) + count += 3; + if (he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) + count += 3; + if (!from_ap && (he_cap->phy_cap_info[0] & + (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)) == 0) + count = 4; + break; + default: + break; + } - return from_ap ? 3 : 4; + return count; } /* 802.11be EHT PPE Thresholds */ @@ -344,7 +355,7 @@ ieee80211_eht_ppe_size(u16 ppe_thres_hdr, const u8 *phy_cap_info) static inline bool ieee80211_eht_capa_size_ok(const u8 *he_capa, const u8 *data, u8 len, - bool from_ap) + bool from_ap, enum nl80211_band band) { const struct ieee80211_eht_cap_elem_fixed *elem = (const void *)data; u8 needed = sizeof(struct ieee80211_eht_cap_elem_fixed); @@ -354,7 +365,8 @@ ieee80211_eht_capa_size_ok(const u8 *he_capa, const u8 *data, u8 len, needed += ieee80211_eht_mcs_nss_size((const void *)he_capa, (const void *)data, - from_ap); + from_ap, + band); if (len < needed) return false; diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c index e88f28edfd57..37b8387bd728 100644 --- a/net/mac80211/eht.c +++ b/net/mac80211/eht.c @@ -32,7 +32,8 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata, mcs_nss_size = ieee80211_eht_mcs_nss_size(he_cap_ie_elem, &eht_cap_ie_elem->fixed, sdata->vif.type == - NL80211_IFTYPE_STATION); + NL80211_IFTYPE_STATION, + sband->band); eht_total_size += mcs_nss_size; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8987a4504520..8e0c7ec95827 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -4431,7 +4431,8 @@ u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata) n = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, &eht_cap->eht_cap_elem, - is_ap); + is_ap, + sband->band); return 2 + 1 + sizeof(eht_cap->eht_cap_elem) + n + ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], @@ -4464,7 +4465,8 @@ int ieee80211_put_eht_cap(struct sk_buff *skb, orig_mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, &eht_cap->eht_cap_elem, - for_ap); + for_ap, + sband->band); ieee80211_get_adjusted_he_cap(conn, he_cap, &he); @@ -4498,7 +4500,7 @@ int ieee80211_put_eht_cap(struct sk_buff *skb, fixed.phy_cap_info[0] &= ~IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ; - mcs_nss_len = ieee80211_eht_mcs_nss_size(&he, &fixed, for_ap); + mcs_nss_len = ieee80211_eht_mcs_nss_size(&he, &fixed, for_ap, sband->band); ppet_len = ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], fixed.phy_cap_info); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fb0bb4a957d9..3c29872073f8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2137,7 +2137,8 @@ nl80211_send_iftype_data(struct sk_buff *msg, mcs_nss_size = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, &eht_cap->eht_cap_elem, - is_ap); + is_ap, + sband->band); ppe_thres_hdr = get_unaligned_le16(&eht_cap->eht_ppe_thres[0]); ppe_thresh_size = @@ -5919,7 +5920,8 @@ static bool eht_set_mcs_mask(struct genl_info *info, struct wireless_dev *wdev, mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, &eht_cap->eht_cap_elem, wdev->iftype == - NL80211_IFTYPE_STATION); + NL80211_IFTYPE_STATION, + sband->band); if (mcs_nss_len == 3) { /* Supported iftypes for setting non-20 MHZ only EHT MCS */ @@ -6007,7 +6009,8 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, &eht_cap->eht_cap_elem, wdev->iftype == - NL80211_IFTYPE_STATION); + NL80211_IFTYPE_STATION, + sband->band); eht_build_mcs_mask(info, eht_cap, mcs_nss_len, mask->control[i].eht_mcs); @@ -6686,7 +6689,9 @@ static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params) params->eht_cap = (void *)(cap->data + 1); if (!ieee80211_eht_capa_size_ok((const u8 *)params->he_cap, (const u8 *)params->eht_cap, - cap->datalen - 1, true)) + cap->datalen - 1, + true, + params->chandef.chan->band)) return -EINVAL; } cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len); -- 2.43.0