ieee80211_he_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_he_mcs_nss_size() to only read non reserved bits in the Supported Channel Width Set subfield. Signed-off-by: Pablo Martin-Gomez --- include/linux/ieee80211-he.h | 21 ++++++++++++--------- net/mac80211/he.c | 9 ++++++--- net/mac80211/mlme.c | 2 +- net/mac80211/util.c | 4 ++-- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/linux/ieee80211-he.h b/include/linux/ieee80211-he.h index a08c446fbb04..7b2f160a15b5 100644 --- a/include/linux/ieee80211-he.h +++ b/include/linux/ieee80211-he.h @@ -17,6 +17,7 @@ #include #include +#include #define IEEE80211_TWT_CONTROL_NDP BIT(0) #define IEEE80211_TWT_CONTROL_RESP_MODE BIT(1) @@ -452,17 +453,19 @@ enum ieee80211_he_highest_mcs_supported_subfield_enc { /* Calculate 802.11ax HE capabilities IE Tx/Rx HE MCS NSS Support Field size */ static inline u8 -ieee80211_he_mcs_nss_size(const struct ieee80211_he_cap_elem *he_cap) +ieee80211_he_mcs_nss_size(const struct ieee80211_he_cap_elem *he_cap, enum nl80211_band band) { u8 count = 4; - if (he_cap->phy_cap_info[0] & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) - count += 4; + if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) { + if (he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) + count += 4; - if (he_cap->phy_cap_info[0] & - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) - count += 4; + if (he_cap->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + count += 4; + } return count; } @@ -506,7 +509,7 @@ ieee80211_he_ppe_size(u8 ppe_thres_hdr, const u8 *phy_cap_info) return n; } -static inline bool ieee80211_he_capa_size_ok(const u8 *data, u8 len) +static inline bool ieee80211_he_capa_size_ok(const u8 *data, u8 len, enum nl80211_band band) { const struct ieee80211_he_cap_elem *he_cap_ie_elem = (const void *)data; u8 needed = sizeof(*he_cap_ie_elem); @@ -514,7 +517,7 @@ static inline bool ieee80211_he_capa_size_ok(const u8 *data, u8 len) if (len < needed) return false; - needed += ieee80211_he_mcs_nss_size(he_cap_ie_elem); + needed += ieee80211_he_mcs_nss_size(he_cap_ie_elem, band); if (len < needed) return false; diff --git a/net/mac80211/he.c b/net/mac80211/he.c index 93e0342cff4f..4f3bafceb243 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -8,6 +8,7 @@ #include "ieee80211_i.h" #include "rate.h" +#include static void ieee80211_update_from_he_6ghz_capa(const struct ieee80211_he_6ghz_capa *he_6ghz_capa, @@ -112,7 +113,8 @@ _ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, const struct ieee80211_sta_he_cap *own_he_cap_ptr, const u8 *he_cap_ie, u8 he_cap_len, const struct ieee80211_he_6ghz_capa *he_6ghz_capa, - struct link_sta_info *link_sta) + struct link_sta_info *link_sta, + enum nl80211_band band) { struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; struct ieee80211_sta_he_cap own_he_cap; @@ -130,7 +132,7 @@ _ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, own_he_cap = *own_he_cap_ptr; /* Make sure size is OK */ - mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap_ie_elem); + mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap_ie_elem, band); he_ppe_size = ieee80211_he_ppe_size(he_cap_ie[sizeof(he_cap->he_cap_elem) + mcs_nss_size], @@ -215,7 +217,8 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, he_cap_len, (sband->band == NL80211_BAND_6GHZ) ? he_6ghz_capa : NULL, - link_sta); + link_sta, + sband->band); } void diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7fc5616cb244..4c1e5259837e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5933,7 +5933,7 @@ static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, /* skip one byte ext_tag_id */ he_cap = (void *)(he_cap_elem->data + 1); - mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap); + mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap, cbss->channel->band); /* invalid HE IE */ if (he_cap_elem->datalen < 1 + mcs_nss_size + sizeof(*he_cap)) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8e0c7ec95827..e2e44f3f4670 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2491,7 +2491,7 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata) if (!he_cap) return 0; - n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); + n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem, sband->band); return 2 + 1 + sizeof(he_cap->he_cap_elem) + n + ieee80211_he_ppe_size(he_cap->ppe_thres[0], @@ -2568,7 +2568,7 @@ int ieee80211_put_he_cap(struct sk_buff *skb, /* modify on stack first to calculate 'n' and 'ie_len' correctly */ ieee80211_get_adjusted_he_cap(conn, he_cap, &elem); - n = ieee80211_he_mcs_nss_size(&elem); + n = ieee80211_he_mcs_nss_size(&elem, sband->band); ie_len = 2 + 1 + sizeof(he_cap->he_cap_elem) + n + ieee80211_he_ppe_size(he_cap->ppe_thres[0], -- 2.43.0