Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B
@ 2026-05-06 13:09 Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 01/14] wifi: rtw89: add AMPDU to radiotap Ping-Ke Shih
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

The rtw89 has supported monitor mode already, but only basic channel,
bandwidth and data rate. This patchset introduces SIG-A/SIG-B from
PHY status IE-09/IE-10 to fill VHT and HE-SU/HE-TB/HE-MU/HE-EXT_SU
radiotap.

Hardware captures HE-MU packets by target BSS color and AID, so add a
debugfs to fill the target values.

Ping-Ke Shih (14):
  wifi: rtw89: add AMPDU to radiotap
  wifi: rtw89: add VHT beamformed to radiotap
  wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR
  wifi: rtw89: phy: define PHY status IE length for generations
  wifi: rtw89: phy: enable IE-09/IE-10 PHY status report for monitor
    mode
  wifi: rtw89: move HE radiotap to an individual function
  wifi: rtw89: fill VHT radiotap
  wifi: rtw89: fill HE-SU/HE-TB/HE-MU/HE-EXT_SU radiotap
  wifi: rtw89: debug: make implementation of beacon_info entry in order
  wifi: rtw89: add debugfs entry of monitor mode options to capture
    HE-MU packets
  wifi: rtw89: phy: check length before parsing PHY status IE
  wifi: rtw89: phy: skip trailing 8-byte zeros of PHY status IE for
    RTL8922D
  wifi: rtw89: phy: support PHY status IE-09 GEN2 for RTL8922D
  wifi: rtw89: check skb headroom before adding radiotap

 drivers/net/wireless/realtek/rtw89/core.c     | 615 ++++++++++++++++--
 drivers/net/wireless/realtek/rtw89/core.h     |  29 +-
 drivers/net/wireless/realtek/rtw89/debug.c    | 165 +++--
 drivers/net/wireless/realtek/rtw89/mac80211.c |   8 +
 drivers/net/wireless/realtek/rtw89/phy.c      |  44 +-
 drivers/net/wireless/realtek/rtw89/phy.h      |   8 +
 drivers/net/wireless/realtek/rtw89/phy_be.c   |   8 +
 drivers/net/wireless/realtek/rtw89/txrx.h     | 108 +++
 8 files changed, 879 insertions(+), 106 deletions(-)


base-commit: c1ed02655f9134d6af6a01a58b734329c2f4f22c
-- 
2.25.1


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 01/14] wifi: rtw89: add AMPDU to radiotap
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 02/14] wifi: rtw89: add VHT beamformed " Ping-Ke Shih
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

The RX desc can report current frame is in AMPDU, but no way point out
if it is a last one in AMPDU. Update AMPDU reference only.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 8 ++++++++
 drivers/net/wireless/realtek/rtw89/core.h | 1 +
 2 files changed, 9 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index b85728ceb63c..240502b25b2e 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3568,6 +3568,7 @@ void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev,
 	desc_info->hw_dec = le32_get_bits(rxd_s->dword3, AX_RXD_HW_DEC);
 	desc_info->sw_dec = le32_get_bits(rxd_s->dword3, AX_RXD_SW_DEC);
 	desc_info->addr1_match = le32_get_bits(rxd_s->dword3, AX_RXD_A1_MATCH);
+	desc_info->ampdu = le32_get_bits(rxd_s->dword3, AX_RXD_AMPDU);
 
 	shift_len = desc_info->shift << 1; /* 2-byte unit */
 	drv_info_len = desc_info->drv_info_size << 3; /* 8-byte unit */
@@ -3624,6 +3625,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev,
 	desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC);
 	desc_info->sw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_SW_DEC);
 	desc_info->addr1_match = le32_get_bits(rxd_s->dword3, BE_RXD_A1_MATCH);
+	desc_info->ampdu = le32_get_bits(rxd_s->dword3, BE_RXD_AMPDU);
 
 	desc_info->bw = le32_get_bits(rxd_s->dword4, BE_RXD_BW_MASK);
 	desc_info->data_rate = le32_get_bits(rxd_s->dword4, BE_RXD_RX_DATARATE_MASK);
@@ -3698,6 +3700,7 @@ void rtw89_core_query_rxdesc_v3(struct rtw89_dev *rtwdev,
 	desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC);
 	desc_info->sw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_SW_DEC);
 	desc_info->addr1_match = le32_get_bits(rxd_s->dword3, BE_RXD_A1_MATCH);
+	desc_info->ampdu = le32_get_bits(rxd_s->dword3, BE_RXD_AMPDU);
 
 	desc_info->bw = le32_get_bits(rxd_s->dword4, BE_RXD_BW_MASK);
 	desc_info->data_rate = le32_get_bits(rxd_s->dword4, BE_RXD_RX_DATARATE_MASK);
@@ -3830,6 +3833,11 @@ static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev,
 	    !(desc_info->sw_dec || desc_info->icv_err))
 		rx_status->flag |= RX_FLAG_DECRYPTED;
 
+	if (desc_info->ampdu) {
+		rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+		rx_status->ampdu_reference = desc_info->ppdu_cnt;
+	}
+
 	rx_status->bw = rtw89_hw_to_rate_info_bw(desc_info->bw);
 
 	data_rate = desc_info->data_rate;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index bccfee7535a7..66dbb1fc3ca8 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1130,6 +1130,7 @@ struct rtw89_rx_desc_info {
 	bool hw_dec;
 	bool sw_dec;
 	bool addr1_match;
+	bool ampdu;
 	u8 frag;
 	u16 seq;
 	u8 frame_type;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 02/14] wifi: rtw89: add VHT beamformed to radiotap
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 01/14] wifi: rtw89: add AMPDU to radiotap Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 03/14] wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR Ping-Ke Shih
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

Set VHT beamformed bit by PHY status IE-01 report.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 240502b25b2e..d6bf1d57e8e1 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -2099,6 +2099,7 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev,
 	phy_ppdu->chan_idx = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_CH_IDX);
 	phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC);
 	phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC);
+	phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF);
 
 	if (!phy_ppdu->hdr_2_en)
 		phy_ppdu->rx_path_en =
@@ -2115,7 +2116,6 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev,
 	phy_ppdu->ofdm.avg_snr = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_AVG_SNR);
 	phy_ppdu->ofdm.evm_max = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MAX);
 	phy_ppdu->ofdm.evm_min = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MIN);
-	phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF);
 	phy_ppdu->ofdm.has = true;
 
 	/* sign conversion for S(12,2) */
@@ -3146,6 +3146,8 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev,
 	if (!phy_ppdu)
 		return;
 
+	if (phy_ppdu->bf)
+		rx_status->enc_flags |= RX_ENC_FLAG_BF;
 	if (phy_ppdu->ldpc)
 		rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
 	if (phy_ppdu->stbc)
@@ -6848,7 +6850,8 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
 
 	hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
 				    IEEE80211_RADIOTAP_MCS_HAVE_STBC;
-	hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC;
+	hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
+				    IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
 
 	ieee80211_hw_set(hw, SIGNAL_DBM);
 	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 03/14] wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 01/14] wifi: rtw89: add AMPDU to radiotap Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 02/14] wifi: rtw89: add VHT beamformed " Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 04/14] wifi: rtw89: phy: define PHY status IE length for generations Ping-Ke Shih
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

The SNIFFER_MODE bit can ignore filter rules, and receive packets to
driver, so set the bit to accept all packets.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/mac80211.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index b72f6661fbd1..2c6711133c80 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -356,6 +356,11 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,
 		}
 	}
 
+	if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)
+		rtwdev->hal.rx_fltr |= B_AX_SNIFFER_MODE;
+	else
+		rtwdev->hal.rx_fltr &= ~B_AX_SNIFFER_MODE;
+
 	rx_fltr = rtwdev->hal.rx_fltr;
 
 	/* mac80211 doesn't configure filter when HW scan, driver need to
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 04/14] wifi: rtw89: phy: define PHY status IE length for generations
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (2 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 03/14] wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 05/14] wifi: rtw89: phy: enable IE-09/IE-10 PHY status report for monitor mode Ping-Ke Shih
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

Both RTL8922A and RTL8922D are WiFi 7 chips, but their IE length of PHY
status are different. Define them accordingly.

Generation 0: WiFi 6 chips
Generation 1: WiFi 7 RTL8922A
Generation 2: WiFi 7 RTL8922D

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c   | 19 ++-----------------
 drivers/net/wireless/realtek/rtw89/phy.c    |  4 ++++
 drivers/net/wireless/realtek/rtw89/phy.h    |  5 +++++
 drivers/net/wireless/realtek/rtw89/phy_be.c |  8 ++++++++
 4 files changed, 19 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index d6bf1d57e8e1..caedb2bd21d5 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -2040,29 +2040,14 @@ static void rtw89_core_rx_process_phy_ppdu_iter(void *data,
 	}
 }
 
-#define VAR_LEN 0xff
-#define VAR_LEN_UNIT 8
 static u16 rtw89_core_get_phy_status_ie_len(struct rtw89_dev *rtwdev,
 					    const struct rtw89_phy_sts_iehdr *iehdr)
 {
-	static const u8 physts_ie_len_tabs[RTW89_CHIP_GEN_NUM][32] = {
-		[RTW89_CHIP_AX] = {
-			16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN,
-			VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN,
-			VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32
-		},
-		[RTW89_CHIP_BE] = {
-			32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN,
-			VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN,
-			VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32
-		},
-	};
-	const u8 *physts_ie_len_tab;
+	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
+	const u8 *physts_ie_len_tab = phy->physt_ie_len;
 	u16 ie_len;
 	u8 ie;
 
-	physts_ie_len_tab = physts_ie_len_tabs[rtwdev->chip->chip_gen];
-
 	ie = le32_get_bits(iehdr->w0, RTW89_PHY_STS_IEHDR_TYPE);
 	if (physts_ie_len_tab[ie] != VAR_LEN)
 		ie_len = physts_ie_len_tab[ie];
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index f98a77f9fc1a..4f82b1a9fa4c 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -8948,6 +8948,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_ax = {
 	.cr_base = 0x10000,
 	.physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START,
 	.physt_bmp_eht = 0xfc,
+	.physt_ie_len = {16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN,
+			 VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN,
+			 VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32},
+	.physt_gen = 0,
 	.ccx = &rtw89_ccx_regs_ax,
 	.physts = &rtw89_physts_regs_ax,
 	.cfo = &rtw89_cfo_regs_ax,
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index a6e685654037..d8038ae5ca86 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -154,6 +154,9 @@
 #define EDCCA_UNIT_CONVER 128
 #define EDCCA_PWROFST_DEFAULT 18
 
+#define VAR_LEN 0xff
+#define VAR_LEN_UNIT 8
+
 enum rtw89_phy_c2h_ra_func {
 	RTW89_PHY_C2H_FUNC_STS_RPT,
 	RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT,
@@ -573,6 +576,8 @@ struct rtw89_phy_gen_def {
 	u32 cr_base;
 	u32 physt_bmp_start;
 	u32 physt_bmp_eht;
+	u8 physt_ie_len[32];
+	u8 physt_gen;
 	const struct rtw89_ccx_regs *ccx;
 	const struct rtw89_physts_regs *physts;
 	const struct rtw89_cfo_regs *cfo;
diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c
index 5cd298a2c91b..23137f2dbd4b 100644
--- a/drivers/net/wireless/realtek/rtw89/phy_be.c
+++ b/drivers/net/wireless/realtek/rtw89/phy_be.c
@@ -1561,6 +1561,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be = {
 	.cr_base = 0x20000,
 	.physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START,
 	.physt_bmp_eht = R_PHY_STS_BITMAP_EHT,
+	.physt_ie_len = {32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN,
+			 VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN,
+			 VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32},
+	.physt_gen = 1,
 	.ccx = &rtw89_ccx_regs_be,
 	.physts = &rtw89_physts_regs_be,
 	.cfo = &rtw89_cfo_regs_be,
@@ -1582,6 +1586,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1 = {
 	.cr_base = 0x0,
 	.physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START_BE4,
 	.physt_bmp_eht = R_PHY_STS_BITMAP_EHT_BE4,
+	.physt_ie_len = {32, 40, 24, 24, 16, 16, 16, 16, VAR_LEN, VAR_LEN, VAR_LEN, 168,
+			 VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 32, 56,
+			 96, VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32},
+	.physt_gen = 2,
 	.ccx = &rtw89_ccx_regs_be_v1,
 	.physts = &rtw89_physts_regs_be_v1,
 	.cfo = &rtw89_cfo_regs_be_v1,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 05/14] wifi: rtw89: phy: enable IE-09/IE-10 PHY status report for monitor mode
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (3 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 04/14] wifi: rtw89: phy: define PHY status IE length for generations Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 06/14] wifi: rtw89: move HE radiotap to an individual function Ping-Ke Shih
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

The IE-09/IE-10 of PHY status contain SIG-A/SIG-B respectively, so enable
them in monitor mode to have rich information. If the parser detects
length invalid, ignore to reference IE-09/IE-10 to prevent accessing out
of range.

The RTL8922D is generation 2 of PHY status, which doesn't report SIG-B by
IE-10, so not enable it.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c     | 43 +++++++++++++++++--
 drivers/net/wireless/realtek/rtw89/core.h     |  6 ++-
 drivers/net/wireless/realtek/rtw89/mac80211.c |  3 ++
 drivers/net/wireless/realtek/rtw89/phy.c      | 17 +++++++-
 drivers/net/wireless/realtek/rtw89/phy.h      |  1 +
 drivers/net/wireless/realtek/rtw89/txrx.h     |  9 ++++
 6 files changed, 74 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index caedb2bd21d5..f877c2707c84 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -2152,14 +2152,38 @@ static void rtw89_core_parse_phy_status_ie00_v2(struct rtw89_dev *rtwdev,
 		rpl_path[i] = tmp_rpl[i] >> 1;
 }
 
+static void rtw89_core_parse_phy_status_ie09(struct rtw89_dev *rtwdev,
+					     const struct rtw89_phy_sts_iehdr *iehdr,
+					     struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+	phy_ppdu->ie09 = (const void *)iehdr;
+}
+
+static void rtw89_core_parse_phy_status_ie10(struct rtw89_dev *rtwdev,
+					     const struct rtw89_phy_sts_iehdr *iehdr,
+					     struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+	phy_ppdu->ie10 = (const void *)iehdr;
+}
+
 static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev,
 					    const struct rtw89_phy_sts_iehdr *iehdr,
 					    struct rtw89_rx_phy_ppdu *phy_ppdu)
 {
+	bool accept;
 	u8 ie;
 
 	ie = le32_get_bits(iehdr->w0, RTW89_PHY_STS_IEHDR_TYPE);
 
+	/*
+	 * For normal mode, only parse ppdu_sts that are A1-matched, except for
+	 * scanning that needs to get chan_idx in IE01.
+	 */
+	accept = phy_ppdu->to_self || ie == RTW89_PHYSTS_IE01_CMN_OFDM ||
+		 rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR;
+	if (!accept)
+		return 0;
+
 	switch (ie) {
 	case RTW89_PHYSTS_IE00_CMN_CCK:
 		rtw89_core_parse_phy_status_ie00(rtwdev, iehdr, phy_ppdu);
@@ -2169,6 +2193,12 @@ static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev,
 	case RTW89_PHYSTS_IE01_CMN_OFDM:
 		rtw89_core_parse_phy_status_ie01(rtwdev, iehdr, phy_ppdu);
 		break;
+	case RTW89_PHYSTS_IE09_FTR_0:
+		rtw89_core_parse_phy_status_ie09(rtwdev, iehdr, phy_ppdu);
+		break;
+	case RTW89_PHYSTS_IE10_FTR_PLCP_EXT:
+		rtw89_core_parse_phy_status_ie10(rtwdev, iehdr, phy_ppdu);
+		break;
 	default:
 		break;
 	}
@@ -2228,11 +2258,14 @@ static int rtw89_core_rx_process_phy_ppdu(struct rtw89_dev *rtwdev,
 static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,
 				       struct rtw89_rx_phy_ppdu *phy_ppdu)
 {
-	u16 ie_len;
 	void *pos, *end;
+	bool accept;
+	u16 ie_len;
 
-	/* mark invalid reports and bypass them */
-	if (phy_ppdu->ie < RTW89_CCK_PKT)
+	/* for normal mode, mark invalid reports and bypass them */
+	accept = phy_ppdu->ie >= RTW89_CCK_PKT ||
+		 rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR;
+	if (!accept)
 		return -EINVAL;
 
 	pos = phy_ppdu->buf + PHY_STS_HDR_LEN;
@@ -2246,6 +2279,10 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,
 		rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu);
 		pos += ie_len;
 		if (pos > end || ie_len == 0) {
+			/* clear pointers to prevent accessing out of IE */
+			phy_ppdu->ie09 = NULL;
+			phy_ppdu->ie10 = NULL;
+
 			rtw89_debug(rtwdev, RTW89_DBG_TXRX,
 				    "phy status parse failed\n");
 			return -EINVAL;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 66dbb1fc3ca8..ca716e95cb2c 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -24,6 +24,8 @@ struct rtw89_h2c_rf_tssi;
 struct rtw89_fw_txpwr_track_cfg;
 struct rtw89_phy_rfk_log_fmt;
 struct rtw89_phy_calc_efuse_gain;
+struct rtw89_phy_sts_ie09;
+struct rtw89_phy_sts_ie10;
 struct rtw89_debugfs;
 struct rtw89_regd_data;
 struct rtw89_wow_cam_info;
@@ -840,7 +842,7 @@ struct rtw89_rx_phy_ppdu {
 	u8 mac_id;
 	u8 chan_idx;
 	u8 phy_idx;
-	u8 ie;
+	u8 ie; /* enum rtw89_phy_status_bitmap */
 	u16 rate;
 	u8 rpl_avg;
 	u8 rpl_path[RF_PATH_MAX];
@@ -862,6 +864,8 @@ struct rtw89_rx_phy_ppdu {
 	bool to_self;
 	bool valid;
 	bool hdr_2_en;
+	const struct rtw89_phy_sts_ie09 *ie09; /* SIG-A */
+	const struct rtw89_phy_sts_ie10 *ie10; /* SIG-B */
 };
 
 enum rtw89_mac_idx {
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 2c6711133c80..330ece51286d 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -98,6 +98,9 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
 	    !rtwdev->scanning)
 		rtw89_enter_ips(rtwdev);
 
+	if (changed & IEEE80211_CONF_CHANGE_MONITOR)
+		rtw89_physts_parsing_init(rtwdev);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index 4f82b1a9fa4c..15483a86951d 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -7085,10 +7085,21 @@ static void rtw89_physts_enable_hdr_2(struct rtw89_dev *rtwdev, enum rtw89_phy_i
 static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev,
 					enum rtw89_phy_idx phy_idx)
 {
+	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
 	const struct rtw89_chip_info *chip = rtwdev->chip;
+	u32 monitor_mode_mu_ies = 0;
+	u32 monitor_mode_su_ies = 0;
 	u32 val;
 	u8 i;
 
+	if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) {
+		monitor_mode_mu_ies = BIT(RTW89_PHYSTS_IE09_FTR_0);
+		if (phy->physt_gen < 2)
+			monitor_mode_mu_ies |= BIT(RTW89_PHYSTS_IE10_FTR_PLCP_EXT);
+
+		monitor_mode_su_ies = BIT(RTW89_PHYSTS_IE09_FTR_0);
+	}
+
 	rtw89_physts_enable_fail_report(rtwdev, false, phy_idx);
 
 	/* enable hdr_2 for 8922D (PHYSTS_BE_GEN2 above) */
@@ -7102,6 +7113,7 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev,
 		val = rtw89_physts_get_ie_bitmap(rtwdev, i, phy_idx);
 		if (i == RTW89_HE_MU || i == RTW89_VHT_MU) {
 			val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF);
+			val |= monitor_mode_mu_ies;
 		} else if (i == RTW89_TRIG_BASE_PPDU) {
 			val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF) |
 			       BIT(RTW89_PHYSTS_IE01_CMN_OFDM);
@@ -7115,11 +7127,14 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev,
 				val |= BIT(RTW89_PHYSTS_IE20_DBG_OFDM_FD_USER_SEG_0);
 		}
 
+		if (i == RTW89_HE_PKT || i == RTW89_VHT_PKT)
+			val |= monitor_mode_su_ies;
+
 		rtw89_physts_set_ie_bitmap(rtwdev, i, val, phy_idx);
 	}
 }
 
-static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev)
+void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev)
 {
 	__rtw89_physts_parsing_init(rtwdev, RTW89_PHY_0);
 	if (rtwdev->dbcc_en)
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index d8038ae5ca86..74fbf5baff58 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -914,6 +914,7 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev,
 void rtw89_phy_dm_init(struct rtw89_dev *rtwdev);
 void rtw89_phy_dm_reinit(struct rtw89_dev *rtwdev);
 void rtw89_phy_dm_init_data(struct rtw89_dev *rtwdev);
+void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev);
 void rtw89_phy_write32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask,
 			   u32 data, enum rtw89_phy_idx phy_idx);
 void rtw89_phy_write32_idx_set(struct rtw89_dev *rtwdev, u32 addr, u32 bits,
diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h
index 125ba2a9f145..18fe6d3d0f83 100644
--- a/drivers/net/wireless/realtek/rtw89/txrx.h
+++ b/drivers/net/wireless/realtek/rtw89/txrx.h
@@ -658,6 +658,15 @@ struct rtw89_phy_sts_ie01_v2 {
 #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_C GENMASK(11, 4)
 #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_D GENMASK(23, 16)
 
+struct rtw89_phy_sts_ie09 {
+	__le64 qw0;
+} __packed;
+
+struct rtw89_phy_sts_ie10 {
+	__le64 qw0;
+	u8 sigb[];
+} __packed;
+
 enum rtw89_tx_channel {
 	RTW89_TXCH_ACH0	= 0,
 	RTW89_TXCH_ACH1	= 1,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 06/14] wifi: rtw89: move HE radiotap to an individual function
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (4 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 05/14] wifi: rtw89: phy: enable IE-09/IE-10 PHY status report for monitor mode Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 07/14] wifi: rtw89: fill VHT radiotap Ping-Ke Shih
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

To implement more fields of HE radiotap, move the code to an individual
function ahead. Not change logic at all.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 36 +++++++++++++----------
 1 file changed, 21 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index f877c2707c84..a27e482e38a6 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3176,6 +3176,24 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev,
 		rx_status->enc_flags |= u8_encode_bits(1, RX_ENC_FLAG_STBC_MASK);
 }
 
+static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev,
+					  struct sk_buff *skb,
+					  struct ieee80211_rx_status *rx_status)
+{
+	static const struct ieee80211_radiotap_he known_he = {
+		.data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
+				     IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN |
+				     IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN |
+				     IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN),
+		.data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN),
+	};
+	struct ieee80211_radiotap_he *he;
+
+	rx_status->flag |= RX_FLAG_RADIOTAP_HE;
+	he = skb_push(skb, sizeof(*he));
+	*he = known_he;
+}
+
 static const u8 rx_status_bw_to_radiotap_eht_usig[] = {
 	[RATE_INFO_BW_20] = IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_20MHZ,
 	[RATE_INFO_BW_5] = U8_MAX,
@@ -3250,25 +3268,13 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev,
 				       struct sk_buff *skb,
 				       struct ieee80211_rx_status *rx_status)
 {
-	static const struct ieee80211_radiotap_he known_he = {
-		.data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
-				     IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN |
-				     IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN |
-				     IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN),
-		.data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN),
-	};
-	struct ieee80211_radiotap_he *he;
-
 	if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR))
 		return;
 
-	if (rx_status->encoding == RX_ENC_HE) {
-		rx_status->flag |= RX_FLAG_RADIOTAP_HE;
-		he = skb_push(skb, sizeof(*he));
-		*he = known_he;
-	} else if (rx_status->encoding == RX_ENC_EHT) {
+	if (rx_status->encoding == RX_ENC_HE)
+		rtw89_core_update_radiotap_he(rtwdev, skb, rx_status);
+	else if (rx_status->encoding == RX_ENC_EHT)
 		rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status);
-	}
 }
 
 static void rtw89_core_validate_rx_signal(struct ieee80211_rx_status *rx_status)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 07/14] wifi: rtw89: fill VHT radiotap
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (5 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 06/14] wifi: rtw89: move HE radiotap to an individual function Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 08/14] wifi: rtw89: fill HE-SU/HE-TB/HE-MU/HE-EXT_SU radiotap Ping-Ke Shih
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

Fill VHT radiotap by PHY status IE-09 which contains VHT SIG-A.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 53 +++++++++++++++++++++--
 drivers/net/wireless/realtek/rtw89/core.h |  4 +-
 drivers/net/wireless/realtek/rtw89/txrx.h | 26 +++++++++++
 3 files changed, 79 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index a27e482e38a6..2a9326335c53 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3176,6 +3176,50 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev,
 		rx_status->enc_flags |= u8_encode_bits(1, RX_ENC_FLAG_STBC_MASK);
 }
 
+static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev,
+					   struct sk_buff *skb,
+					   struct ieee80211_rx_status *rx_status,
+					   struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+	const struct rtw89_phy_sts_ie09 *ie09;
+	struct ieee80211_radiotap_vht *vht;
+	u8 group_id;
+	u32 sig_a1;
+	u16 paid;
+	u8 nss;
+
+	if (!phy_ppdu)
+		return;
+
+	ie09 = phy_ppdu->ie09;
+	if (!ie09)
+		return;
+
+	vht = skb_push(skb, sizeof(*vht));
+	memset(vht, 0, sizeof(*vht));
+	rx_status->flag |= RX_FLAG_RADIOTAP_VHT;
+
+	sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK);
+
+	group_id = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID);
+	vht->group_id = group_id;
+
+	if (group_id == 0 || group_id == 63) {
+		paid = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_PAID);
+		vht->partial_aid = cpu_to_le16(paid);
+	} else {
+		/* let mac80211 fill vht->mcs_nss[0] */
+#define WITH_MCS_IS_NOT_KNOWN(nss) ((nss) ? (15 << 4) | (nss) : 0)
+		nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS1);
+		vht->mcs_nss[1] = WITH_MCS_IS_NOT_KNOWN(nss);
+		nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS2);
+		vht->mcs_nss[2] = WITH_MCS_IS_NOT_KNOWN(nss);
+		nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS3);
+		vht->mcs_nss[3] = WITH_MCS_IS_NOT_KNOWN(nss);
+#undef WITH_MCS_IS_NOT_KNOWN
+	}
+}
+
 static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev,
 					  struct sk_buff *skb,
 					  struct ieee80211_rx_status *rx_status)
@@ -3266,12 +3310,15 @@ static void rtw89_core_update_radiotap_eht(struct rtw89_dev *rtwdev,
 
 static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev,
 				       struct sk_buff *skb,
-				       struct ieee80211_rx_status *rx_status)
+				       struct ieee80211_rx_status *rx_status,
+				       struct rtw89_rx_phy_ppdu *phy_ppdu)
 {
 	if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR))
 		return;
 
-	if (rx_status->encoding == RX_ENC_HE)
+	if (rx_status->encoding == RX_ENC_VHT)
+		rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu);
+	else if (rx_status->encoding == RX_ENC_HE)
 		rtw89_core_update_radiotap_he(rtwdev, skb, rx_status);
 	else if (rx_status->encoding == RX_ENC_EHT)
 		rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status);
@@ -3484,7 +3531,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,
 	rtw89_core_hw_to_sband_rate(rx_status);
 	rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu);
 	rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu);
-	rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status);
+	rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status, phy_ppdu);
 	rtw89_core_validate_rx_signal(rx_status);
 	rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status);
 	rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index ca716e95cb2c..d1cd5997cf97 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -58,6 +58,7 @@ extern const struct ieee80211_ops rtw89_ops;
 #define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR)
 #define DELTA_SWINGIDX_SIZE 30
 
+#define RTW89_RADIOTAP_ROOM_VHT sizeof(struct ieee80211_radiotap_vht)
 #define RTW89_RADIOTAP_ROOM_HE sizeof(struct ieee80211_radiotap_he)
 #define RTW89_RADIOTAP_ROOM_EHT \
 	(sizeof(struct ieee80211_radiotap_tlv) + \
@@ -65,7 +66,8 @@ extern const struct ieee80211_ops rtw89_ops;
 	 sizeof(struct ieee80211_radiotap_tlv) + \
 	 ALIGN(sizeof(struct ieee80211_radiotap_eht_usig), 4))
 #define RTW89_RADIOTAP_ROOM \
-	ALIGN(max(RTW89_RADIOTAP_ROOM_HE, RTW89_RADIOTAP_ROOM_EHT), 64)
+	ALIGN(max3(RTW89_RADIOTAP_ROOM_VHT, RTW89_RADIOTAP_ROOM_HE, \
+		   RTW89_RADIOTAP_ROOM_EHT), 64)
 
 #define RTW89_HTC_MASK_VARIANT GENMASK(1, 0)
 #define RTW89_HTC_VARIANT_HE 3
diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h
index 18fe6d3d0f83..ddf8742d6712 100644
--- a/drivers/net/wireless/realtek/rtw89/txrx.h
+++ b/drivers/net/wireless/realtek/rtw89/txrx.h
@@ -662,6 +662,32 @@ struct rtw89_phy_sts_ie09 {
 	__le64 qw0;
 } __packed;
 
+#define RTW89_PHY_STS_IE09_L_SIG_MASK GENMASK(21, 5) /* Legacy/VHT/HE L-SIG */
+#define RTW89_PHY_STS_IE09_L_SIG_RATE GENMASK(3, 0)
+#define RTW89_PHY_STS_IE09_L_SIG_RSVD BIT(4)
+#define RTW89_PHY_STS_IE09_L_SIG_LENGTH GENMASK(16, 5)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK GENMASK_ULL(45, 22) /* VHT SIG-A1 */
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_BW GENMASK(1, 0)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD1 BIT(2)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_STBC BIT(3)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID GENMASK(9, 4)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_NSTS GENMASK(12, 10)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_PAID GENMASK(21, 13)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS0 GENMASK(12, 10)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS1 GENMASK(15, 13)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS2 GENMASK(18, 16)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS3 GENMASK(21, 19)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_TXOP_NOPS BIT(22)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD2 BIT(23)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK GENMASK_ULL(55, 46) /* VHT SIG-A2 */
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI BIT(0)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI_DISA BIT(1)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE0 BIT(2)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_LDPC BIT(3)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE3 GENMASK(7, 4)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8)
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9)
+
 struct rtw89_phy_sts_ie10 {
 	__le64 qw0;
 	u8 sigb[];
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 08/14] wifi: rtw89: fill HE-SU/HE-TB/HE-MU/HE-EXT_SU radiotap
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (6 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 07/14] wifi: rtw89: fill VHT radiotap Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 09/14] wifi: rtw89: debug: make implementation of beacon_info entry in order Ping-Ke Shih
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

Fill HE radiotap by PHY status IE-09/IE-10 which contains HE SIG-A/SIG-B
respectively.

The IE-10 may contain two content channels (if bandwidth is larger than
40MHz), and starting address of second content channel can be calculated by
length of first content channel containing up to 15 user fields with 8-byte
alignment.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 393 +++++++++++++++++++++-
 drivers/net/wireless/realtek/rtw89/core.h |  18 +-
 drivers/net/wireless/realtek/rtw89/txrx.h |  54 +++
 3 files changed, 461 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 2a9326335c53..7fa479ce52e2 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3220,9 +3220,361 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev,
 	}
 }
 
+static void rtw89_core_update_radiotap_he_su(struct rtw89_dev *rtwdev,
+					     struct sk_buff *skb,
+					     struct ieee80211_rx_status *rx_status,
+					     struct ieee80211_radiotap_he *he,
+					     const struct rtw89_phy_sts_ie09 *ie09)
+{
+	u32 sig_a1, sig_a2;
+	u16 t;
+
+	if (!ie09)
+		return;
+
+	sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK);
+	sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK);
+
+	he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN |
+				 0);
+
+	he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN |
+				 0);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE);
+	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL);
+	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL);
+
+	rx_status->he_dcm = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_DCM);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BSS_COLOR);
+	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_SR);
+	he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP);
+	he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG);
+	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_BEAMFORMED);
+	he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_TXBF);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PREFEC);
+	he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PE);
+	he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER);
+	he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER);
+}
+
+static void rtw89_core_update_radiotap_he_tb(struct rtw89_dev *rtwdev,
+					     struct sk_buff *skb,
+					     struct ieee80211_rx_status *rx_status,
+					     struct ieee80211_radiotap_he *he,
+					     const struct rtw89_phy_sts_ie09 *sig)
+{
+	u32 sig_a1, sig_a2;
+	u16 t;
+
+	if (!sig)
+		return;
+
+	sig_a1 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK);
+	sig_a2 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK);
+
+	he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN);
+
+	he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR);
+	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1);
+	he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1);
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB2);
+	he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2);
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB3);
+	he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3);
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB4);
+	he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP);
+	he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP);
+}
+
+static u8 rtw89_core_he_mu_get_n_user_fields_by_ru_alloc(struct rtw89_dev *rtwdev,
+							 u8 ru_alloc)
+{
+	u8 mu_users;
+
+	if (ru_alloc <= 15) {
+		static const u8 entries_0_15[16] = {
+			9, 8, 8, 7, 8, 7, 7, 6, 8, 7, 7, 6, 7, 6, 6, 5,
+		};
+
+		return entries_0_15[ru_alloc];
+	}
+
+	if (ru_alloc >= 16 && ru_alloc <= 95) {
+		mu_users = ru_alloc & 0x07;
+
+		switch (ru_alloc & 0xF8) {
+		case 16: case 24: return mu_users + 3;
+		case 32: case 64: return mu_users + 6;
+		case 40: case 48: case 72: case 80: return mu_users + 5;
+		case 56: case 88: return mu_users + 4;
+		}
+	}
+
+	if (ru_alloc >= 96 && ru_alloc <= 111) {
+		mu_users = ru_alloc & 0x03;
+		mu_users += ru_alloc >> 2 & 0x03;
+
+		return mu_users + 2;
+	}
+
+	if (ru_alloc == 112)
+		return 4;
+
+	if (ru_alloc >= 113 && ru_alloc <= 115)
+		return 0;
+
+	if (ru_alloc >= 128 && ru_alloc <= 191) {
+		mu_users = ru_alloc & 0x07;
+		mu_users += ru_alloc >> 3 & 0x07;
+
+		return mu_users + 3;
+	}
+
+	if (ru_alloc >= 192 && ru_alloc <= 215)
+		return (ru_alloc & 0x07) + 1;
+
+	return 0;
+}
+
+static int rtw89_core_he_mu_get_n_ru_alloc(struct rtw89_dev *rtwdev, u8 bw)
+{
+	switch (bw) {
+	case 0:
+	case 1:
+		return 1;
+	case 2:
+	case 4:
+	case 5:
+		return 2;
+	case 3:
+	case 6:
+	case 7:
+		return 3;
+	}
+
+	return 0;
+}
+
+static void rtw89_core_he_mu_get_cc_ptr(struct rtw89_dev *rtwdev,
+					const struct rtw89_phy_sts_ie10 *ie10,
+					u8 bw, int n_ru, int n_center_26tone,
+					const u8 **c1, const u8 **c2)
+{
+	const u8 *sigb, *end_sigb;
+	int total_bits, c2_offset;
+	int n_user_fields = 0;
+	const u8 *ptr;
+	int i;
+
+	sigb = &ie10->sigb[0];
+	end_sigb = (const void *)ie10 +
+		   rtw89_core_get_phy_status_ie_len(rtwdev, (const void *)ie10);
+
+	ptr = sigb;
+	if (ptr + n_ru + n_center_26tone >= end_sigb)
+		return;
+	*c1 = ptr;
+
+	if (bw == 0)	/* 20MHz doesn't have content channel 2 */
+		return;
+
+	for (i = 0; i < n_ru; i++)
+		n_user_fields +=
+			rtw89_core_he_mu_get_n_user_fields_by_ru_alloc(rtwdev, (*c1)[i]);
+
+	/* hardware report max 15 recordes, align 8 */
+	n_user_fields = min(n_user_fields, 15);
+
+	total_bits = n_ru * 8 + n_center_26tone + 10 +
+		     n_user_fields * 21 + DIV_ROUND_UP(n_user_fields, 2) * 10;
+	c2_offset = ALIGN(DIV_ROUND_UP(total_bits, 8), 8);
+
+	ptr = &sigb[c2_offset];
+	if (ptr + n_ru + n_center_26tone >= end_sigb)
+		return;
+
+	*c2 = ptr;
+}
+
+static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev,
+					     struct sk_buff *skb,
+					     struct ieee80211_rx_status *rx_status,
+					     struct ieee80211_radiotap_he *he,
+					     struct ieee80211_radiotap_he_mu *he_mu,
+					     const struct rtw89_phy_sts_ie09 *ie09,
+					     const struct rtw89_phy_sts_ie10 *ie10)
+{
+	const u8 *c1 = NULL, *c2 = NULL;
+	int n_center_26tone, n_ru, i;
+	bool doppler, comp;
+	u32 sig_a1, sig_a2;
+	u16 t;
+	u8 bw;
+
+	if (!ie09)
+		return;
+
+	he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN);
+	he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN |
+				 IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN);
+
+	sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK);
+	sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL);
+	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BSS_COLOR);
+	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SR);
+	he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE);
+
+	doppler = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER);
+	he->data6 |= le16_encode_bits(doppler, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP);
+	he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP);
+
+	if (doppler) {
+		t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MID);
+		he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY);
+	}
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LPDC_XSYMSEG);
+	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_FEC);
+	he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD);
+
+	t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_PE);
+	he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG);
+
+	he_mu->flags1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN |
+				     IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN |
+				     IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN |
+				     IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN);
+	he_mu->flags2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS);
+	he_mu->flags1 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM);
+	he_mu->flags1 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM);
+
+	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_SYM_USR);
+	he_mu->flags2 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS);
+
+	comp = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_COM);
+	he_mu->flags2 |= le16_encode_bits(comp, IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP);
+
+	bw = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BW);
+	he_mu->flags2 |= le16_encode_bits(bw, IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW);
+
+	if (comp == 0 && bw <= 5) {
+		u8 punc = clamp_t(int, bw - 3, 0, 2);
+
+		he_mu->flags2 |=
+			cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN);
+		he_mu->flags2 |=
+			le16_encode_bits(punc, IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW);
+	}
+
+	/* the Common field in the HE-SIG-B field is not present */
+	if (comp || !ie10)
+		return;
+
+	n_ru = rtw89_core_he_mu_get_n_ru_alloc(rtwdev, bw);
+	n_center_26tone = bw >= 1;
+
+	rtw89_core_he_mu_get_cc_ptr(rtwdev, ie10, bw, n_ru, n_center_26tone, &c1, &c2);
+	if (!c1)
+		return;
+
+	he_mu->flags1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN |
+				     IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN |
+				     IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN |
+				     IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN);
+
+	for (i = 0; i < n_ru; i++) {
+		he_mu->ru_ch1[i] = c1[i];
+		if (c2)
+			he_mu->ru_ch2[i] = c2[i];
+	}
+
+	if (n_center_26tone) {
+		u8 ru;
+
+		ru = c1[n_ru] & BIT(0);
+		he_mu->flags1 |=
+			le16_encode_bits(ru, IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU);
+
+		ru = c2 ? c2[n_ru] & BIT(0) : 0;
+		he_mu->flags1 |=
+			le16_encode_bits(ru, IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU);
+	}
+}
+
+static u16 rtw89_core_get_radiotap_he_format(struct rtw89_rx_desc_info *desc_info)
+{
+	switch (desc_info->ppdu_type) {
+	case RTW89_RX_PPDU_T_HE_MU:
+		return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU;
+	case RTW89_RX_PPDU_T_HE_ERSU:
+		return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU;
+	case RTW89_RX_PPDU_T_HE_TB:
+		return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG;
+	default:
+		return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU;
+	}
+}
+
 static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev,
+					  struct rtw89_rx_desc_info *desc_info,
 					  struct sk_buff *skb,
-					  struct ieee80211_rx_status *rx_status)
+					  struct ieee80211_rx_status *rx_status,
+					  struct rtw89_rx_phy_ppdu *phy_ppdu)
 {
 	static const struct ieee80211_radiotap_he known_he = {
 		.data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
@@ -3231,11 +3583,45 @@ static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev,
 				     IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN),
 		.data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN),
 	};
+	struct ieee80211_radiotap_he_mu *he_mu = NULL;
+	const struct rtw89_phy_sts_ie09 *ie09;
+	const struct rtw89_phy_sts_ie10 *ie10;
 	struct ieee80211_radiotap_he *he;
+	u16 he_format;
+
+	he_format = rtw89_core_get_radiotap_he_format(desc_info);
+
+	/* Radiotap of HE-MU must be placed after HE (skb_push ahead) */
+	if (he_format == IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU) {
+		rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU;
+		he_mu = skb_push(skb, sizeof(*he_mu));
+		memset(he_mu, 0, sizeof(*he_mu));
+	}
 
 	rx_status->flag |= RX_FLAG_RADIOTAP_HE;
 	he = skb_push(skb, sizeof(*he));
 	*he = known_he;
+
+	he->data1 |= le16_encode_bits(he_format, IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK);
+
+	if (!phy_ppdu)
+		return;
+
+	ie09 = phy_ppdu->ie09;
+	ie10 = phy_ppdu->ie10;
+
+	switch (he_format) {
+	case IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU:
+		rtw89_core_update_radiotap_he_mu(rtwdev, skb, rx_status, he, he_mu,
+						 ie09, ie10);
+		break;
+	case IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG:
+		rtw89_core_update_radiotap_he_tb(rtwdev, skb, rx_status, he, ie09);
+		break;
+	default:
+		rtw89_core_update_radiotap_he_su(rtwdev, skb, rx_status, he, ie09);
+		break;
+	}
 }
 
 static const u8 rx_status_bw_to_radiotap_eht_usig[] = {
@@ -3309,6 +3695,7 @@ static void rtw89_core_update_radiotap_eht(struct rtw89_dev *rtwdev,
 }
 
 static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev,
+				       struct rtw89_rx_desc_info *desc_info,
 				       struct sk_buff *skb,
 				       struct ieee80211_rx_status *rx_status,
 				       struct rtw89_rx_phy_ppdu *phy_ppdu)
@@ -3319,7 +3706,7 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev,
 	if (rx_status->encoding == RX_ENC_VHT)
 		rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu);
 	else if (rx_status->encoding == RX_ENC_HE)
-		rtw89_core_update_radiotap_he(rtwdev, skb, rx_status);
+		rtw89_core_update_radiotap_he(rtwdev, desc_info, skb, rx_status, phy_ppdu);
 	else if (rx_status->encoding == RX_ENC_EHT)
 		rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status);
 }
@@ -3531,7 +3918,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,
 	rtw89_core_hw_to_sband_rate(rx_status);
 	rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu);
 	rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu);
-	rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status, phy_ppdu);
+	rtw89_core_update_radiotap(rtwdev, desc_info, skb_ppdu, rx_status, phy_ppdu);
 	rtw89_core_validate_rx_signal(rx_status);
 	rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status);
 	rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index d1cd5997cf97..b85d7fa4296a 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -59,7 +59,8 @@ extern const struct ieee80211_ops rtw89_ops;
 #define DELTA_SWINGIDX_SIZE 30
 
 #define RTW89_RADIOTAP_ROOM_VHT sizeof(struct ieee80211_radiotap_vht)
-#define RTW89_RADIOTAP_ROOM_HE sizeof(struct ieee80211_radiotap_he)
+#define RTW89_RADIOTAP_ROOM_HE (sizeof(struct ieee80211_radiotap_he) + \
+				sizeof(struct ieee80211_radiotap_he_mu))
 #define RTW89_RADIOTAP_ROOM_EHT \
 	(sizeof(struct ieee80211_radiotap_tlv) + \
 	 ALIGN(struct_size((struct ieee80211_radiotap_eht *)0, user_info, 1), 4) + \
@@ -955,6 +956,21 @@ enum rtw89_bandwidth {
 	RTW89_CHANNEL_WIDTH_10 = 7,
 };
 
+enum rtw89_rx_ppdu_type {
+	RTW89_RX_PPDU_T_LCCK = 0,
+	RTW89_RX_PPDU_T_SCCK = 1,
+	RTW89_RX_PPDU_T_OFDM = 2,
+	RTW89_RX_PPDU_T_HT = 3,
+	RTW89_RX_PPDU_T_HTGF = 4,
+	RTW89_RX_PPDU_T_VHT_SU = 5,
+	RTW89_RX_PPDU_T_VHT_MU = 6,
+	RTW89_RX_PPDU_T_HE_SU = 7,
+	RTW89_RX_PPDU_T_HE_ERSU = 8,
+	RTW89_RX_PPDU_T_HE_MU = 9,
+	RTW89_RX_PPDU_T_HE_TB = 10,
+	RTW89_RX_PPDU_T_UNKNOWN = 15,
+};
+
 enum rtw89_ps_mode {
 	RTW89_PS_MODE_NONE	= 0,
 	RTW89_PS_MODE_RFOFF	= 1,
diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h
index ddf8742d6712..4a567d3fe3f7 100644
--- a/drivers/net/wireless/realtek/rtw89/txrx.h
+++ b/drivers/net/wireless/realtek/rtw89/txrx.h
@@ -687,6 +687,60 @@ struct rtw89_phy_sts_ie09 {
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE3 GENMASK(7, 4)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE SU SIG-A1 */
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_FORMAT BIT(0)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE BIT(1)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL BIT(2)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MCS GENMASK(6, 3)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_DCM BIT(7)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BSS_COLOR GENMASK(13, 8)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_RSVD1 BIT(14)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_SR GENMASK(18, 15)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BW GENMASK(20, 19)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_GILTF GENMASK(22, 21)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_NSTS GENMASK(25, 23)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE SU SIG-A2 */
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP GENMASK(6, 0)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_CODING BIT(7)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG BIT(8)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_STBC BIT(9)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_BEAMFORMED BIT(10)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PREFEC GENMASK(12, 11)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PE BIT(13)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_RSVD2 BIT(14)
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER BIT(15)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE TB SIG-A1 */
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_FORMAT BIT(0)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR GENMASK(6, 1)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1 GENMASK(10, 7)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB2 GENMASK(14, 11)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB3 GENMASK(18, 15)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB4 GENMASK(22, 19)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_RSVD1 BIT(23)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BW GENMASK(25, 24)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE TB SIG-A2 */
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP GENMASK(6, 0)
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_RSVD1 GENMASK(15, 7)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE MU SIG-A1 */
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL BIT(0)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS GENMASK(3, 1)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM BIT(4)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BSS_COLOR GENMASK(10, 5)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SR GENMASK(14, 11)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BW GENMASK(17, 15)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_SYM_USR GENMASK(21, 18)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_COM BIT(22)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_GI_LTF GENMASK(24, 23)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER BIT(25)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE MU SIG-A2 */
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP GENMASK(6, 0)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_RSVD BIT(7)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LTF_MID GENMASK(10, 8)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MID BIT(10)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LPDC_XSYMSEG BIT(11)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_STBC BIT(12)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_FEC GENMASK(14, 13)
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_PE BIT(15)
 
 struct rtw89_phy_sts_ie10 {
 	__le64 qw0;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 09/14] wifi: rtw89: debug: make implementation of beacon_info entry in order
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (7 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 08/14] wifi: rtw89: fill HE-SU/HE-TB/HE-MU/HE-EXT_SU radiotap Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 10/14] wifi: rtw89: add debugfs entry of monitor mode options to capture HE-MU packets Ping-Ke Shih
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

When adding more debugfs entries, the beacon_info entry becomes not in
order. Move to correct location. Don't change logic at all.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/debug.c | 142 ++++++++++-----------
 1 file changed, 71 insertions(+), 71 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index 597052261974..6fe8e7254e80 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -4919,6 +4919,77 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev,
 	return count;
 }
 
+static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb,
+				 char *buf, size_t bufsz)
+{
+	struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat;
+	char *p = buf, *end = buf + bufsz;
+
+	p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx);
+	p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr);
+	p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi));
+	p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate);
+	p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len);
+
+	return p - buf;
+}
+
+static ssize_t
+rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev,
+				 struct rtw89_debugfs_priv *debugfs_priv,
+				 char *buf, size_t bufsz)
+{
+	struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track;
+	struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat;
+	struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist;
+	u16 upper, lower = bcn_stat->tbtt_tu_min;
+	char *p = buf, *end = buf + bufsz;
+	u16 *drift = bcn_stat->drift;
+	u8 bcn_num = bcn_stat->num;
+	struct rtw89_bb_ctx *bb;
+	u8 count;
+	u8 i;
+
+	rtw89_for_each_active_bb(rtwdev, bb)
+		p += rtw89_get_beacon_info(rtwdev, bb, p, end - p);
+
+	p += scnprintf(p, end - p, "[Beacon info]\n");
+	p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int);
+	p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim);
+
+	p += scnprintf(p, end - p, "\n[Distribution]\n");
+	p += scnprintf(p, end - p, "tbtt\n");
+	for (i = 0; i < RTW89_BCN_TRACK_MAX_BIN_NUM; i++) {
+		upper = lower + RTW89_BCN_TRACK_BIN_WIDTH - 1;
+		if (i == RTW89_BCN_TRACK_MAX_BIN_NUM - 1)
+			upper = max(upper, bcn_stat->tbtt_tu_max);
+
+		p += scnprintf(p, end - p, "%02u - %02u: %u\n",
+			       lower, upper, bcn_dist->bins[i]);
+
+		lower = upper + 1;
+	}
+
+	p += scnprintf(p, end - p, "\ndrift\n");
+
+	for (i = 0; i < bcn_num; i += count) {
+		count = 1;
+		while (i + count < bcn_num && drift[i] == drift[i + count])
+			count++;
+
+		p += scnprintf(p, end - p, "%u: %u\n", drift[i], count);
+	}
+	p += scnprintf(p, end - p, "\nlower bound: %u\n", bcn_dist->lower_bound);
+	p += scnprintf(p, end - p, "upper bound: %u\n", bcn_dist->upper_bound);
+	p += scnprintf(p, end - p, "outlier count: %u\n", bcn_dist->outlier_count);
+
+	p += scnprintf(p, end - p, "\n[Tracking]\n");
+	p += scnprintf(p, end - p, "tbtt offset: %u\n", bcn_track->tbtt_offset);
+	p += scnprintf(p, end - p, "bcn timeout: %u\n", bcn_track->bcn_timeout);
+
+	return p - buf;
+}
+
 enum __diag_mac_cmd {
 	__CMD_EQUALV,
 	__CMD_EQUALO,
@@ -5255,77 +5326,6 @@ rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev,
 	return p - buf;
 }
 
-static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb,
-				 char *buf, size_t bufsz)
-{
-	struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat;
-	char *p = buf, *end = buf + bufsz;
-
-	p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx);
-	p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr);
-	p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi));
-	p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate);
-	p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len);
-
-	return p - buf;
-}
-
-static ssize_t
-rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev,
-				 struct rtw89_debugfs_priv *debugfs_priv,
-				 char *buf, size_t bufsz)
-{
-	struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track;
-	struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat;
-	struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist;
-	u16 upper, lower = bcn_stat->tbtt_tu_min;
-	char *p = buf, *end = buf + bufsz;
-	u16 *drift = bcn_stat->drift;
-	u8 bcn_num = bcn_stat->num;
-	struct rtw89_bb_ctx *bb;
-	u8 count;
-	u8 i;
-
-	rtw89_for_each_active_bb(rtwdev, bb)
-		p += rtw89_get_beacon_info(rtwdev, bb, p, end - p);
-
-	p += scnprintf(p, end - p, "[Beacon info]\n");
-	p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int);
-	p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim);
-
-	p += scnprintf(p, end - p, "\n[Distribution]\n");
-	p += scnprintf(p, end - p, "tbtt\n");
-	for (i = 0; i < RTW89_BCN_TRACK_MAX_BIN_NUM; i++) {
-		upper = lower + RTW89_BCN_TRACK_BIN_WIDTH - 1;
-		if (i == RTW89_BCN_TRACK_MAX_BIN_NUM - 1)
-			upper = max(upper, bcn_stat->tbtt_tu_max);
-
-		p += scnprintf(p, end - p, "%02u - %02u: %u\n",
-			       lower, upper, bcn_dist->bins[i]);
-
-		lower = upper + 1;
-	}
-
-	p += scnprintf(p, end - p, "\ndrift\n");
-
-	for (i = 0; i < bcn_num; i += count) {
-		count = 1;
-		while (i + count < bcn_num && drift[i] == drift[i + count])
-			count++;
-
-		p += scnprintf(p, end - p, "%u: %u\n", drift[i], count);
-	}
-	p += scnprintf(p, end - p, "\nlower bound: %u\n", bcn_dist->lower_bound);
-	p += scnprintf(p, end - p, "upper bound: %u\n", bcn_dist->upper_bound);
-	p += scnprintf(p, end - p, "outlier count: %u\n", bcn_dist->outlier_count);
-
-	p += scnprintf(p, end - p, "\n[Tracking]\n");
-	p += scnprintf(p, end - p, "tbtt offset: %u\n", bcn_track->tbtt_offset);
-	p += scnprintf(p, end - p, "bcn timeout: %u\n", bcn_track->bcn_timeout);
-
-	return p - buf;
-}
-
 #define rtw89_debug_priv_get(name, opts...)			\
 {								\
 	.cb_read = rtw89_debug_priv_ ##name## _get,		\
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 10/14] wifi: rtw89: add debugfs entry of monitor mode options to capture HE-MU packets
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (8 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 09/14] wifi: rtw89: debug: make implementation of beacon_info entry in order Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 11/14] wifi: rtw89: phy: check length before parsing PHY status IE Ping-Ke Shih
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

To capture HE-MU packets, set BSS color and AID for specific connected
station.

The writing format: <bss color> <aid>
For example,
  $ echo 0x4 0x16 > monitor_opts

Read this entry to get current setting:
  bss_color=0x4 aid=0x16

By the way, add another sec2() function to create debugfs entries to
prevent running smatch timeout.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/debug.c | 57 ++++++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/phy.c   | 23 ++++++---
 drivers/net/wireless/realtek/rtw89/phy.h   |  2 +
 3 files changed, 74 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index 6fe8e7254e80..8ee800c76cfe 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -93,6 +93,7 @@ struct rtw89_debugfs {
 	struct rtw89_debugfs_priv beacon_info;
 	struct rtw89_debugfs_priv diag_mac;
 	struct rtw89_debugfs_priv diag_bb;
+	struct rtw89_debugfs_priv monitor_opts;
 };
 
 struct rtw89_debugfs_iter_data {
@@ -5326,6 +5327,54 @@ rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev,
 	return p - buf;
 }
 
+static ssize_t
+rtw89_debug_priv_monitor_opts_get(struct rtw89_dev *rtwdev,
+				  struct rtw89_debugfs_priv *debugfs_priv,
+				  char *buf, size_t bufsz)
+{
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	char *p = buf, *end = buf + bufsz;
+	u32 bss_color;
+	u32 aid;
+
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+	rtw89_leave_ps_mode(rtwdev);
+
+	bss_color = rtw89_phy_read32_idx(rtwdev, chip->bss_clr_map_reg,
+					 B_BSS_CLR_MAP_TGT, RTW89_PHY_0);
+	aid = rtw89_phy_read32_idx(rtwdev, chip->bss_clr_map_reg,
+				   B_BSS_CLR_MAP_STAID, RTW89_PHY_0);
+
+	p += scnprintf(p, end - p, "bss_color=0x%x aid=0x%x\n", bss_color, aid);
+
+	return p - buf;
+}
+
+static ssize_t
+rtw89_debug_priv_monitor_opts_set(struct rtw89_dev *rtwdev,
+				  struct rtw89_debugfs_priv *debugfs_priv,
+				  const char *buf, size_t count)
+{
+	u32 bss_color;
+	u32 aid;
+	int num;
+
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+	num = sscanf(buf, "%x %x", &bss_color, &aid);
+	if (num != 2) {
+		rtw89_info(rtwdev, "valid format: <bss color> <aid>\n");
+		return -EINVAL;
+	}
+
+	rtw89_leave_ps_mode(rtwdev);
+
+	__rtw89_phy_set_bss_color(rtwdev, bss_color, aid, RTW89_PHY_0);
+
+	return count;
+}
+
 #define rtw89_debug_priv_get(name, opts...)			\
 {								\
 	.cb_read = rtw89_debug_priv_ ##name## _get,		\
@@ -5390,6 +5439,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = {
 	.beacon_info = rtw89_debug_priv_get(beacon_info),
 	.diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK),
 	.diag_bb = rtw89_debug_priv_get(diag_bb, RSIZE_8K, RLOCK),
+	.monitor_opts = rtw89_debug_priv_set_and_get(monitor_opts, RWLOCK),
 };
 
 #define rtw89_debugfs_add(name, mode, fopname, parent)				\
@@ -5435,12 +5485,18 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top
 	rtw89_debugfs_add_r(phy_info);
 	rtw89_debugfs_add_rw(bb_info);
 	rtw89_debugfs_add_r(stations);
+}
+
+static
+void rtw89_debugfs_add_sec2(struct rtw89_dev *rtwdev, struct dentry *debugfs_topdir)
+{
 	rtw89_debugfs_add_rw(disable_dm);
 	rtw89_debugfs_add_rw(static_pd_th);
 	rtw89_debugfs_add_rw(mlo_mode);
 	rtw89_debugfs_add_r(beacon_info);
 	rtw89_debugfs_add_r(diag_mac);
 	rtw89_debugfs_add_r(diag_bb);
+	rtw89_debugfs_add_rw(monitor_opts);
 }
 
 void rtw89_debugfs_init(struct rtw89_dev *rtwdev)
@@ -5457,6 +5513,7 @@ void rtw89_debugfs_init(struct rtw89_dev *rtwdev)
 
 	rtw89_debugfs_add_sec0(rtwdev, debugfs_topdir);
 	rtw89_debugfs_add_sec1(rtwdev, debugfs_topdir);
+	rtw89_debugfs_add_sec2(rtwdev, debugfs_topdir);
 }
 
 void rtw89_debugfs_deinit(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index 15483a86951d..759be4dab42b 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -8231,12 +8231,24 @@ void rtw89_phy_dm_init_data(struct rtw89_dev *rtwdev)
 		__rtw89_phy_dm_init_data(rtwdev, bb);
 }
 
+void __rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, u8 bss_color, u16 aid,
+			       enum rtw89_phy_idx phy_idx)
+{
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	const struct rtw89_reg_def *bss_clr_vld = &chip->bss_clr_vld;
+
+	rtw89_phy_write32_idx(rtwdev, bss_clr_vld->addr, bss_clr_vld->mask, 0x1,
+			      phy_idx);
+	rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_TGT,
+			      bss_color, phy_idx);
+	rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_STAID,
+			      aid, phy_idx);
+}
+
 void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev,
 			     struct rtw89_vif_link *rtwvif_link)
 {
 	struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);
-	const struct rtw89_chip_info *chip = rtwdev->chip;
-	const struct rtw89_reg_def *bss_clr_vld = &chip->bss_clr_vld;
 	enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx;
 	struct ieee80211_bss_conf *bss_conf;
 	u8 bss_color;
@@ -8253,12 +8265,7 @@ void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev,
 
 	rcu_read_unlock();
 
-	rtw89_phy_write32_idx(rtwdev, bss_clr_vld->addr, bss_clr_vld->mask, 0x1,
-			      phy_idx);
-	rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_TGT,
-			      bss_color, phy_idx);
-	rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_STAID,
-			      vif->cfg.aid, phy_idx);
+	__rtw89_phy_set_bss_color(rtwdev, bss_color, vif->cfg.aid, phy_idx);
 }
 
 static bool rfk_chan_validate_desc(const struct rtw89_rfk_chan_desc *desc)
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index 74fbf5baff58..b4b818f786df 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -1128,6 +1128,8 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev);
 void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work);
 void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev,
 			     struct rtw89_vif_link *rtwvif_link);
+void __rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, u8 bss_color, u16 aid,
+			       enum rtw89_phy_idx phy_idx);
 void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev,
 					  enum rtw89_mac_idx mac_idx,
 					  enum rtw89_tssi_bandedge_cfg bandedge_cfg);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 11/14] wifi: rtw89: phy: check length before parsing PHY status IE
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (9 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 10/14] wifi: rtw89: add debugfs entry of monitor mode options to capture HE-MU packets Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 12/14] wifi: rtw89: phy: skip trailing 8-byte zeros of PHY status IE for RTL8922D Ping-Ke Shih
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

Hardware might report PHY status IE with unexpected length, and parser
might access out of range. Check the length ahead.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 7fa479ce52e2..7e1182a49942 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -2276,7 +2276,6 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,
 		const struct rtw89_phy_sts_iehdr *iehdr = pos;
 
 		ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, iehdr);
-		rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu);
 		pos += ie_len;
 		if (pos > end || ie_len == 0) {
 			/* clear pointers to prevent accessing out of IE */
@@ -2285,8 +2284,11 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,
 
 			rtw89_debug(rtwdev, RTW89_DBG_TXRX,
 				    "phy status parse failed\n");
+
 			return -EINVAL;
 		}
+
+		rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu);
 	}
 
 	rtw89_chip_convert_rpl_to_rssi(rtwdev, phy_ppdu);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 12/14] wifi: rtw89: phy: skip trailing 8-byte zeros of PHY status IE for RTL8922D
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (10 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 11/14] wifi: rtw89: phy: check length before parsing PHY status IE Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:09 ` [PATCH rtw-next 13/14] wifi: rtw89: phy: support PHY status IE-09 GEN2 " Ping-Ke Shih
  2026-05-06 13:10 ` [PATCH rtw-next 14/14] wifi: rtw89: check skb headroom before adding radiotap Ping-Ke Shih
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

Hardware reports a list of PHY status IE. In monitor mode, IE-09 of
PHY status is enabled, and the report contains trailing 8-byte zeros,
causing failed to parse and drop all IE information.

The 8 zeros are recognize as IE type 0, but length of type 0 must be
not 8 (reference to rtw89_phy_gen_def::physt_ie_len[0]).
Check and skip them.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 7e1182a49942..fddb0c822c28 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -2275,6 +2275,13 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,
 	while (pos < end) {
 		const struct rtw89_phy_sts_iehdr *iehdr = pos;
 
+		/*
+		 * RTL8922D might reports 8 bytes zeros at end if IE09 presents.
+		 * Check and ignore the zeros.
+		 */
+		if (unlikely(phy_ppdu->ie09 && end - pos == 8 && iehdr->w0 == 0))
+			break;
+
 		ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, iehdr);
 		pos += ie_len;
 		if (pos > end || ie_len == 0) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 13/14] wifi: rtw89: phy: support PHY status IE-09 GEN2 for RTL8922D
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (11 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 12/14] wifi: rtw89: phy: skip trailing 8-byte zeros of PHY status IE for RTL8922D Ping-Ke Shih
@ 2026-05-06 13:09 ` Ping-Ke Shih
  2026-05-06 13:10 ` [PATCH rtw-next 14/14] wifi: rtw89: check skb headroom before adding radiotap Ping-Ke Shih
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:09 UTC (permalink / raw)
  To: linux-wireless

The format of PHY status IE-10 for RTL8922D is different from earlier
chips. Fortunately only starting bit is different, but the layout is the
same. Get the VHT/HE SIG-A value by corresponding mask accordingly.

The IE-09 format of generation 0 and 1 are totally the same.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 72 +++++++++++++++++++----
 drivers/net/wireless/realtek/rtw89/core.h |  4 +-
 drivers/net/wireless/realtek/rtw89/txrx.h | 23 +++++++-
 3 files changed, 82 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index fddb0c822c28..c21737981b61 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -2156,7 +2156,19 @@ static void rtw89_core_parse_phy_status_ie09(struct rtw89_dev *rtwdev,
 					     const struct rtw89_phy_sts_iehdr *iehdr,
 					     struct rtw89_rx_phy_ppdu *phy_ppdu)
 {
-	phy_ppdu->ie09 = (const void *)iehdr;
+	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
+	const union rtw89_phy_sts_ie09 *ie09;
+	u16 ie_len;
+
+	ie09 = (const void *)iehdr;
+
+	if (phy->physt_gen >= 2) {
+		ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, (const void *)iehdr);
+		if (ie_len < sizeof(ie09->gen2))
+			return;
+	}
+
+	phy_ppdu->ie09 = ie09;
 }
 
 static void rtw89_core_parse_phy_status_ie10(struct rtw89_dev *rtwdev,
@@ -3190,7 +3202,8 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev,
 					   struct ieee80211_rx_status *rx_status,
 					   struct rtw89_rx_phy_ppdu *phy_ppdu)
 {
-	const struct rtw89_phy_sts_ie09 *ie09;
+	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
+	const union rtw89_phy_sts_ie09 *ie09;
 	struct ieee80211_radiotap_vht *vht;
 	u8 group_id;
 	u32 sig_a1;
@@ -3208,7 +3221,10 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev,
 	memset(vht, 0, sizeof(*vht));
 	rx_status->flag |= RX_FLAG_RADIOTAP_VHT;
 
-	sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK);
+	if (phy->physt_gen >= 2)
+		sig_a1 = le64_get_bits(ie09->gen2.qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK_GEN2);
+	else
+		sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK);
 
 	group_id = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID);
 	vht->group_id = group_id;
@@ -3233,16 +3249,26 @@ static void rtw89_core_update_radiotap_he_su(struct rtw89_dev *rtwdev,
 					     struct sk_buff *skb,
 					     struct ieee80211_rx_status *rx_status,
 					     struct ieee80211_radiotap_he *he,
-					     const struct rtw89_phy_sts_ie09 *ie09)
+					     const union rtw89_phy_sts_ie09 *ie09)
 {
+	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
 	u32 sig_a1, sig_a2;
 	u16 t;
 
 	if (!ie09)
 		return;
 
-	sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK);
-	sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK);
+	if (phy->physt_gen >= 2) {
+		sig_a1 = le64_get_bits(ie09->gen2.qw0,
+				       RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK_GEN2);
+		sig_a2 = le64_get_bits(ie09->gen2.qw0,
+				       RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_L0);
+		sig_a2 |= le64_get_bits(ie09->gen2.qw1,
+					RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_H6) << 6;
+	} else {
+		sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK);
+		sig_a2 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK);
+	}
 
 	he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN |
 				 IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN |
@@ -3295,16 +3321,26 @@ static void rtw89_core_update_radiotap_he_tb(struct rtw89_dev *rtwdev,
 					     struct sk_buff *skb,
 					     struct ieee80211_rx_status *rx_status,
 					     struct ieee80211_radiotap_he *he,
-					     const struct rtw89_phy_sts_ie09 *sig)
+					     const union rtw89_phy_sts_ie09 *sig)
 {
+	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
 	u32 sig_a1, sig_a2;
 	u16 t;
 
 	if (!sig)
 		return;
 
-	sig_a1 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK);
-	sig_a2 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK);
+	if (phy->physt_gen >= 2) {
+		sig_a1 = le64_get_bits(sig->gen2.qw0,
+				       RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK_GEN2);
+		sig_a2 = le64_get_bits(sig->gen2.qw0,
+				       RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_L0);
+		sig_a2 |= le64_get_bits(sig->gen2.qw1,
+					RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_H6);
+	} else {
+		sig_a1 = le64_get_bits(sig->gen0.qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK);
+		sig_a2 = le64_get_bits(sig->gen0.qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK);
+	}
 
 	he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN |
 				 IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
@@ -3445,9 +3481,10 @@ static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev,
 					     struct ieee80211_rx_status *rx_status,
 					     struct ieee80211_radiotap_he *he,
 					     struct ieee80211_radiotap_he_mu *he_mu,
-					     const struct rtw89_phy_sts_ie09 *ie09,
+					     const union rtw89_phy_sts_ie09 *ie09,
 					     const struct rtw89_phy_sts_ie10 *ie10)
 {
+	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def;
 	const u8 *c1 = NULL, *c2 = NULL;
 	int n_center_26tone, n_ru, i;
 	bool doppler, comp;
@@ -3468,8 +3505,17 @@ static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev,
 				 IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN |
 				 IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN);
 
-	sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK);
-	sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK);
+	if (phy->physt_gen >= 2) {
+		sig_a1 = le64_get_bits(ie09->gen2.qw0,
+				       RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK_GEN2);
+		sig_a2 = le64_get_bits(ie09->gen2.qw0,
+				       RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_L0);
+		sig_a2 |= le64_get_bits(ie09->gen2.qw1,
+					RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_H6) << 6;
+	} else {
+		sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK);
+		sig_a2 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK);
+	}
 
 	t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL);
 	he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL);
@@ -3593,7 +3639,7 @@ static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev,
 		.data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN),
 	};
 	struct ieee80211_radiotap_he_mu *he_mu = NULL;
-	const struct rtw89_phy_sts_ie09 *ie09;
+	const union rtw89_phy_sts_ie09 *ie09;
 	const struct rtw89_phy_sts_ie10 *ie10;
 	struct ieee80211_radiotap_he *he;
 	u16 he_format;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index b85d7fa4296a..a6dee97d99f2 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -24,7 +24,7 @@ struct rtw89_h2c_rf_tssi;
 struct rtw89_fw_txpwr_track_cfg;
 struct rtw89_phy_rfk_log_fmt;
 struct rtw89_phy_calc_efuse_gain;
-struct rtw89_phy_sts_ie09;
+union rtw89_phy_sts_ie09;
 struct rtw89_phy_sts_ie10;
 struct rtw89_debugfs;
 struct rtw89_regd_data;
@@ -867,7 +867,7 @@ struct rtw89_rx_phy_ppdu {
 	bool to_self;
 	bool valid;
 	bool hdr_2_en;
-	const struct rtw89_phy_sts_ie09 *ie09; /* SIG-A */
+	const union rtw89_phy_sts_ie09 *ie09; /* SIG-A */
 	const struct rtw89_phy_sts_ie10 *ie10; /* SIG-B */
 };
 
diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h
index 4a567d3fe3f7..4e7109fbe7c9 100644
--- a/drivers/net/wireless/realtek/rtw89/txrx.h
+++ b/drivers/net/wireless/realtek/rtw89/txrx.h
@@ -658,15 +658,23 @@ struct rtw89_phy_sts_ie01_v2 {
 #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_C GENMASK(11, 4)
 #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_D GENMASK(23, 16)
 
-struct rtw89_phy_sts_ie09 {
-	__le64 qw0;
+union rtw89_phy_sts_ie09 {
+	struct {
+		__le64 qw0;
+	} gen0;
+	struct {
+		__le64 qw0;
+		__le64 qw1;
+	} gen2;
 } __packed;
 
 #define RTW89_PHY_STS_IE09_L_SIG_MASK GENMASK(21, 5) /* Legacy/VHT/HE L-SIG */
+#define RTW89_PHY_STS_IE09_L_SIG_MASK_GEN2 GENMASK(31, 15) /* Legacy/VHT/HE L-SIG for GEN2 */
 #define RTW89_PHY_STS_IE09_L_SIG_RATE GENMASK(3, 0)
 #define RTW89_PHY_STS_IE09_L_SIG_RSVD BIT(4)
 #define RTW89_PHY_STS_IE09_L_SIG_LENGTH GENMASK(16, 5)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK GENMASK_ULL(45, 22) /* VHT SIG-A1 */
+#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK_GEN2 GENMASK_ULL(55, 32) /* VHT SIG-A1 for GEN2 */
 #define RTW89_PHY_STS_IE09_VHT_SIG_A1_BW GENMASK(1, 0)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD1 BIT(2)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A1_STBC BIT(3)
@@ -680,6 +688,8 @@ struct rtw89_phy_sts_ie09 {
 #define RTW89_PHY_STS_IE09_VHT_SIG_A1_TXOP_NOPS BIT(22)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD2 BIT(23)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK GENMASK_ULL(55, 46) /* VHT SIG-A2 */
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 56) /* VHT SIG-A2 for GEN2 */
+#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK_GEN2_H8 GENMASK_ULL(1, 0) /* VHT SIG-A2 for GEN2 */
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI BIT(0)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI_DISA BIT(1)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE0 BIT(2)
@@ -688,6 +698,7 @@ struct rtw89_phy_sts_ie09 {
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8)
 #define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9)
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE SU SIG-A1 */
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE SU SIG-A1 for GEN2 */
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_FORMAT BIT(0)
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE BIT(1)
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL BIT(2)
@@ -700,6 +711,8 @@ struct rtw89_phy_sts_ie09 {
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_GILTF GENMASK(22, 21)
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_NSTS GENMASK(25, 23)
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE SU SIG-A2 */
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE SU SIG-A2 for GEN2 */
+#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE SU SIG-A2 for GEN2 */
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP GENMASK(6, 0)
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_CODING BIT(7)
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG BIT(8)
@@ -710,6 +723,7 @@ struct rtw89_phy_sts_ie09 {
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_RSVD2 BIT(14)
 #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER BIT(15)
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE TB SIG-A1 */
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE TB SIG-A1 for GEN2 */
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_FORMAT BIT(0)
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR GENMASK(6, 1)
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1 GENMASK(10, 7)
@@ -719,9 +733,12 @@ struct rtw89_phy_sts_ie09 {
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_RSVD1 BIT(23)
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BW GENMASK(25, 24)
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE TB SIG-A2 */
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE TB SIG-A2 for GEN2 */
+#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE TB SIG-A2 for GEN2 */
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP GENMASK(6, 0)
 #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_RSVD1 GENMASK(15, 7)
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE MU SIG-A1 */
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE MU SIG-A1 for GEN2 */
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL BIT(0)
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS GENMASK(3, 1)
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM BIT(4)
@@ -733,6 +750,8 @@ struct rtw89_phy_sts_ie09 {
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_GI_LTF GENMASK(24, 23)
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER BIT(25)
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE MU SIG-A2 */
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE MU SIG-A2 for GEN2 */
+#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE MU SIG-A2 for GEN2 */
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP GENMASK(6, 0)
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_RSVD BIT(7)
 #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LTF_MID GENMASK(10, 8)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH rtw-next 14/14] wifi: rtw89: check skb headroom before adding radiotap
  2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
                   ` (12 preceding siblings ...)
  2026-05-06 13:09 ` [PATCH rtw-next 13/14] wifi: rtw89: phy: support PHY status IE-09 GEN2 " Ping-Ke Shih
@ 2026-05-06 13:10 ` Ping-Ke Shih
  13 siblings, 0 replies; 15+ messages in thread
From: Ping-Ke Shih @ 2026-05-06 13:10 UTC (permalink / raw)
  To: linux-wireless

The radiotap headroom is allocated only if IEEE80211_CONF_MONITOR is set.
However, it is potentially racing that SKB allocation without radiotap
headroom but adding radiotap from matched PPDU status of another SKB.
Add a check to avoid the case.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index c21737981b61..432d46dfd26a 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3758,6 +3758,13 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev,
 	if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR))
 		return;
 
+	/*
+	 * At transient adding a monitor vif from a station vif, the headroom
+	 * might not include radiotap.
+	 */
+	if (unlikely(skb_headroom(skb) < RTW89_RADIOTAP_ROOM + NET_SKB_PAD))
+		return;
+
 	if (rx_status->encoding == RX_ENC_VHT)
 		rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu);
 	else if (rx_status->encoding == RX_ENC_HE)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-05-06 13:11 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-06 13:09 [PATCH rtw-next 00/14] wifi: rtw89: improve radiotap especially HE SIG-A/SIG-B Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 01/14] wifi: rtw89: add AMPDU to radiotap Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 02/14] wifi: rtw89: add VHT beamformed " Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 03/14] wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 04/14] wifi: rtw89: phy: define PHY status IE length for generations Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 05/14] wifi: rtw89: phy: enable IE-09/IE-10 PHY status report for monitor mode Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 06/14] wifi: rtw89: move HE radiotap to an individual function Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 07/14] wifi: rtw89: fill VHT radiotap Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 08/14] wifi: rtw89: fill HE-SU/HE-TB/HE-MU/HE-EXT_SU radiotap Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 09/14] wifi: rtw89: debug: make implementation of beacon_info entry in order Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 10/14] wifi: rtw89: add debugfs entry of monitor mode options to capture HE-MU packets Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 11/14] wifi: rtw89: phy: check length before parsing PHY status IE Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 12/14] wifi: rtw89: phy: skip trailing 8-byte zeros of PHY status IE for RTL8922D Ping-Ke Shih
2026-05-06 13:09 ` [PATCH rtw-next 13/14] wifi: rtw89: phy: support PHY status IE-09 GEN2 " Ping-Ke Shih
2026-05-06 13:10 ` [PATCH rtw-next 14/14] wifi: rtw89: check skb headroom before adding radiotap Ping-Ke Shih

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox