* [RFC PATCH 1/7] wifi: nl80211: remove EHT IE size validation
2026-04-10 19:03 [RFC PATCH 0/7] Do not read band-dependent reserved bits Pablo Martin-Gomez
@ 2026-04-10 19:03 ` Pablo Martin-Gomez
2026-04-10 20:25 ` Johannes Berg
2026-04-10 19:03 ` [RFC PATCH 2/7] wifi: make EHT capa size check not read reserved bits Pablo Martin-Gomez
` (6 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Pablo Martin-Gomez @ 2026-04-10 19:03 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
Remove ieee802_eht_capa_size_ok() calls from the middle of nl80211
packet processing paths. There is no reason why EHT Capabilities elem
size should be checked here while HE & UHR are not.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
net/wireless/nl80211.c | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index f334cdef8958..fb0bb4a957d9 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -8624,12 +8624,6 @@ static int nl80211_set_station_tdls(struct genl_info *info,
nla_data(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
params->link_sta_params.eht_capa_len =
nla_len(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
-
- if (!ieee80211_eht_capa_size_ok((const u8 *)params->link_sta_params.he_capa,
- (const u8 *)params->link_sta_params.eht_capa,
- params->link_sta_params.eht_capa_len,
- false))
- return -EINVAL;
}
}
@@ -8984,12 +8978,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
nla_data(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
params.link_sta_params.eht_capa_len =
nla_len(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
-
- if (!ieee80211_eht_capa_size_ok((const u8 *)params.link_sta_params.he_capa,
- (const u8 *)params.link_sta_params.eht_capa,
- params.link_sta_params.eht_capa_len,
- false))
- return -EINVAL;
}
}
@@ -18550,12 +18538,6 @@ nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info,
nla_data(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
params.eht_capa_len =
nla_len(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
-
- if (!ieee80211_eht_capa_size_ok((const u8 *)params.he_capa,
- (const u8 *)params.eht_capa,
- params.eht_capa_len,
- false))
- return -EINVAL;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [RFC PATCH 1/7] wifi: nl80211: remove EHT IE size validation
2026-04-10 19:03 ` [RFC PATCH 1/7] wifi: nl80211: remove EHT IE size validation Pablo Martin-Gomez
@ 2026-04-10 20:25 ` Johannes Berg
0 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2026-04-10 20:25 UTC (permalink / raw)
To: Pablo Martin-Gomez; +Cc: linux-wireless
On Fri, 2026-04-10 at 21:03 +0200, Pablo Martin-Gomez wrote:
> Remove ieee802_eht_capa_size_ok() calls from the middle of nl80211
> packet processing paths. There is no reason why EHT Capabilities elem
> size should be checked here while HE & UHR are not.
That's not right: NL80211_ATTR_HE_CAPABILITY and
NL80211_ATTR_UHR_CAPABILITY have policy validation functions. It's just
that NL80211_ATTR_EHT_CAPABILITY doesn't, because it cannot, because it
depends on the HE capability (validating EHT requires access to HE data,
and policy validation functions can't do that.)
johannes
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH 2/7] wifi: make EHT capa size check not read reserved bits
2026-04-10 19:03 [RFC PATCH 0/7] Do not read band-dependent reserved bits Pablo Martin-Gomez
2026-04-10 19:03 ` [RFC PATCH 1/7] wifi: nl80211: remove EHT IE size validation Pablo Martin-Gomez
@ 2026-04-10 19:03 ` Pablo Martin-Gomez
2026-04-10 20:35 ` Johannes Berg
2026-04-10 19:03 ` [RFC PATCH 3/7] wifi: make HE " Pablo Martin-Gomez
` (5 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Pablo Martin-Gomez @ 2026-04-10 19:03 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
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 <pmartin-gomez@freebox.fr>
---
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 <linux/types.h>
#include <linux/if_ether.h>
+#include <linux/nl80211.h>
/* need HE definitions for the inlines here */
#include <linux/ieee80211-he.h>
@@ -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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [RFC PATCH 2/7] wifi: make EHT capa size check not read reserved bits
2026-04-10 19:03 ` [RFC PATCH 2/7] wifi: make EHT capa size check not read reserved bits Pablo Martin-Gomez
@ 2026-04-10 20:35 ` Johannes Berg
0 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2026-04-10 20:35 UTC (permalink / raw)
To: Pablo Martin-Gomez; +Cc: linux-wireless
On Fri, 2026-04-10 at 21:03 +0200, Pablo Martin-Gomez wrote:
>
> 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 ++++--
>
> 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)
I don't see how this can build without adjusting net/mac80211/parse.c?
johannes
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH 3/7] wifi: make HE capa size check not read reserved bits
2026-04-10 19:03 [RFC PATCH 0/7] Do not read band-dependent reserved bits Pablo Martin-Gomez
2026-04-10 19:03 ` [RFC PATCH 1/7] wifi: nl80211: remove EHT IE size validation Pablo Martin-Gomez
2026-04-10 19:03 ` [RFC PATCH 2/7] wifi: make EHT capa size check not read reserved bits Pablo Martin-Gomez
@ 2026-04-10 19:03 ` Pablo Martin-Gomez
2026-04-10 19:03 ` [RFC PATCH 4/7] wifi: do not read band-dependent " Pablo Martin-Gomez
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Pablo Martin-Gomez @ 2026-04-10 19:03 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
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 <pmartin-gomez@freebox.fr>
---
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 <linux/types.h>
#include <linux/if_ether.h>
+#include <linux/nl80211.h>
#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 <linux/nl80211.h>
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
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 4/7] wifi: do not read band-dependent reserved bits
2026-04-10 19:03 [RFC PATCH 0/7] Do not read band-dependent reserved bits Pablo Martin-Gomez
` (2 preceding siblings ...)
2026-04-10 19:03 ` [RFC PATCH 3/7] wifi: make HE " Pablo Martin-Gomez
@ 2026-04-10 19:03 ` Pablo Martin-Gomez
2026-04-10 19:03 ` [RFC PATCH 5/7] wifi: ath11k: " Pablo Martin-Gomez
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Pablo Martin-Gomez @ 2026-04-10 19:03 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
Bits in Supported Channel Width Set subfield of the HE PHY
Capabilities Information field and Support For 320 MHz in 6 GHz
of the EHT PHY Capabilities Information field are reserved depending on
the current band.
Check the current band to make sure the bits that are being read are not
reserved.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
net/mac80211/debugfs_sta.c | 7 +++--
net/mac80211/he.c | 64 ++++++++++++++++++++------------------
net/mac80211/mlme.c | 5 +--
net/mac80211/vht.c | 2 +-
4 files changed, 41 insertions(+), 37 deletions(-)
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index ef75255d47d5..ea792660fe25 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -1003,13 +1003,14 @@ static ssize_t link_sta_he_capa_read(struct file *file, char __user *userbuf,
PRINT_NSS_SUPP(rx_mcs_80, "RX-MCS-80");
PRINT_NSS_SUPP(tx_mcs_80, "TX-MCS-80");
- if (cap[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) {
+ if (band != NL80211_BAND_2GHZ &&
+ cap[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) {
PRINT_NSS_SUPP(rx_mcs_160, "RX-MCS-160");
PRINT_NSS_SUPP(tx_mcs_160, "TX-MCS-160");
}
- if (cap[0] &
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) {
+ if (band != NL80211_BAND_2GHZ &&
+ cap[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) {
PRINT_NSS_SUPP(rx_mcs_80p80, "RX-MCS-80P80");
PRINT_NSS_SUPP(tx_mcs_80p80, "TX-MCS-80P80");
}
diff --git a/net/mac80211/he.c b/net/mac80211/he.c
index 4f3bafceb243..95ec76eda951 100644
--- a/net/mac80211/he.c
+++ b/net/mac80211/he.c
@@ -168,38 +168,40 @@ _ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
&own_he_cap.he_mcs_nss_supp.tx_mcs_80,
&he_cap->he_mcs_nss_supp.tx_mcs_80);
- own_160 = own_he_cap.he_cap_elem.phy_cap_info[0] &
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
- peer_160 = he_cap->he_cap_elem.phy_cap_info[0] &
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
-
- if (peer_160 && own_160) {
- ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_160,
- &he_cap->he_mcs_nss_supp.rx_mcs_160,
- &own_he_cap.he_mcs_nss_supp.tx_mcs_160,
- &he_cap->he_mcs_nss_supp.tx_mcs_160);
- } else if (peer_160 && !own_160) {
- ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.rx_mcs_160);
- ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.tx_mcs_160);
- he_cap->he_cap_elem.phy_cap_info[0] &=
- ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
- }
+ if (band != NL80211_BAND_2GHZ) {
+ own_160 = own_he_cap.he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ peer_160 = he_cap->he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+ if (peer_160 && own_160) {
+ ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_160,
+ &he_cap->he_mcs_nss_supp.rx_mcs_160,
+ &own_he_cap.he_mcs_nss_supp.tx_mcs_160,
+ &he_cap->he_mcs_nss_supp.tx_mcs_160);
+ } else if (peer_160 && !own_160) {
+ ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.rx_mcs_160);
+ ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.tx_mcs_160);
+ he_cap->he_cap_elem.phy_cap_info[0] &=
+ ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ }
- own_80p80 = own_he_cap.he_cap_elem.phy_cap_info[0] &
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
- peer_80p80 = he_cap->he_cap_elem.phy_cap_info[0] &
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
-
- if (peer_80p80 && own_80p80) {
- ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80p80,
- &he_cap->he_mcs_nss_supp.rx_mcs_80p80,
- &own_he_cap.he_mcs_nss_supp.tx_mcs_80p80,
- &he_cap->he_mcs_nss_supp.tx_mcs_80p80);
- } else if (peer_80p80 && !own_80p80) {
- ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.rx_mcs_80p80);
- ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.tx_mcs_80p80);
- he_cap->he_cap_elem.phy_cap_info[0] &=
- ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+ own_80p80 = own_he_cap.he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+ peer_80p80 = he_cap->he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+
+ if (peer_80p80 && own_80p80) {
+ ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80p80,
+ &he_cap->he_mcs_nss_supp.rx_mcs_80p80,
+ &own_he_cap.he_mcs_nss_supp.tx_mcs_80p80,
+ &he_cap->he_mcs_nss_supp.tx_mcs_80p80);
+ } else if (peer_80p80 && !own_80p80) {
+ ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.rx_mcs_80p80);
+ ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.tx_mcs_80p80);
+ he_cap->he_cap_elem.phy_cap_info[0] &=
+ ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+ }
}
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 4c1e5259837e..d840177fcd62 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -716,13 +716,14 @@ ieee80211_get_eht_cap_mcs_nss(const struct ieee80211_sta_he_cap *sta_he_cap,
case 0:
return sta_eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_max_nss[idx];
case 1:
- if (!(he_phy_cap0 &
+ if (band == NL80211_BAND_2GHZ || !(he_phy_cap0 &
(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)))
return 0xff; /* pass check */
return sta_eht_cap->eht_mcs_nss_supp.bw._160.rx_tx_max_nss[idx];
case 2:
- if (!(eht_phy_cap0 & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ))
+ if (band != NL80211_BAND_6GHZ ||
+ !(eht_phy_cap0 & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ))
return 0xff; /* pass check */
return sta_eht_cap->eht_mcs_nss_supp.bw._320.rx_tx_max_nss[idx];
}
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index a6570781740a..8b1e4bf43475 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -595,7 +595,7 @@ void ieee80211_sta_init_nss(struct link_sta_info *link_sta)
}
}
- support_160 = he_cap->he_cap_elem.phy_cap_info[0] &
+ support_160 = band != NL80211_BAND_2GHZ && he_cap->he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
if (support_160)
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 5/7] wifi: ath11k: do not read band-dependent reserved bits
2026-04-10 19:03 [RFC PATCH 0/7] Do not read band-dependent reserved bits Pablo Martin-Gomez
` (3 preceding siblings ...)
2026-04-10 19:03 ` [RFC PATCH 4/7] wifi: do not read band-dependent " Pablo Martin-Gomez
@ 2026-04-10 19:03 ` Pablo Martin-Gomez
2026-04-10 19:03 ` [RFC PATCH 6/7] wifi: ath12k: " Pablo Martin-Gomez
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Pablo Martin-Gomez @ 2026-04-10 19:03 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
Bits in Supported Channel Width Set subfield of the HE PHY
Capabilities Information field and Support For 320 MHz in 6 GHz
of the EHT PHY Capabilities Information field are reserved depending on
the current band.
Check the current band to make sure the bits that are being read are not
reserved.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
drivers/net/wireless/ath/ath11k/mac.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index a48b6bf1f29a..eb3dc5770077 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -2383,7 +2383,7 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar,
return;
arg->he_flag = true;
- support_160 = !!(he_cap->he_cap_elem.phy_cap_info[0] &
+ support_160 = band != NL80211_BAND_2GHZ && !!(he_cap->he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G);
/* Supported HE-MCS and NSS Set of peer he_cap is intersection with self he_cp */
@@ -8683,13 +8683,14 @@ ath11k_mac_has_single_legacy_rate(struct ath11k *ar,
}
static __le16
-ath11k_mac_get_tx_mcs_map(const struct ieee80211_sta_he_cap *he_cap)
+ath11k_mac_get_tx_mcs_map(const struct ieee80211_sta_he_cap *he_cap,
+ enum nl80211_band band)
{
- if (he_cap->he_cap_elem.phy_cap_info[0] &
+ if (band != NL80211_BAND_2GHZ && he_cap->he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
return he_cap->he_mcs_nss_supp.tx_mcs_80p80;
- if (he_cap->he_cap_elem.phy_cap_info[0] &
+ if (band != NL80211_BAND_2GHZ && he_cap->he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
return he_cap->he_mcs_nss_supp.tx_mcs_160;
@@ -8740,7 +8741,7 @@ ath11k_mac_bitrate_mask_get_single_nss(struct ath11k *ar,
if (!he_cap)
return false;
- he_mcs_map = le16_to_cpu(ath11k_mac_get_tx_mcs_map(he_cap));
+ he_mcs_map = le16_to_cpu(ath11k_mac_get_tx_mcs_map(he_cap, sband->band));
for (i = 0; i < ARRAY_SIZE(mask->control[band].he_mcs); i++) {
if (mask->control[band].he_mcs[i] == 0)
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 6/7] wifi: ath12k: do not read band-dependent reserved bits
2026-04-10 19:03 [RFC PATCH 0/7] Do not read band-dependent reserved bits Pablo Martin-Gomez
` (4 preceding siblings ...)
2026-04-10 19:03 ` [RFC PATCH 5/7] wifi: ath11k: " Pablo Martin-Gomez
@ 2026-04-10 19:03 ` Pablo Martin-Gomez
2026-04-10 19:03 ` [RFC PATCH 7/7] wifi: mt76: " Pablo Martin-Gomez
2026-04-15 22:38 ` [RFC PATCH 0/7] Do " Jeff Johnson
7 siblings, 0 replies; 11+ messages in thread
From: Pablo Martin-Gomez @ 2026-04-10 19:03 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
Bits in Supported Channel Width Set subfield of the HE PHY
Capabilities Information field and Support For 320 MHz in 6 GHz
of the EHT PHY Capabilities Information field are reserved depending on
the current band.
Check the current band to make sure the bits that are being read are not
reserved.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
drivers/net/wireless/ath/ath12k/mac.c | 32 ++++++++++++++++-----------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 6e4cfbb0e5bd..51b5a9ceff75 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -4,6 +4,7 @@
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
+#include <linux/nl80211.h>
#include <net/mac80211.h>
#include <net/cfg80211.h>
#include <linux/etherdevice.h>
@@ -8703,28 +8704,32 @@ static void
ath12k_mac_copy_eht_mcs_nss(struct ath12k_band_cap *band_cap,
struct ieee80211_eht_mcs_nss_supp *mcs_nss,
const struct ieee80211_he_cap_elem *he_cap,
- const struct ieee80211_eht_cap_elem_fixed *eht_cap)
+ const struct ieee80211_eht_cap_elem_fixed *eht_cap,
+ enum nl80211_band band)
{
- if ((he_cap->phy_cap_info[0] &
- (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+ if ((band == NL80211_BAND_2GHZ && (he_cap->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0) ||
+ (band != NL80211_BAND_2GHZ && (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)
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)) == 0))
memcpy(&mcs_nss->only_20mhz, &band_cap->eht_mcs_20_only,
sizeof(struct ieee80211_eht_mcs_nss_supp_20mhz_only));
- if (he_cap->phy_cap_info[0] &
- (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G))
+ if ((band == NL80211_BAND_2GHZ && (he_cap->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) ||
+ (band != NL80211_BAND_2GHZ && (he_cap->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)))
memcpy(&mcs_nss->bw._80, &band_cap->eht_mcs_80,
sizeof(struct ieee80211_eht_mcs_nss_supp_bw));
- if (he_cap->phy_cap_info[0] &
+ if (band != NL80211_BAND_2GHZ && he_cap->phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
memcpy(&mcs_nss->bw._160, &band_cap->eht_mcs_160,
sizeof(struct ieee80211_eht_mcs_nss_supp_bw));
- if (eht_cap->phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)
+ if (band == NL80211_BAND_6GHZ &&
+ eht_cap->phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)
memcpy(&mcs_nss->bw._320, &band_cap->eht_mcs_320,
sizeof(struct ieee80211_eht_mcs_nss_supp_bw));
}
@@ -8809,7 +8814,8 @@ static void ath12k_mac_copy_eht_cap(struct ath12k *ar,
struct ath12k_band_cap *band_cap,
struct ieee80211_he_cap_elem *he_cap_elem,
int iftype,
- struct ieee80211_sta_eht_cap *eht_cap)
+ struct ieee80211_sta_eht_cap *eht_cap,
+ enum nl80211_band band)
{
struct ieee80211_eht_cap_elem_fixed *eht_cap_elem = &eht_cap->eht_cap_elem;
@@ -8852,7 +8858,7 @@ static void ath12k_mac_copy_eht_cap(struct ath12k *ar,
}
ath12k_mac_copy_eht_mcs_nss(band_cap, &eht_cap->eht_mcs_nss_supp,
- he_cap_elem, eht_cap_elem);
+ he_cap_elem, eht_cap_elem, band);
if (eht_cap_elem->phy_cap_info[5] &
IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT)
@@ -8888,7 +8894,7 @@ static int ath12k_mac_copy_sband_iftype_data(struct ath12k *ar,
ath12k_mac_setup_he_6ghz_cap(cap, band_cap);
}
ath12k_mac_copy_eht_cap(ar, band_cap, &he_cap->he_cap_elem, i,
- &data[idx].eht_cap);
+ &data[idx].eht_cap, band);
idx++;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 7/7] wifi: mt76: do not read band-dependent reserved bits
2026-04-10 19:03 [RFC PATCH 0/7] Do not read band-dependent reserved bits Pablo Martin-Gomez
` (5 preceding siblings ...)
2026-04-10 19:03 ` [RFC PATCH 6/7] wifi: ath12k: " Pablo Martin-Gomez
@ 2026-04-10 19:03 ` Pablo Martin-Gomez
2026-04-15 22:38 ` [RFC PATCH 0/7] Do " Jeff Johnson
7 siblings, 0 replies; 11+ messages in thread
From: Pablo Martin-Gomez @ 2026-04-10 19:03 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Pablo Martin-Gomez
Bits in Supported Channel Width Set subfield of the HE PHY
Capabilities Information field and Support For 320 MHz in 6 GHz
of the EHT PHY Capabilities Information field are reserved depending on
the current band.
Check the current band to make sure the bits that are being read are not
reserved.
Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
---
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 16420375112d..3b58fe86e1fc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -1544,7 +1544,8 @@ mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb,
static void
mt7996_mcu_sta_eht_tlv(struct sk_buff *skb,
- struct ieee80211_link_sta *link_sta)
+ struct ieee80211_link_sta *link_sta,
+ enum nl80211_band band)
{
struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
struct ieee80211_vif *vif = container_of((void *)msta->vif,
@@ -1569,11 +1570,12 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb,
eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]);
if (vif->type != NL80211_IFTYPE_STATION &&
- (link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
- (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
- 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) {
+ ((band == NL80211_BAND_2GHZ && (link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0) ||
+ (band != NL80211_BAND_2GHZ && (link_sta->he_cap.he_cap_elem.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))) {
memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz,
sizeof(eht->mcs_map_bw20));
return;
@@ -2773,7 +2775,7 @@ int mt7996_mcu_add_sta(struct mt7996_dev *dev,
/* starec he 6g*/
mt7996_mcu_sta_he_6g_tlv(skb, link_sta);
/* starec eht */
- mt7996_mcu_sta_eht_tlv(skb, link_sta);
+ mt7996_mcu_sta_eht_tlv(skb, link_sta, link_conf->chanreq.oper.chan->band);
/* starec muru */
mt7996_mcu_sta_muru_tlv(dev, skb, link_conf, link_sta);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [RFC PATCH 0/7] Do not read band-dependent reserved bits
2026-04-10 19:03 [RFC PATCH 0/7] Do not read band-dependent reserved bits Pablo Martin-Gomez
` (6 preceding siblings ...)
2026-04-10 19:03 ` [RFC PATCH 7/7] wifi: mt76: " Pablo Martin-Gomez
@ 2026-04-15 22:38 ` Jeff Johnson
7 siblings, 0 replies; 11+ messages in thread
From: Jeff Johnson @ 2026-04-15 22:38 UTC (permalink / raw)
To: Pablo Martin-Gomez, Johannes Berg; +Cc: linux-wireless
On 4/10/2026 12:03 PM, Pablo Martin-Gomez wrote:
> This series is a recyclying and extension of a previous attempt [1] to
> not read reserved bits depending on the current band for HE/EHT STAs.
>
> Convention (section 9.2.2 of 802.11-2024) states that reserved bits
> should be set to 0 (unless stated otherwise) and not to be read. Some
> STAs do not respect the convention in transmission and the kernel do not
> respect the convention in reception. The practical effect can be a
> really low throughtput between a MT7927 Windows STA and a ath12k Linux AP.
>
> I've been quite drastic and added a check of the current band before
> read a bit that might be reserved whatever it's from an IE that was
> received or an IE will be sent. I also removed checks where they didn't
> make much sense and the current band was complicated/impossible to
> retrieve. That is why I'm positing this as a RFC.
>
> [1]: https://lore.kernel.org/linux-wireless/20250120114551.1542812-1-pmartin-gomez@freebox.fr/
>
>
> Pablo Martin-Gomez (7):
> wifi: nl80211: remove EHT IE size validation
> wifi: make EHT capa size check not read reserved bits
> wifi: make HE capa size check not read reserved bits
> wifi: do not read band-dependent reserved bits
> wifi: ath11k: do not read band-dependent reserved bits
> wifi: ath12k: do not read band-dependent reserved bits
> wifi: mt76: do not read band-dependent reserved bits
>
> drivers/net/wireless/ath/ath11k/mac.c | 11 +--
> drivers/net/wireless/ath/ath12k/mac.c | 35 +++++----
> .../net/wireless/mediatek/mt76/mt7996/mcu.c | 16 ++--
> include/linux/ieee80211-eht.h | 58 +++++++++------
> include/linux/ieee80211-he.h | 21 +++---
> net/mac80211/debugfs_sta.c | 7 +-
> net/mac80211/eht.c | 3 +-
> net/mac80211/he.c | 73 ++++++++++---------
> net/mac80211/mlme.c | 7 +-
> net/mac80211/util.c | 12 +--
> net/mac80211/vht.c | 2 +-
> net/wireless/nl80211.c | 31 +++-----
> 12 files changed, 149 insertions(+), 127 deletions(-)
>
Please use scripts/get_maintainer.pl to identify all necessary recipients for
your patches. Or start using 'b4' which does all the work for you. You are
modifying drivers without including the per-driver mailing lists and maintainers.
/jeff
^ permalink raw reply [flat|nested] 11+ messages in thread