From: JB Tsai <jb.tsai@mediatek.com>
To: <nbd@nbd.name>, <lorenzo@kernel.org>
Cc: <linux-wireless@vger.kernel.org>,
<linux-mediatek@lists.infradead.org>, <Sean.Wang@mediatek.com>,
<Quan.Zhou@mediatek.com>, <Ryder.Lee@mediatek.com>,
<Shengwei.Lu@mediatek.com>, <litien.chang@mediatek.com>,
<jb.tsai@mediatek.com>, Shengwei Lu <shengwei.lu@mediatek.com>,
Sean Wang <sean.wang@mediatek.com>
Subject: [PATCH 1/2] [PATCH 1/2] wifi: mt76: mt7925: add IE-parsing helpers for AP NSTS extraction for MT7928
Date: Tue, 4 Aug 2026 14:24:02 +0800 [thread overview]
Message-ID: <20260804062403.642593-1-jb.tsai@mediatek.com> (raw)
From: Shengwei Lu <shengwei.lu@mediatek.com>
Add a dedicated module (ies.c / ies.h) that provides four helpers to
parse BSS IEs and extract the AP's maximum TX spatial-stream count
(NSTS):
- mt7928_ht_nsts_from_ies() - reads HT Capability TX MCS streams
- mt7928_vht_nsts_from_ies() - reads VHT Capability TX MCS map
- mt7928_he_nsts_from_ies() - reads HE Capability TX MCS/NSS for 80 MHz
- mt7928_eht_nsts_from_ies() - reads EHT Capability TX MCS/NSS
Also add mt7928_get_bss_suswm_cap() which walks the HT/VHT/HE/EHT caps
of both the AP (peer link_sta) and the local device (own sband) and
fills a struct mt7928_suswm_cap {bfer_en, bfee_en, nsts} summarising
the SU beamforming capabilities and the AP's NSTS count. The
MT_SU_SWM_{BFER,BFEE,NSTS_MEET} bitmap defines used by the STA_REC_SUSWM
TLV are also introduced here.
These helpers will be consumed by the STA_REC_SUSWM TLV builder to
determine whether the AP's NSTS count falls within the device's
supported spatial-stream limit.
Signed-off-by: Shengwei Lu <shengwei.lu@mediatek.com>
Co-developed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
.../wireless/mediatek/mt76/mt7925/Makefile | 2 +-
.../net/wireless/mediatek/mt76/mt7925/ies.c | 183 ++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/ies.h | 31 +++
3 files changed, 215 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/ies.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/ies.h
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index f9dcc0bba393..af62c775c3d6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
obj-$(CONFIG_MT7925E) += mt7925e.o
obj-$(CONFIG_MT7925U) += mt7925u.o
-mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o
+mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o ies.o
mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7925e-y := pci.o pci_mac.o pci_mcu.o
mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/ies.c b/drivers/net/wireless/mediatek/mt76/mt7925/ies.c
new file mode 100644
index 000000000000..0eb7dc7665b3
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/ies.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include "ies.h"
+
+u8 mt7928_ht_nsts_from_ies(const struct cfg80211_bss_ies *ies)
+{
+ const struct ieee80211_ht_cap *ht_cap;
+ const struct element *elem;
+ u8 nsts = 0, i, max_tx_streams;
+
+ elem = cfg80211_find_elem(WLAN_EID_HT_CAPABILITY,
+ ies->data, ies->len);
+ if (!elem || elem->datalen < sizeof(*ht_cap))
+ return 0;
+
+ ht_cap = (const void *)elem->data;
+
+ if ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) &&
+ (ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_DEFINED))
+ max_tx_streams =
+ ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+ >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
+ else
+ max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;
+
+ for (i = 0; i < max_tx_streams; i++) {
+ if (ht_cap->mcs.rx_mask[i])
+ nsts = i + 1;
+ }
+
+ return nsts;
+}
+
+u8 mt7928_vht_nsts_from_ies(const struct cfg80211_bss_ies *ies)
+{
+ const struct ieee80211_vht_cap *vht_cap;
+ const struct element *elem;
+ u16 tx_mcs_map;
+ u8 nsts = 0, i;
+
+ elem = cfg80211_find_elem(WLAN_EID_VHT_CAPABILITY,
+ ies->data, ies->len);
+ if (!elem || elem->datalen < sizeof(*vht_cap))
+ return 0;
+
+ vht_cap = (const void *)elem->data;
+ tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
+
+ for (i = 0; i < 8; i++) {
+ if (((tx_mcs_map >> (2 * i)) & 0x3) != IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ nsts = i + 1;
+ }
+
+ return nsts;
+}
+
+u8 mt7928_he_nsts_from_ies(const struct cfg80211_bss_ies *ies)
+{
+ const struct ieee80211_he_cap_elem *he_cap;
+ const struct ieee80211_he_mcs_nss_supp *mcs_nss;
+ const struct element *elem;
+ u8 mcs_nss_size;
+ u16 tx_mcs_80;
+ u8 nsts = 0, i;
+
+ elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
+ ies->data, ies->len);
+
+ if (!elem || elem->datalen < 1 + sizeof(*he_cap))
+ return 0;
+
+ he_cap = (const void *)(elem->data + 1);
+ mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
+
+ if (elem->datalen < 1 + sizeof(*he_cap) + mcs_nss_size)
+ return 0;
+
+ mcs_nss = (const void *)(he_cap + 1);
+ tx_mcs_80 = le16_to_cpu(mcs_nss->tx_mcs_80);
+
+ for (i = 0; i < 8; i++) {
+ if (((tx_mcs_80 >> (2 * i)) & 0x3) != IEEE80211_HE_MCS_NOT_SUPPORTED)
+ nsts = i + 1;
+ }
+
+ return nsts;
+}
+
+u8 mt7928_eht_nsts_from_ies(const struct cfg80211_bss_ies *ies)
+{
+ const struct ieee80211_eht_cap_elem_fixed *eht_cap;
+ const struct ieee80211_he_cap_elem *he_cap;
+ const struct element *eht_elem, *he_elem;
+ const u8 *mcs_nss_data;
+ u8 mcs_nss_size;
+ u8 nsts = 0, i;
+
+ eht_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_CAPABILITY,
+ ies->data, ies->len);
+ if (!eht_elem || eht_elem->datalen < 1 + sizeof(*eht_cap))
+ return 0;
+
+ he_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
+ ies->data, ies->len);
+ if (!he_elem || he_elem->datalen < 1 + sizeof(*he_cap))
+ return 0;
+
+ eht_cap = (const void *)(eht_elem->data + 1);
+ he_cap = (const void *)(he_elem->data + 1);
+
+ mcs_nss_size = ieee80211_eht_mcs_nss_size(he_cap, eht_cap, true);
+ if (eht_elem->datalen < 1 + sizeof(*eht_cap) + mcs_nss_size)
+ return 0;
+
+ mcs_nss_data = (const u8 *)(eht_cap + 1);
+
+ for (i = 0; i < mcs_nss_size; i++)
+ nsts = (mcs_nss_data[i] >> 4) & 0xf;
+
+ return nsts;
+}
+
+void mt7928_get_bss_suswm_cap(const struct cfg80211_bss_ies *ies,
+ const struct ieee80211_link_sta *link_sta,
+ const struct ieee80211_supported_band *own_sband,
+ enum nl80211_iftype iftype,
+ struct mt7928_suswm_cap *cap)
+{
+ const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap;
+ const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap;
+ const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap;
+ const struct ieee80211_sta_eht_cap *eht_cap = &link_sta->eht_cap;
+ const struct ieee80211_sta_vht_cap *own_vht_cap = &own_sband->vht_cap;
+ const struct ieee80211_sta_he_cap *own_he_cap =
+ ieee80211_get_he_iftype_cap(own_sband, iftype);
+ const struct ieee80211_sta_eht_cap *own_eht_cap =
+ ieee80211_get_eht_iftype_cap(own_sband, iftype);
+
+ cap->bfer_en = false;
+ cap->bfee_en = false;
+ cap->nsts = 0;
+
+ if (ht_cap->ht_supported)
+ cap->nsts = mt7928_ht_nsts_from_ies(ies);
+
+ if (vht_cap->vht_supported) {
+ if (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
+ cap->bfer_en = true;
+
+ if (own_vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
+ cap->bfee_en = true;
+
+ cap->nsts = mt7928_vht_nsts_from_ies(ies);
+ }
+
+ if (he_cap->has_he && own_he_cap) {
+ if (he_cap->he_cap_elem.phy_cap_info[3] &
+ IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER)
+ cap->bfer_en = true;
+
+ if (own_he_cap->he_cap_elem.phy_cap_info[4] &
+ IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE)
+ cap->bfee_en = true;
+
+ cap->nsts = mt7928_he_nsts_from_ies(ies);
+ }
+
+ if (eht_cap->has_eht && own_eht_cap) {
+ if (eht_cap->eht_cap_elem.phy_cap_info[0] &
+ IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER)
+ cap->bfer_en = true;
+
+ if (own_eht_cap->eht_cap_elem.phy_cap_info[0] &
+ IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE)
+ cap->bfee_en = true;
+
+ cap->nsts = mt7928_eht_nsts_from_ies(ies);
+ }
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/ies.h b/drivers/net/wireless/mediatek/mt76/mt7925/ies.h
new file mode 100644
index 000000000000..a1239f22ef54
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/ies.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2026 MediaTek Inc. */
+
+#ifndef __MT7925_IES_H
+#define __MT7925_IES_H
+
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+
+#define MT_SU_SWM_BFER BIT(0)
+#define MT_SU_SWM_BFEE BIT(1)
+#define MT_SU_SWM_NSTS_MEET BIT(2)
+
+struct mt7928_suswm_cap {
+ bool bfee_en;
+ bool bfer_en;
+ u8 nsts;
+};
+
+u8 mt7928_ht_nsts_from_ies(const struct cfg80211_bss_ies *ies);
+u8 mt7928_vht_nsts_from_ies(const struct cfg80211_bss_ies *ies);
+u8 mt7928_he_nsts_from_ies(const struct cfg80211_bss_ies *ies);
+u8 mt7928_eht_nsts_from_ies(const struct cfg80211_bss_ies *ies);
+
+void mt7928_get_bss_suswm_cap(const struct cfg80211_bss_ies *ies,
+ const struct ieee80211_link_sta *link_sta,
+ const struct ieee80211_supported_band *own_sband,
+ enum nl80211_iftype iftype,
+ struct mt7928_suswm_cap *cap);
+
+#endif /* __MT7925_IES_H */
--
2.45.2
next reply other threads:[~2026-08-04 6:24 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-04 6:24 JB Tsai [this message]
2026-08-04 6:24 ` [PATCH 2/2] [PATCH 2/2] wifi: mt76: mt7925: add STA_REC_SUSWM TLV for SU beamforming capability for MT7928 JB Tsai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260804062403.642593-1-jb.tsai@mediatek.com \
--to=jb.tsai@mediatek.com \
--cc=Quan.Zhou@mediatek.com \
--cc=Ryder.Lee@mediatek.com \
--cc=Sean.Wang@mediatek.com \
--cc=Shengwei.Lu@mediatek.com \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-wireless@vger.kernel.org \
--cc=litien.chang@mediatek.com \
--cc=lorenzo@kernel.org \
--cc=nbd@nbd.name \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox