mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes
@ 2026-11-13 23:52 Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 01/10] wifi: rtw89: avoid NULL vif dereference in rtw89_ops_tx() Enver Ikromov
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

This series fixes a NULL pointer dereference and a negative rate index
bug on the rtw89 TX path for injected frames, makes monitor mode
receive frames that are currently dropped by the hardware RX filter
without disabling hardware Auto-ACK on concurrent active interfaces,
enables raw 802.11 frame injection on standalone and concurrent
MONITOR_FLAG_ACTIVE monitor interfaces, and restores the operating
channel when leaving idle on devices using mac80211 chanctx emulation.

Patches 1-2 are standalone bug fixes with Fixes: and Cc: stable tags:
  1. drop frames without a vif instead of dereferencing NULL in
     rtw89_ops_tx() (reachable when injecting on a passive monitor
     interface while a station interface is up)
  2. treat the radiotap "no rate" index (-1) as signed in
     rtw89_core_tx_update_injection()

Patch 3 retains FIF_CONTROL | FIF_PSPOLL, tracks FIF_OTHER_BSS in
rtwdev->hal.rx_fltr_other_bss, and clears target-address, multicast,
CAM, beacon check, and UID filter drop bits whenever B_AX_SNIFFER_MODE
or rx_fltr_other_bss is active (including across scan/ROC/MAC reinit
restores) while keeping B_AX_SNIFFER_MODE restricted to pure passive
monitor mode so hardware Auto-ACK remains intact for concurrent STA/AP
and MONITOR_FLAG_ACTIVE interfaces.

Patches 4-9 enable raw frame injection on standalone and active monitor
interfaces:
  4. RCU protection and sole-monitor tracking for pure_monitor_mode_vif
     with optional deferred RCU synchronization
  5. RTW89_SELF_ROLE_AP for monitor interfaces so the JoinInfo H2C
     initializes the MAC ID's CMAC control entry without arming client
     TBTT tracking or AP beacon transmission
  6. resolve standalone NULL-vif TX via pure_monitor_mode_vif and direct
     MONITOR_FLAG_ACTIVE vif TX via rtwvif->mon_tx_ready with coalesced
     synchronize_rcu(), track rtwdev->monitor_vifs for radiotap/PHYSTS
     enablement, parse frequency IEs before pushing VHT/HE/EHT radiotap
     headers during concurrent scans, and validate linear 802.11 header
     lengths via skb_headlen()
  7. route injected control frames through the management queue and
     guard hdr->seq_ctrl reads on short/control frames
  8. skip host-side IP/UDP traffic stats, WoWLAN AKM parsing, BT-coex,
     HE HTC, and A-MPDU/TXQ aggregation hooks on injected frames
  9. honor IEEE80211_TX_CTL_ASSIGN_SEQ, IEEE80211_TX_RC_USE_RTS_CTS,
     IEEE80211_RADIOTAP_DATA_RETRIES (hw->max_rate_tries = 63), and
     IEEE80211_TX_CTL_NO_ACK across AX/BE TX descriptors and PCIe/RPQ
     TX completion status

Patch 10 restores hw->conf.chandef when leaving idle or toggling
monitor mode on devices without assign_vif_chanctx while RTW89_PHY_0 is
inactive after rtw89_ops_stop() / rtw89_enter_ips().

Verification:
- Firmware behavior in patches 3, 5, and 10 was verified against
  RTL8852AE firmware 0.13.36.2 (JoinInfo H2C role/CCTL handling,
  scan-offload RX filter pause/restore, and absence of BEACON_FILTER
  forcing no_chanctx even with support_chanctx_num == 1).
- Each patch builds cleanly in isolation on rtw-next (Linux 7.4-rc1)
  with W=1, C=1 (sparse), and CONFIG_PROVE_LOCKING=y, and passes
  checkpatch.pl --strict without warnings.
- Runtime hardware validation was conducted on RTL8852AE (10ec:8852,
  firmware rtw89/rtw8852a_fw-1.bin 0.13.36.2, Linux 7.1.5): promiscuous
  RX across down/up restart (ftrace confirmed channel/RFK programming),
  NULL-vif drop on passive monitor while a station interface is up vs
  concurrent MONITOR_FLAG_ACTIVE raw TX + Auto-ACK preservation,
  10B ACK / 16B RTS / SN=1337 Data & AssocReq raw TX, NO_ACK vs
  DATA_RETRIES tx_retry_lmt telemetry verification, and 25 concurrent
  del/add RCU stress cycles (0 dmesg alerts).
  RTL8852B/C, RTL8851B, RTL8922A (BE), USB devices, and DBCC setups were
  not runtime-tested.

Changes in v5:
- Split the NULL vif fix and signed rate index fix out as standalone
  patches 1-2 with Fixes: and Cc: stable tags.
- Split monitor injection into focused patches 5-9 and support
  concurrent MONITOR_FLAG_ACTIVE interfaces alongside standalone
  passive monitors while coalescing synchronize_rcu() on teardown.
- Route only injected control frames through the management path so
  mac80211-generated BlockAckReq frames keep the normal data path.
- Separate FIF_OTHER_BSS drop-mask clearing from B_AX_SNIFFER_MODE so
  arbitrary-BSS RX works on MONITOR_FLAG_ACTIVE without suppressing
  hardware Auto-ACK on active interfaces.
- Advertise hw->max_rate_tries = 63, honor rates[0].count, and avoid
  false tx_retry_lmt increments on single-shot NO_ACK completions.
- Gate channel restoration on leaving idle / monitor changes with
  !rtwdev->ops->assign_vif_chanctx so non-chanctx devices (such as
  RTL8852AE) activate RTW89_PHY_0 for both standalone and active
  monitor interfaces.

Changes in v4:
- Use full legal name in From/Signed-off-by.
- Retain FIF_CONTROL and FIF_PSPOLL and clear B_AX_A_MC and
  B_AX_A_MC_LIST_CAM_MATCH in monitor mode.
- Split RCU conversion of pure_monitor_mode_vif into its own patch.
- Handle injected control frames, negative rate indices, NO_ACK, and
  sequence number preservation.
- Move monitor restart channel restoration into rtw89_ops_config().

Changes in v3:
- Split into a patch series.
- Dropped direct register writes in favor of driver abstractions.
- Added RCU synchronization around pure_monitor_mode_vif.

v4: not sent to the list
v3: https://lore.kernel.org/linux-wireless/20261104203051.564-1-owner@ph4ntom-rev.uz/
v2: https://lore.kernel.org/linux-wireless/179371849554.20972.16232816164394690660@ph4ntom-rev.uz/
v1 (RFC): https://lore.kernel.org/linux-wireless/1a196d6dfbc.35537d221913104450.3720060455831933789@zoho.com/


Enver Ikromov (10):
  wifi: rtw89: avoid NULL vif dereference in rtw89_ops_tx()
  wifi: rtw89: handle negative rate index for injected frames
  wifi: rtw89: fix RX filter configuration for monitor mode
  wifi: rtw89: protect pure_monitor_mode_vif with RCU
  wifi: rtw89: use AP self role for monitor interfaces
  wifi: rtw89: transmit frames from monitor interfaces
  wifi: rtw89: send injected control frames through the management path
  wifi: rtw89: skip host-side TX hooks for injected frames
  wifi: rtw89: honor ASSIGN_SEQ, retries, and NO_ACK for injected frames
  wifi: rtw89: restore channel when leaving idle on non-chanctx devices

 drivers/net/wireless/realtek/rtw89/core.c     | 144 ++++++++++++++----
 drivers/net/wireless/realtek/rtw89/core.h     |  17 ++-
 drivers/net/wireless/realtek/rtw89/mac.c      |   3 +-
 drivers/net/wireless/realtek/rtw89/mac.h      |  18 +++
 drivers/net/wireless/realtek/rtw89/mac80211.c | 131 +++++++++++++++-
 drivers/net/wireless/realtek/rtw89/mac_be.c   |   3 +-
 drivers/net/wireless/realtek/rtw89/pci.c      |   3 +-
 drivers/net/wireless/realtek/rtw89/phy.c      |   2 +-
 8 files changed, 278 insertions(+), 43 deletions(-)


base-commit: 61a5eb87b2d9b4d5e353780b781cf691f39ba9e1
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 01/10] wifi: rtw89: avoid NULL vif dereference in rtw89_ops_tx()
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 02/10] wifi: rtw89: handle negative rate index for injected frames Enver Ikromov
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

rtw89 sets IEEE80211_HW_WANT_MONITOR_VIF but not
IEEE80211_HW_QUEUE_CONTROL. For frames transmitted on a monitor
interface, ieee80211_tx_frags() therefore passes
info->control.vif == NULL to the driver whenever local->monitor_sdata
does not exist, e.g. when a raw frame is sent on a monitor interface
while a station interface is also up and the frame's addr2 does not
match any running interface.

rtw89_ops_tx() unconditionally converts the vif with vif_to_rtwvif()
and dereferences rtwvif->offchan, so such a frame makes the kernel
access memory at a small offset from NULL.

Drop frames without a vif, as there is no MAC ID or port they could
be transmitted on.

Fixes: a0e97ae3f332 ("wifi: rtw89: add ieee80211::remain_on_channel ops")
Cc: stable@vger.kernel.org
Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/mac80211.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 9134cdc..5a12639 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -23,11 +23,18 @@ static void rtw89_ops_tx(struct ieee80211_hw *hw,
 	struct rtw89_dev *rtwdev = hw->priv;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_vif *vif = info->control.vif;
-	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
 	struct ieee80211_sta *sta = control->sta;
 	u32 flags = IEEE80211_SKB_CB(skb)->flags;
+	struct rtw89_vif *rtwvif;
 	int ret, qsel;
 
+	if (unlikely(!vif)) {
+		ieee80211_free_txskb(hw, skb);
+		return;
+	}
+
+	rtwvif = vif_to_rtwvif(vif);
+
 	if (rtwvif->offchan && !(flags & IEEE80211_TX_CTL_TX_OFFCHAN) && sta) {
 		struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
 
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 02/10] wifi: rtw89: handle negative rate index for injected frames
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 01/10] wifi: rtw89: avoid NULL vif dereference in rtw89_ops_tx() Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 03/10] wifi: rtw89: fix RX filter configuration for monitor mode Enver Ikromov
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

When the radiotap header of an injected frame carries no rate,
info->control.rates[0].idx is -1. rtw89_core_tx_update_injection()
stores the index in a u8, so the value becomes 0xff and is added to
the CCK1/OFDM6 base, programming an invalid hardware rate into the TX
descriptor.

Use a signed index. If it is negative, keep the rate already selected
by rtw89_core_tx_update_mgmt_info() for management frames, and fall
back to data_retry_lowest_rate (or OFDM6 when
IEEE80211_TX_CTL_NO_CCK_RATE is set) for data frames.

Fixes: e96abe4fd7cd ("wifi: rtw89: consider data rate/bandwidth/GI for injected packets")
Cc: stable@vger.kernel.org
Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/core.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index b7df97e..527593e 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -1239,12 +1239,25 @@ static void rtw89_core_tx_update_injection(struct rtw89_dev *rtwdev,
 	enum mac80211_rate_control_flags flags = info->control.rates[0].flags;
 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
 	const struct rtw89_chan *chan;
-	u8 idx = info->control.rates[0].idx;
+	s8 idx = info->control.rates[0].idx;
 	u8 nss, mcs;
 
 	desc_info->use_rate = true;
 	desc_info->dis_data_fb = true;
 
+	/* No rate was given in the radiotap header. Management frames
+	 * already carry the rate from rtw89_core_tx_update_mgmt_info(), so
+	 * only data frames need a fallback.
+	 */
+	if (idx < 0) {
+		if (tx_req->tx_type == RTW89_CORE_TX_TYPE_DATA)
+			desc_info->data_rate =
+				info->flags & IEEE80211_TX_CTL_NO_CCK_RATE ?
+				RTW89_HW_RATE_OFDM6 :
+				desc_info->data_retry_lowest_rate;
+		return;
+	}
+
 	if (flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
 		desc_info->data_bw = 3;
 	else if (flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 03/10] wifi: rtw89: fix RX filter configuration for monitor mode
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 01/10] wifi: rtw89: avoid NULL vif dereference in rtw89_ops_tx() Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 02/10] wifi: rtw89: handle negative rate index for injected frames Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 04/10] wifi: rtw89: protect pure_monitor_mode_vif with RCU Enver Ikromov
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

When entering monitor mode or enabling FIF_OTHER_BSS,
rtw89_ops_configure_filter() updates B_AX_SNIFFER_MODE or clears
B_AX_A_A1_MATCH in hal.rx_fltr, but leaves hardware target-address,
multicast, address CAM, beacon check, and UID drop masks active unless
specific FIF_* flags happen to toggle:

- B_AX_A_UC_CAM_MATCH and B_AX_A_BC_CAM_MATCH are only cleared when
  FIF_PROBE_REQ changes, dropping unicast and broadcast frames from
  arbitrary BSSs without a CAM entry in hardware.
- B_AX_A_MC is only cleared when FIF_ALLMULTI is set, and
  B_AX_A_MC_LIST_CAM_MATCH is never cleared, dropping multicast frames
  when a monitor socket is opened without IFF_PROMISC.
- B_AX_UID_FILTER_MASK is initialized in DEFAULT_AX_RX_FLTR and
  DEFAULT_BE_RX_FLTR via mac->default_rx_fltr and is never cleared by
  any FIF_* flag.
- B_AX_A_A1_MATCH, B_AX_A_BC, and B_AX_A_BCN_CHK_EN must stay cleared
  whenever B_AX_SNIFFER_MODE or FIF_OTHER_BSS is active, including
  after rtw89_hw_scan_complete_cb(), rtw89_roc_end(), or MAC reinit
  writes rtwdev->hal.rx_fltr back to R_AX_RX_FLTR_OPT /
  R_BE_RX_FLTR_OPT.
- FIF_CONTROL and FIF_PSPOLL are masked out of *new_flags even though
  rx_fltr_init_ax() and rx_fltr_init_be() already forward control
  frames to the host via R_AX_CTRL_FLTR / R_BE_CTRL_FLTR.

Retain FIF_CONTROL and FIF_PSPOLL in *new_flags, record FIF_OTHER_BSS
in hal.rx_fltr_other_bss so active monitor interfaces with
MONITOR_FLAG_OTHER_BSS (and concurrent passive monitors) receive
arbitrary-BSS frames without setting B_AX_SNIFFER_MODE, and add
rtw89_mac_refine_rx_fltr() to clear the drop masks without mutating
baseline rtwdev->hal.rx_fltr across mode transitions. The bit positions
in R_AX_RX_FLTR_OPT and R_BE_RX_FLTR_OPT are identical.

Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/core.h     |  1 +
 drivers/net/wireless/realtek/rtw89/mac.c      |  3 ++-
 drivers/net/wireless/realtek/rtw89/mac.h      | 16 ++++++++++++++++
 drivers/net/wireless/realtek/rtw89/mac80211.c |  5 ++++-
 drivers/net/wireless/realtek/rtw89/mac_be.c   |  3 ++-
 5 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 7f84ee5..7be8810 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -6499,6 +6499,7 @@ struct rtw89_hal {
 	u8 cv;
 	u8 cid; /* enum rtw89_core_chip_cid */
 	u8 acv;
+	bool rx_fltr_other_bss;
 	u16 aid; /* enum rtw89_core_chip_aid */
 	u32 antenna_tx;
 	u32 antenna_rx;
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 41796bf..06fb263 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -2681,6 +2681,7 @@ void rtw89_mac_set_rx_fltr(struct rtw89_dev *rtwdev, u8 mac_idx, u32 rx_fltr)
 	u32 reg;
 	u32 val;
 
+	rx_fltr = rtw89_mac_refine_rx_fltr(rtwdev, rx_fltr);
 	reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, mac_idx);
 
 	val = rtw89_read32(rtwdev, reg);
@@ -2706,7 +2707,7 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
 		if (ret)
 			return ret;
 	}
-	mac_ftlr = rtwdev->hal.rx_fltr;
+	mac_ftlr = rtw89_mac_refine_rx_fltr(rtwdev, rtwdev->hal.rx_fltr);
 	plcp_ftlr = B_AX_CCK_CRC_CHK | B_AX_CCK_SIG_CHK |
 		    B_AX_LSIG_PARITY_CHK_EN | B_AX_SIGA_CRC_CHK |
 		    B_AX_VHT_SU_SIGB_CRC_CHK | B_AX_VHT_MU_SIGB_CRC_CHK |
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index db42e33..b4a7cb7 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -1494,6 +1494,22 @@ void rtw89_mac_set_edcca_mode_bands(struct rtw89_dev *rtwdev, bool normal)
 	rtw89_mac_set_edcca_mode(rtwdev, RTW89_MAC_1, normal);
 }
 
+static inline u32 rtw89_mac_refine_rx_fltr(struct rtw89_dev *rtwdev,
+					   u32 rx_fltr)
+{
+	if ((rx_fltr & B_AX_SNIFFER_MODE) || rtwdev->hal.rx_fltr_other_bss)
+		rx_fltr &= ~(B_AX_A_A1_MATCH |
+			     B_AX_A_BC |
+			     B_AX_A_MC |
+			     B_AX_A_UC_CAM_MATCH |
+			     B_AX_A_BC_CAM_MATCH |
+			     B_AX_A_MC_LIST_CAM_MATCH |
+			     B_AX_A_BCN_CHK_EN |
+			     B_AX_UID_FILTER_MASK);
+
+	return rx_fltr;
+}
+
 void rtw89_mac_set_rx_fltr(struct rtw89_dev *rtwdev, u8 mac_idx, u32 rx_fltr);
 void rtw89_mac_update_rts_threshold(struct rtw89_dev *rtwdev);
 void rtw89_mac_flush_txq(struct rtw89_dev *rtwdev, u32 queues, bool drop);
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 5a12639..2a3adf0 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -335,7 +335,10 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,
 	rtw89_leave_ps_mode(rtwdev);
 
 	*new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL |
-		      FIF_BCN_PRBRESP_PROMISC | FIF_PROBE_REQ;
+		      FIF_BCN_PRBRESP_PROMISC | FIF_PROBE_REQ |
+		      FIF_CONTROL | FIF_PSPOLL;
+
+	rtwdev->hal.rx_fltr_other_bss = !!(*new_flags & FIF_OTHER_BSS);
 
 	if (changed_flags & FIF_ALLMULTI) {
 		if (*new_flags & FIF_ALLMULTI)
diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c
index fa35caf..7147679 100644
--- a/drivers/net/wireless/realtek/rtw89/mac_be.c
+++ b/drivers/net/wireless/realtek/rtw89/mac_be.c
@@ -1328,7 +1328,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_DATA, RTW89_FWD_TO_HOST, mac_idx);
 
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RX_FLTR_OPT, mac_idx);
-	rtw89_write32(rtwdev, reg, rtwdev->hal.rx_fltr);
+	rtw89_write32(rtwdev, reg,
+		      rtw89_mac_refine_rx_fltr(rtwdev, rtwdev->hal.rx_fltr));
 
 	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_PLCP_HDR_FLTR, mac_idx);
 	val = B_BE_HE_SIGB_CRC_CHK | B_BE_VHT_MU_SIGB_CRC_CHK |
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 04/10] wifi: rtw89: protect pure_monitor_mode_vif with RCU
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
                   ` (2 preceding siblings ...)
  2026-11-13 23:52 ` [PATCH rtw-next v5 03/10] wifi: rtw89: fix RX filter configuration for monitor mode Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 05/10] wifi: rtw89: use AP self role for monitor interfaces Enver Ikromov
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

A following patch reads rtwdev->pure_monitor_mode_vif from
rtw89_ops_tx(), which runs without the wiphy lock, while interface
add/remove updates the pointer under the wiphy lock.

Annotate pure_monitor_mode_vif with __rcu, convert existing readers to
rcu_access_pointer() and rcu_dereference_protected(), and recompute
the pointer via rtw89_recalc_pure_monitor_vif() and
rtw89_set_pure_monitor_vif().

Whereas mac80211 only creates local->monitor_sdata while no other
interface is open, a monitor interface opened with MONITOR_FLAG_ACTIVE
is passed directly to drv_add_interface() even alongside an active
station or AP interface. Walking rtwdev->rtwvifs_list ensures
pure_monitor_mode_vif is published if and only if a single monitor
interface is the sole active driver vif, and unpublishes it with
synchronize_rcu() before its link, port, and MAC ID are released.

Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/core.c     |  9 +++-
 drivers/net/wireless/realtek/rtw89/core.h     |  2 +-
 drivers/net/wireless/realtek/rtw89/mac80211.c | 49 +++++++++++++++++--
 3 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 527593e..3447f0e 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -530,7 +530,7 @@ void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev,
 			    struct rtw89_vif_link *rtwvif_link)
 {
 	const struct rtw89_chip_info *chip = rtwdev->chip;
-	bool mon = !!rtwdev->pure_monitor_mode_vif;
+	bool mon = !!rcu_access_pointer(rtwdev->pure_monitor_mode_vif);
 	bool prehdl_link = false;
 
 	if (chip->chip_gen != RTW89_CHIP_AX &&
@@ -560,8 +560,13 @@ void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev,
 static void rtw89_chip_rfk_channel_for_pure_mon_vif(struct rtw89_dev *rtwdev,
 						    enum rtw89_phy_idx phy_idx)
 {
-	struct rtw89_vif *rtwvif = rtwdev->pure_monitor_mode_vif;
+	struct wiphy *wiphy = rtwdev->hw->wiphy;
 	struct rtw89_vif_link *rtwvif_link;
+	struct rtw89_vif *rtwvif;
+
+	lockdep_assert_wiphy(wiphy);
+	rtwvif = rcu_dereference_protected(rtwdev->pure_monitor_mode_vif,
+					   lockdep_is_held(&wiphy->mtx));
 
 	if (!rtwvif)
 		return;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 7be8810..78f6ef0 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -7832,7 +7832,7 @@ struct rtw89_dev {
 	int napi_budget_countdown;
 
 	struct rtw89_debugfs *debugfs;
-	struct rtw89_vif *pure_monitor_mode_vif;
+	struct rtw89_vif __rcu *pure_monitor_mode_vif;
 	struct rtw89_led led;
 
 	/* HCI related data, keep last */
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 2a3adf0..b38e04f 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -183,6 +183,47 @@ static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev,
 	rtw89_mac_remove_vif(rtwdev, rtwvif_link);
 }
 
+static bool rtw89_set_pure_monitor_vif(struct rtw89_dev *rtwdev,
+				       struct rtw89_vif *rtwvif, bool sync)
+{
+	struct wiphy *wiphy = rtwdev->hw->wiphy;
+	struct rtw89_vif *old;
+
+	lockdep_assert_wiphy(wiphy);
+
+	old = rcu_dereference_protected(rtwdev->pure_monitor_mode_vif,
+					lockdep_is_held(&wiphy->mtx));
+	if (old == rtwvif)
+		return false;
+
+	rcu_assign_pointer(rtwdev->pure_monitor_mode_vif, rtwvif);
+
+	/* wait for lockless readers still using the previous vif */
+	if (old && sync)
+		synchronize_rcu();
+
+	return !!old;
+}
+
+static bool rtw89_recalc_pure_monitor_vif(struct rtw89_dev *rtwdev,
+					  struct rtw89_vif *exclude, bool sync)
+{
+	struct rtw89_vif *sole = NULL;
+	struct rtw89_vif *iter;
+
+	rtw89_for_each_rtwvif(rtwdev, iter) {
+		if (iter == exclude)
+			continue;
+		if (sole || rtwvif_to_vif(iter)->type != NL80211_IFTYPE_MONITOR) {
+			sole = NULL;
+			break;
+		}
+		sole = iter;
+	}
+
+	return rtw89_set_pure_monitor_vif(rtwdev, sole, sync);
+}
+
 static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
 				   struct ieee80211_vif *vif)
 {
@@ -241,8 +282,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
 	if (ret)
 		goto unset_link;
 
-	rtwdev->pure_monitor_mode_vif = vif->type == NL80211_IFTYPE_MONITOR ?
-					rtwvif : NULL;
+	rtw89_recalc_pure_monitor_vif(rtwdev, NULL, true);
 	rtw89_recalc_lps(rtwdev);
 	return 0;
 
@@ -271,6 +311,9 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
 	rtw89_debug(rtwdev, RTW89_DBG_STATE, "remove vif %pM type %d p2p %d\n",
 		    vif->addr, vif->type, vif->p2p);
 
+	/* unpublish before tearing down the link, port and MAC ID */
+	rtw89_recalc_pure_monitor_vif(rtwdev, rtwvif, true);
+
 	wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work);
 
 	rtwvif_link = rtwvif->links[RTW89_VIF_IDLE_LINK_ID];
@@ -290,8 +333,6 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
 	rtw89_core_release_bit_map(rtwdev->hw_port, port);
 	rtw89_release_mac_id(rtwdev, macid);
 
-	rtwdev->pure_monitor_mode_vif = NULL;
-
 	rtw89_recalc_lps(rtwdev);
 	rtw89_enter_ips_by_hwflags(rtwdev);
 }
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 05/10] wifi: rtw89: use AP self role for monitor interfaces
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
                   ` (3 preceding siblings ...)
  2026-11-13 23:52 ` [PATCH rtw-next v5 04/10] wifi: rtw89: protect pure_monitor_mode_vif with RCU Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 06/10] wifi: rtw89: transmit frames from " Enver Ikromov
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

rtw89_vif_type_mapping() leaves net_type and self_role zero for
NL80211_IFTYPE_MONITOR, i.e. RTW89_NET_TYPE_NO_LINK and
RTW89_SELF_ROLE_CLIENT. When rtw89_mac_add_vif() initializes the
interface, rtw89_mac_vif_init() first creates the role via H2C
ROLE_MAINTAIN and then sends JoinInfo with disassoc set (op == 1) and
this client role.

In firmware (verified on RTL8852AE firmware 0.13.36.2), the JoinInfo
H2C handler treats (self_role != RTW89_SELF_ROLE_AP && op == 1) as a
station disconnect request (HAL8852A_Role_Disconnect), which clears the
newly created role's active bit in the firmware role table and skips
initializing its CMAC control table entry (CCTL_ResetByMacid). Frames
queued on that MAC ID are then silently dropped instead of transmitted.

With self_role set to RTW89_SELF_ROLE_AP and net_type kept at
RTW89_NET_TYPE_NO_LINK, the JoinInfo H2C handler initializes the
MAC ID's CMAC control entry and connects the role without enabling
client TBTT tracking (gated on RTW89_SELF_ROLE_CLIENT) or AP beacon
transmission (gated on RTW89_NET_TYPE_AP_MODE). Set both fields
explicitly for monitor interfaces.

Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/core.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 3447f0e..9cf2b03 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -5756,6 +5756,8 @@ void rtw89_vif_type_mapping(struct rtw89_vif_link *rtwvif_link, bool assoc)
 		rtwvif_link->self_role = RTW89_SELF_ROLE_CLIENT;
 		break;
 	case NL80211_IFTYPE_MONITOR:
+		rtwvif_link->net_type = RTW89_NET_TYPE_NO_LINK;
+		rtwvif_link->self_role = RTW89_SELF_ROLE_AP;
 		break;
 	default:
 		WARN_ON(1);
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 06/10] wifi: rtw89: transmit frames from monitor interfaces
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
                   ` (4 preceding siblings ...)
  2026-11-13 23:52 ` [PATCH rtw-next v5 05/10] wifi: rtw89: use AP self role for monitor interfaces Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 07/10] wifi: rtw89: send injected control frames through the management path Enver Ikromov
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

Frames injected on a monitor interface reach the driver either through
rtw89_ops_tx() (with info->control.vif == NULL for a passive monitor,
info->control.vif == &sdata->vif for MONITOR_FLAG_ACTIVE, or a running
interface matched by addr2) or, for injected data frames whose addr2
matches an active interface, via wake_tx_queue and
rtw89_core_txq_push().

Passing local->monitor_sdata directly is racy because
ieee80211_del_virtual_monitor() calls drv_remove_interface() before
clearing local->monitor_sdata, so mac80211 may still pass a tearing-down
monitor vif to rtw89_ops_tx() after the driver has released its MAC ID
and port.

In rtw89_ops_tx(), require an active PHY entity and resolve passive
monitor TX through rtwdev->pure_monitor_mode_vif under rcu_read_lock(),
while permitting concurrent MONITOR_FLAG_ACTIVE interfaces to transmit
through their own driver vif while rtwvif->mon_tx_ready is set (clearing
mon_tx_ready and coalescing synchronize_rcu() before teardown). Track
active driver monitor vifs in rtwdev->monitor_vifs via
rtw89_is_monitoring() so concurrent MONITOR_FLAG_ACTIVE interfaces
reserve radiotap headroom and enable PHY status parsing (parsing scan
channel IEs before pushing radiotap headers), and verify skb_headlen()
in rtw89_core_tx_write_link() before reading skb->data.

Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/core.c     | 22 ++++--
 drivers/net/wireless/realtek/rtw89/core.h     | 10 ++-
 drivers/net/wireless/realtek/rtw89/mac80211.c | 71 ++++++++++++++++---
 drivers/net/wireless/realtek/rtw89/phy.c      |  2 +-
 4 files changed, 88 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 9cf2b03..54605f7 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -1450,6 +1450,14 @@ static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev,
 	struct rtw89_core_tx_request tx_req = {};
 	int ret;
 
+	if (unlikely(IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_INJECTED)) {
+		struct ieee80211_hdr *hdr = (void *)skb->data;
+
+		if (skb_headlen(skb) < sizeof(struct ieee80211_cts) ||
+		    skb_headlen(skb) < ieee80211_hdrlen(hdr->frame_control))
+			return -EINVAL;
+	}
+
 	tx_req.skb = skb;
 	tx_req.vif = vif;
 	tx_req.sta = sta;
@@ -2258,7 +2266,7 @@ static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev,
 	 * 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;
+		 rtw89_is_monitoring(rtwdev);
 	if (!accept)
 		return 0;
 
@@ -2341,8 +2349,7 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,
 	u16 ie_len;
 
 	/* for normal mode, mark invalid reports and bypass them */
-	accept = phy_ppdu->ie >= RTW89_CCK_PKT ||
-		 rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR;
+	accept = phy_ppdu->ie >= RTW89_CCK_PKT || rtw89_is_monitoring(rtwdev);
 	if (!accept)
 		return -EINVAL;
 
@@ -3231,7 +3238,7 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev,
 					 struct ieee80211_rx_status *rx_status,
 					 struct rtw89_rx_phy_ppdu *phy_ppdu)
 {
-	if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR))
+	if (!rtw89_is_monitoring(rtwdev))
 		return;
 
 	if (!phy_ppdu)
@@ -3803,7 +3810,7 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev,
 				       struct ieee80211_rx_status *rx_status,
 				       struct rtw89_rx_phy_ppdu *phy_ppdu)
 {
-	if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR))
+	if (!rtw89_is_monitoring(rtwdev))
 		return;
 
 	/*
@@ -4029,9 +4036,9 @@ 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);
 	rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu);
-	rtw89_core_update_radiotap(rtwdev, desc_info, skb, rx_status, phy_ppdu);
 	rtw89_core_validate_rx_signal(rx_status);
 	rtw89_core_update_rx_freq_from_ie(rtwdev, skb, rx_status);
+	rtw89_core_update_radiotap(rtwdev, desc_info, skb, rx_status, phy_ppdu);
 	rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu);
 
 	/* In low power mode, it does RX in thread context. */
@@ -7577,7 +7584,8 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
 		hw->wiphy->num_iftype_ext_capab = ARRAY_SIZE(rtw89_iftypes_ext_capa);
 	}
 
-	hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+	hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR |
+			       NL80211_FEATURE_ACTIVE_MONITOR;
 
 	hw->wiphy->max_scan_ssids = RTW89_SCANOFLD_MAX_SSID;
 	hw->wiphy->max_scan_ie_len = RTW89_SCANOFLD_MAX_IE_LEN;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 78f6ef0..7b03154 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -7833,6 +7833,7 @@ struct rtw89_dev {
 
 	struct rtw89_debugfs *debugfs;
 	struct rtw89_vif __rcu *pure_monitor_mode_vif;
+	unsigned int monitor_vifs;
 	struct rtw89_led led;
 
 	/* HCI related data, keep last */
@@ -7870,6 +7871,7 @@ struct rtw89_vif {
 	struct rtw89_roc roc;
 	bool offchan;
 	bool burst_active;
+	bool mon_tx_ready;
 
 	enum rtw89_mlo_mode mlo_mode;
 	struct rtw89_vif_ml_trans ml_trans;
@@ -9323,12 +9325,18 @@ static inline struct rtw89_fw_suit *rtw89_fw_suit_get(struct rtw89_dev *rtwdev,
 	return &fw_info->normal;
 }
 
+static inline bool rtw89_is_monitoring(struct rtw89_dev *rtwdev)
+{
+	return (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) ||
+	       READ_ONCE(rtwdev->monitor_vifs);
+}
+
 static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev,
 						     unsigned int length)
 {
 	struct sk_buff *skb;
 
-	if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) {
+	if (rtw89_is_monitoring(rtwdev)) {
 		skb = dev_alloc_skb(length + RTW89_RADIOTAP_ROOM);
 		if (!skb)
 			return NULL;
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index b38e04f..c8a6003 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -16,6 +16,12 @@
 #include "util.h"
 #include "wow.h"
 
+static bool rtw89_has_active_entity(struct rtw89_dev *rtwdev)
+{
+	return rtw89_get_entity_state(rtwdev, RTW89_PHY_0) ||
+	       (rtwdev->dbcc_en && rtw89_get_entity_state(rtwdev, RTW89_PHY_1));
+}
+
 static void rtw89_ops_tx(struct ieee80211_hw *hw,
 			 struct ieee80211_tx_control *control,
 			 struct sk_buff *skb)
@@ -28,28 +34,56 @@ static void rtw89_ops_tx(struct ieee80211_hw *hw,
 	struct rtw89_vif *rtwvif;
 	int ret, qsel;
 
-	if (unlikely(!vif)) {
-		ieee80211_free_txskb(hw, skb);
-		return;
-	}
+	rcu_read_lock();
 
-	rtwvif = vif_to_rtwvif(vif);
+	if (unlikely(!vif || vif->type == NL80211_IFTYPE_MONITOR)) {
+		if (!rtw89_has_active_entity(rtwdev)) {
+			ieee80211_free_txskb(hw, skb);
+			goto out;
+		}
+
+		/* Passive monitors pass vif == NULL and are only permitted to
+		 * transmit when a standalone monitor vif is the sole active
+		 * interface; active monitors (MONITOR_FLAG_ACTIVE) pass their
+		 * own driver vif and may transmit concurrently while
+		 * mon_tx_ready is set. Both paths unpublish and wait for an
+		 * RCU grace period before releasing link, port, and MAC ID.
+		 */
+		rtwvif = rcu_dereference(rtwdev->pure_monitor_mode_vif);
+		if (!rtwvif && vif) {
+			rtwvif = vif_to_rtwvif(vif);
+			if (!READ_ONCE(rtwvif->mon_tx_ready))
+				rtwvif = NULL;
+		}
+		if (!rtwvif) {
+			ieee80211_free_txskb(hw, skb);
+			goto out;
+		}
+
+		vif = rtwvif_to_vif(rtwvif);
+		info->control.vif = vif;
+	} else {
+		rtwvif = vif_to_rtwvif(vif);
+	}
 
 	if (rtwvif->offchan && !(flags & IEEE80211_TX_CTL_TX_OFFCHAN) && sta) {
 		struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
 
 		rtw89_debug(rtwdev, RTW89_DBG_TXRX, "ops_tx during offchan\n");
 		skb_queue_tail(&rtwsta->roc_queue, skb);
-		return;
+		goto out;
 	}
 
 	ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel);
 	if (ret) {
 		rtw89_err(rtwdev, "failed to transmit skb: %d\n", ret);
 		ieee80211_free_txskb(hw, skb);
-		return;
+		goto out;
 	}
 	rtw89_core_tx_kick_off(rtwdev, qsel);
+
+out:
+	rcu_read_unlock();
 }
 
 static void rtw89_ops_wake_tx_queue(struct ieee80211_hw *hw,
@@ -282,6 +316,13 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
 	if (ret)
 		goto unset_link;
 
+	if (vif->type == NL80211_IFTYPE_MONITOR) {
+		WRITE_ONCE(rtwvif->mon_tx_ready, true);
+		WRITE_ONCE(rtwdev->monitor_vifs, rtwdev->monitor_vifs + 1);
+		if (rtwdev->monitor_vifs == 1 &&
+		    test_bit(RTW89_FLAG_POWERON, rtwdev->flags))
+			rtw89_physts_parsing_init(rtwdev);
+	}
 	rtw89_recalc_pure_monitor_vif(rtwdev, NULL, true);
 	rtw89_recalc_lps(rtwdev);
 	return 0;
@@ -305,6 +346,7 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
 	u8 macid = rtw89_vif_get_main_macid(rtwvif);
 	u8 port = rtw89_vif_get_main_port(rtwvif);
 	struct rtw89_vif_link *rtwvif_link;
+	bool need_sync;
 
 	lockdep_assert_wiphy(hw->wiphy);
 
@@ -312,7 +354,13 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
 		    vif->addr, vif->type, vif->p2p);
 
 	/* unpublish before tearing down the link, port and MAC ID */
-	rtw89_recalc_pure_monitor_vif(rtwdev, rtwvif, true);
+	need_sync = rtw89_recalc_pure_monitor_vif(rtwdev, rtwvif, false);
+	if (vif->type == NL80211_IFTYPE_MONITOR && rtwvif->mon_tx_ready) {
+		WRITE_ONCE(rtwvif->mon_tx_ready, false);
+		need_sync = true;
+	}
+	if (need_sync)
+		synchronize_rcu();
 
 	wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work);
 
@@ -333,6 +381,13 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
 	rtw89_core_release_bit_map(rtwdev->hw_port, port);
 	rtw89_release_mac_id(rtwdev, macid);
 
+	if (vif->type == NL80211_IFTYPE_MONITOR && rtwdev->monitor_vifs) {
+		WRITE_ONCE(rtwdev->monitor_vifs, rtwdev->monitor_vifs - 1);
+		if (!rtwdev->monitor_vifs &&
+		    test_bit(RTW89_FLAG_POWERON, rtwdev->flags))
+			rtw89_physts_parsing_init(rtwdev);
+	}
+
 	rtw89_recalc_lps(rtwdev);
 	rtw89_enter_ips_by_hwflags(rtwdev);
 }
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index 795920b..c04fbcf 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -7183,7 +7183,7 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev,
 	u32 val;
 	u8 i;
 
-	if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) {
+	if (rtw89_is_monitoring(rtwdev)) {
 		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);
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 07/10] wifi: rtw89: send injected control frames through the management path
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
                   ` (5 preceding siblings ...)
  2026-11-13 23:52 ` [PATCH rtw-next v5 06/10] wifi: rtw89: transmit frames from " Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 08/10] wifi: rtw89: skip host-side TX hooks for injected frames Enver Ikromov
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

rtw89_core_get_tx_type() classifies every frame that is neither a
management nor a null-data frame as data. Injected control frames
such as a 10-byte ACK or 16-byte RTS therefore go through
rtw89_core_tx_update_data_info(), which derives a TID and data queue
from skb->priority and enables AMPDU/QoS related descriptor fields
that make no sense for them.

Classify injected non-data frames as management frames. Frames
generated by mac80211 itself, such as BlockAckReq, are not affected.

In addition, rtw89_core_tx_update_desc_info() reads hdr->seq_ctrl at
offset 22 for every frame. Control frames have no sequence control
field and are shorter than 24 bytes, so only read hdr->seq_ctrl from
non-control frames whose linear header is long enough to carry it.

Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/core.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 54605f7..7003c24 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -637,12 +637,21 @@ static enum rtw89_core_tx_type
 rtw89_core_get_tx_type(struct rtw89_dev *rtwdev,
 		       struct sk_buff *skb)
 {
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_hdr *hdr = (void *)skb->data;
 	__le16 fc = hdr->frame_control;
 
 	if (ieee80211_is_mgmt(fc) || ieee80211_is_any_nullfunc(fc))
 		return RTW89_CORE_TX_TYPE_MGMT;
 
+	/* Injected control frames have no QoS/TID and no data payload, so
+	 * send them through the management path. Control frames built by
+	 * mac80211 itself (e.g. BlockAckReq) keep using the data path.
+	 */
+	if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+	    !ieee80211_is_data(fc))
+		return RTW89_CORE_TX_TYPE_MGMT;
+
 	return RTW89_CORE_TX_TYPE_DATA;
 }
 
@@ -1299,7 +1308,7 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
 	enum btc_pkt_type pkt_type;
 	bool upd_wlan_hdr = false;
 	bool is_bmc;
-	u16 seq;
+	u16 seq = 0;
 
 	desc_info->pkt_size = skb->len;
 
@@ -1315,7 +1324,10 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
 	else if (tx_req->vif)
 		desc_info->mlo = ieee80211_vif_is_mld(tx_req->vif);
 
-	seq = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
+	/* control frames (e.g. 10-byte ACK/CTS) have no sequence control */
+	if (!ieee80211_is_ctl(hdr->frame_control) &&
+	    skb_headlen(skb) >= sizeof(struct ieee80211_hdr_3addr))
+		seq = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
 	addr_cam = rtw89_get_addr_cam_of(tx_req->rtwvif_link,
 					 tx_req->rtwsta_link);
 	if (addr_cam->valid && desc_info->mlo)
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 08/10] wifi: rtw89: skip host-side TX hooks for injected frames
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
                   ` (6 preceding siblings ...)
  2026-11-13 23:52 ` [PATCH rtw-next v5 07/10] wifi: rtw89: send injected control frames through the management path Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 09/10] wifi: rtw89: honor ASSIGN_SEQ, retries, and NO_ACK " Enver Ikromov
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

Several TX hooks assume frames that were built by the local network
stack for an associated link:

- rtw89_traffic_stats_accu() and rtw89_core_tx_btc_spec_pkt_notify()
  inspect ip_hdr()/udp_hdr(). For frames injected on a monitor
  interface the network header points into the 802.11 header, so
  traffic statistics and BT-coexistence notifications are derived
  from unrelated bytes.
- rtw89_wow_parse_akm() records the AKM from (Re)Association Request
  frames for WoWLAN, which an injected frame must not change.
- rtw89_core_tx_update_he_qos_htc(), rtw89_core_tx_update_ampdu_info(),
  and rtw89_core_txq_check_agg() mutate QoS/HTC headers, enable A-MPDU
  aggregation, or start/stop BA sessions based on link state, while
  injected frames are expected to be sent as supplied.

Skip these hooks when IEEE80211_TX_CTL_INJECTED is set.

Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/core.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 7003c24..84c12c6 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -1351,9 +1351,11 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
 		break;
 	case RTW89_CORE_TX_TYPE_DATA:
 		rtw89_core_tx_update_data_info(rtwdev, tx_req);
-		pkt_type = rtw89_core_tx_btc_spec_pkt_notify(rtwdev, tx_req);
-		rtw89_core_tx_update_he_qos_htc(rtwdev, tx_req, pkt_type);
-		rtw89_core_tx_update_ampdu_info(rtwdev, tx_req, pkt_type);
+		if (likely(!(info->flags & IEEE80211_TX_CTL_INJECTED))) {
+			pkt_type = rtw89_core_tx_btc_spec_pkt_notify(rtwdev, tx_req);
+			rtw89_core_tx_update_he_qos_htc(rtwdev, tx_req, pkt_type);
+			rtw89_core_tx_update_ampdu_info(rtwdev, tx_req, pkt_type);
+		}
 		rtw89_core_tx_update_llc_hdr(rtwdev, desc_info, skb);
 		break;
 	default:
@@ -1477,8 +1479,10 @@ static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev,
 	tx_req.rtwsta_link = rtwsta_link;
 	tx_req.with_wait = !!wait;
 
-	rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, true, true);
-	rtw89_wow_parse_akm(rtwdev, skb);
+	if (likely(!(IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_INJECTED))) {
+		rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, true, true);
+		rtw89_wow_parse_akm(rtwdev, skb);
+	}
 	rtw89_core_tx_update_desc_info(rtwdev, &tx_req);
 	rtw89_core_tx_wake(rtwdev, &tx_req);
 
@@ -4740,6 +4744,9 @@ static void rtw89_core_txq_check_agg(struct rtw89_dev *rtwdev,
 	struct ieee80211_sta *sta = txq->sta;
 	struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta);
 
+	if (unlikely(IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_INJECTED))
+		return;
+
 	if (test_bit(RTW89_TXQ_F_FORBID_BA, rtwtxq->flags))
 		return;
 
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 09/10] wifi: rtw89: honor ASSIGN_SEQ, retries, and NO_ACK for injected frames
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
                   ` (7 preceding siblings ...)
  2026-11-13 23:52 ` [PATCH rtw-next v5 08/10] wifi: rtw89: skip host-side TX hooks for injected frames Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-13 23:52 ` [PATCH rtw-next v5 10/10] wifi: rtw89: restore channel when leaving idle on non-chanctx devices Enver Ikromov
  2026-11-15  3:12 ` [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Ping-Ke Shih
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

For injected frames, rtw89_core_tx_update_injection() only applies
the radiotap rate and leaves several TX descriptor and completion
settings mismatched with the radiotap flags:

- The management path sets hw_ssn_sel/hw_seq_mode (and MLO links set
  upd_wlan_hdr), overwriting user-supplied sequence numbers when
  IEEE80211_TX_CTL_ASSIGN_SEQ is not set, and leaving hw_seq_mode
  enabled even on injected control frames that have no seq_ctrl field.
  Conversely, injected data frames leave hw_seq_mode unset when
  ASSIGN_SEQ is requested on a monitor interface.
- Unicast frames unconditionally set RTS_EN = 1 and HW_RTS_EN = 1 in
  txwd_info4. On normal traffic HW_RTS_EN = 1 gates RTS on the MAC
  length/duration threshold (4080B / 2816us), whereas for injected
  frames RTS_EN must follow IEEE80211_TX_RC_USE_RTS_CTS with
  HW_RTS_EN = 0 so short frames requesting RTS/CTS actually emit RTS.
- IEEE80211_TX_CTL_NO_ACK (e.g. IEEE80211_RADIOTAP_F_TX_NOACK) neither
  sets BE_TXD_BODY6_NO_ACK on Wi-Fi 7 descriptors nor bounds retries,
  causing unacknowledged frames to be retransmitted up to the retry
  limit. Note that Wi-Fi 6 TXWD v0 chips (RTL8852A/B, RTL8851B) have
  no dedicated per-packet NO_ACK descriptor bit and rely on
  tx_cnt_lmt = 1 to suppress retransmissions; when that single attempt
  completes with RTW89_TX_RETRY_LIMIT, suppress false tx_retry_lmt
  accounting in rtw89_pci_tx_status() and set
  IEEE80211_TX_STAT_NOACK_TRANSMITTED in rtw89_tx_rpt_tx_status() too.
- hw->max_rate_tries is left at the mac80211 default (4), truncating
  IEEE80211_RADIOTAP_DATA_RETRIES, and info->control.rates[0].count is
  ignored when a radiotap rate is specified.
- Positive radiotap HT/VHT/legacy rate indices are added to the table
  base without clamping to the chip's valid MCS or legacy rate bounds.

Configure hardware sequence assignment according to
IEEE80211_TX_CTL_ASSIGN_SEQ (and never on control frames), drive
rts_en and hw_rts_en for radiotap RTS/CTS, advertise
RTW89_TX_DATA_CNT_LMT_MAX (63) in hw->max_rate_tries, honor
rates[0].count and NO_ACK descriptor/status semantics, and clamp
positive radiotap rate indices to valid hardware ranges.

Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/core.c | 63 ++++++++++++++++++-----
 drivers/net/wireless/realtek/rtw89/core.h |  4 ++
 drivers/net/wireless/realtek/rtw89/mac.h  |  2 +
 drivers/net/wireless/realtek/rtw89/pci.c  |  3 +-
 4 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 84c12c6..7d174d2 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -1252,12 +1252,43 @@ static void rtw89_core_tx_update_injection(struct rtw89_dev *rtwdev,
 	const struct rtw89_hw_rate_def *hw_rate = &rtw89_hw_rate[rtwdev->chip->chip_gen];
 	enum mac80211_rate_control_flags flags = info->control.rates[0].flags;
 	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+	struct ieee80211_hdr *hdr = (void *)tx_req->skb->data;
 	const struct rtw89_chan *chan;
 	s8 idx = info->control.rates[0].idx;
-	u8 nss, mcs;
+	u8 nss, mcs, max_mcs;
 
 	desc_info->use_rate = true;
 	desc_info->dis_data_fb = true;
+	desc_info->hw_rts_en = false;
+	desc_info->rts_en = (flags & IEEE80211_TX_RC_USE_RTS_CTS) &&
+			    !desc_info->is_bmc &&
+			    !(info->flags & IEEE80211_TX_CTL_NO_ACK);
+
+	/* Control frames have no seq_ctrl field; otherwise keep the
+	 * sequence number supplied by user space unless ASSIGN_SEQ is set.
+	 */
+	if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) ||
+	    ieee80211_is_ctl(hdr->frame_control)) {
+		desc_info->upd_wlan_hdr = false;
+		desc_info->hw_ssn_sel = 0;
+		desc_info->hw_seq_mode = 0;
+	} else {
+		desc_info->hw_ssn_sel = RTW89_MGMT_HW_SSN_SEL;
+		desc_info->hw_seq_mode = RTW89_MGMT_HW_SEQ_MODE;
+	}
+
+	/* Suppress hardware retransmissions (and ACK waiting on BE) for NO_ACK;
+	 * otherwise honor explicit radiotap retry limits when a rate is set.
+	 */
+	if (info->flags & IEEE80211_TX_CTL_NO_ACK) {
+		desc_info->no_ack = true;
+		desc_info->tx_cnt_lmt_en = true;
+		desc_info->tx_cnt_lmt = 1;
+	} else if (info->control.rates[0].count) {
+		desc_info->tx_cnt_lmt_en = true;
+		desc_info->tx_cnt_lmt = umin(info->control.rates[0].count,
+					     RTW89_TX_DATA_CNT_LMT_MAX);
+	}
 
 	/* No rate was given in the radiotap header. Management frames
 	 * already carry the rate from rtw89_core_tx_update_mgmt_info(), so
@@ -1283,16 +1314,20 @@ static void rtw89_core_tx_update_injection(struct rtw89_dev *rtwdev,
 		desc_info->gi_ltf = 1;
 
 	if (flags & IEEE80211_TX_RC_VHT_MCS) {
+		max_mcs = rtwdev->chip->chip_gen == RTW89_CHIP_BE ? 11 : 9;
 		nss = umin(idx >> 4, ARRAY_SIZE(hw_rate->vht) - 1);
-		mcs = idx & 0xf;
+		mcs = umin(idx & 0xf, max_mcs);
 		desc_info->data_rate = hw_rate->vht[nss] + mcs;
 	} else if (flags & IEEE80211_TX_RC_MCS) {
-		desc_info->data_rate = hw_rate->ht + idx;
+		desc_info->data_rate = hw_rate->ht + umin(idx, 31);
 	} else {
 		chan = rtw89_chan_get(rtwdev, tx_req->rtwvif_link->chanctx_idx);
-
-		desc_info->data_rate = idx + (chan->band_type == RTW89_BAND_2G ?
-					      RTW89_HW_RATE_CCK1 : RTW89_HW_RATE_OFDM6);
+		if (chan->band_type == RTW89_BAND_2G)
+			desc_info->data_rate = RTW89_HW_RATE_CCK1 +
+					       umin(idx, 11);
+		else
+			desc_info->data_rate = RTW89_HW_RATE_OFDM6 +
+					       umin(idx, 7);
 	}
 }
 
@@ -1341,6 +1376,8 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
 
 	desc_info->seq = seq;
 	desc_info->is_bmc = is_bmc;
+	desc_info->rts_en = !is_bmc;
+	desc_info->hw_rts_en = true;
 	desc_info->wd_page = true;
 	desc_info->hiq = info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM;
 	desc_info->upd_wlan_hdr = upd_wlan_hdr;
@@ -1676,9 +1713,8 @@ static __le32 rtw89_build_txwd_info3(struct rtw89_tx_desc_info *desc_info)
 
 static __le32 rtw89_build_txwd_info4(struct rtw89_tx_desc_info *desc_info)
 {
-	bool rts_en = !desc_info->is_bmc;
-	u32 dword = FIELD_PREP(RTW89_TXWD_INFO4_RTS_EN, rts_en) |
-		    FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, 1) |
+	u32 dword = FIELD_PREP(RTW89_TXWD_INFO4_RTS_EN, desc_info->rts_en) |
+		    FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, desc_info->hw_rts_en) |
 		    FIELD_PREP(RTW89_TXWD_INFO4_SW_DEFINE, desc_info->sn);
 
 	return cpu_to_le32(dword);
@@ -1819,7 +1855,8 @@ static __le32 rtw89_build_txwd_body5_v2(struct rtw89_tx_desc_info *desc_info)
 
 static __le32 rtw89_build_txwd_body6_v2(struct rtw89_tx_desc_info *desc_info)
 {
-	u32 dword = FIELD_PREP(BE_TXD_BODY6_UPD_WLAN_HDR, desc_info->upd_wlan_hdr);
+	u32 dword = FIELD_PREP(BE_TXD_BODY6_NO_ACK, desc_info->no_ack) |
+		    FIELD_PREP(BE_TXD_BODY6_UPD_WLAN_HDR, desc_info->upd_wlan_hdr);
 
 	return cpu_to_le32(dword);
 }
@@ -1881,9 +1918,8 @@ static __le32 rtw89_build_txwd_info2_v3(struct rtw89_tx_desc_info *desc_info)
 
 static __le32 rtw89_build_txwd_info4_v2(struct rtw89_tx_desc_info *desc_info)
 {
-	bool rts_en = !desc_info->is_bmc;
-	u32 dword = FIELD_PREP(BE_TXD_INFO4_RTS_EN, rts_en) |
-		    FIELD_PREP(BE_TXD_INFO4_HW_RTS_EN, 1);
+	u32 dword = FIELD_PREP(BE_TXD_INFO4_RTS_EN, desc_info->rts_en) |
+		    FIELD_PREP(BE_TXD_INFO4_HW_RTS_EN, desc_info->hw_rts_en);
 
 	return cpu_to_le32(dword);
 }
@@ -7542,6 +7578,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
 	hw->queues = IEEE80211_NUM_ACS;
 	hw->max_rx_aggregation_subframes = chip->max_rx_agg_num;
 	hw->max_tx_aggregation_subframes = chip->max_tx_agg_num;
+	hw->max_rate_tries = RTW89_TX_DATA_CNT_LMT_MAX;
 	hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL;
 
 	hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 7b03154..3754ab6 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1278,6 +1278,7 @@ struct rtw89_tx_desc_info {
 	bool tx_cnt_lmt_en;
 	u8 sn: 4;
 	u8 tx_cnt_lmt: 6;
+#define RTW89_TX_DATA_CNT_LMT_MAX	0x3f
 	u8 addr_info_nr;
 	u8 sec_keyid;
 	u8 sec_type;
@@ -1300,6 +1301,9 @@ struct rtw89_tx_desc_info {
 	bool stbc;
 	bool ldpc;
 	bool upd_wlan_hdr;
+	bool no_ack;
+	bool rts_en;
+	bool hw_rts_en;
 	bool mlo;
 	bool sw_mld;
 };
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index b4a7cb7..6870cc2 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -1888,6 +1888,8 @@ void rtw89_tx_rpt_tx_status(struct rtw89_dev *rtwdev, struct sk_buff *skb,
 	info = IEEE80211_SKB_CB(skb);
 	ieee80211_tx_info_clear_status(info);
 
+	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+		info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
 	if (tx_status == RTW89_TX_DONE)
 		info->flags |= IEEE80211_TX_STAT_ACK;
 	else
diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c
index 6d19e75..6c4d6c0 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.c
+++ b/drivers/net/wireless/realtek/rtw89/pci.c
@@ -475,7 +475,8 @@ static void rtw89_pci_tx_status(struct rtw89_dev *rtwdev,
 	if (tx_status == RTW89_TX_DONE) {
 		info->flags |= IEEE80211_TX_STAT_ACK;
 		tx_ring->tx_acked++;
-	} else {
+	} else if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) ||
+		   tx_status != RTW89_TX_RETRY_LIMIT) {
 		if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
 			rtw89_debug(rtwdev, RTW89_DBG_FW,
 				    "failed to TX of status %x\n", tx_status);
-- 
2.56.0.windows.4


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

* [PATCH rtw-next v5 10/10] wifi: rtw89: restore channel when leaving idle on non-chanctx devices
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
                   ` (8 preceding siblings ...)
  2026-11-13 23:52 ` [PATCH rtw-next v5 09/10] wifi: rtw89: honor ASSIGN_SEQ, retries, and NO_ACK " Enver Ikromov
@ 2026-11-13 23:52 ` Enver Ikromov
  2026-11-15  3:12 ` [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Ping-Ke Shih
  10 siblings, 0 replies; 12+ messages in thread
From: Enver Ikromov @ 2026-11-13 23:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless, linux-kernel, Enver Ikromov

rtw89_alloc_ieee80211_hw() disables native chanctx callbacks
(ops->assign_vif_chanctx = NULL) and relies on mac80211 chanctx
emulation via hw->conf.chandef and rtw89_ops_config() whenever
chip->support_chanctx_num == 0 or the loaded firmware lacks
SCAN_OFFLOAD or BEACON_FILTER (for example on RTL8852AE with firmware
0.13.36.2, where support_chanctx_num == 1 but BEACON_FILTER is absent).

When the last monitor interface is brought down or deleted (or all
interfaces become idle), rtw89_ops_stop() / rtw89_enter_ips() powers
down the MAC/BB/RF and clears the RTW89_PHY_0 entity state while
mac80211 retains local->hw.conf.chandef. When a monitor interface
(including a concurrent MONITOR_FLAG_ACTIVE interface alongside an
unassociated STA, where __ieee80211_recalc_idle() clears
IEEE80211_CONF_IDLE without setting IEEE80211_CONF_MONITOR) is brought
up on the same channel, rtw89_ops_start() sets RTW89_FLAG_POWERON
without programming the channel (so rtw89_leave_ips() returns early),
and ieee80211_hw_conf_chan() omits IEEE80211_CONF_CHANGE_CHANNEL
because chandef is unchanged, leaving RX/TX silent until a manual
channel change.

In rtw89_ops_config(), when IEEE80211_CONF_CHANGE_IDLE or
IEEE80211_CONF_CHANGE_MONITOR leaves the device non-idle on hardware
without assign_vif_chanctx and RTW89_PHY_0 is not yet active, apply
hw->conf.chandef and call rtw89_set_channel(). When
IEEE80211_CONF_CHANGE_CHANNEL has already configured RTW89_PHY_0, the
entity state check avoids redundant RF calibration.

Signed-off-by: Enver Ikromov <owner@ph4ntom-rev.uz>
---
 drivers/net/wireless/realtek/rtw89/mac80211.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index c8a6003..3649a7a 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -152,6 +152,17 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
 	if (changed & IEEE80211_CONF_CHANGE_MONITOR)
 		rtw89_physts_parsing_init(rtwdev);
 
+	if ((changed & (IEEE80211_CONF_CHANGE_IDLE |
+			IEEE80211_CONF_CHANGE_MONITOR)) &&
+	    !(hw->conf.flags & IEEE80211_CONF_IDLE) &&
+	    !rtwdev->ops->assign_vif_chanctx &&
+	    !rtw89_get_entity_state(rtwdev, RTW89_PHY_0) &&
+	    cfg80211_chandef_valid(&hw->conf.chandef)) {
+		rtw89_config_entity_chandef(rtwdev, RTW89_CHANCTX_0,
+					    &hw->conf.chandef);
+		rtw89_set_channel(rtwdev);
+	}
+
 	return 0;
 }
 
-- 
2.56.0.windows.4


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

* RE: [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes
  2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
                   ` (9 preceding siblings ...)
  2026-11-13 23:52 ` [PATCH rtw-next v5 10/10] wifi: rtw89: restore channel when leaving idle on non-chanctx devices Enver Ikromov
@ 2026-11-15  3:12 ` Ping-Ke Shih
  10 siblings, 0 replies; 12+ messages in thread
From: Ping-Ke Shih @ 2026-11-15  3:12 UTC (permalink / raw)
  To: Enver Ikromov; +Cc: linux-wireless, linux-kernel

Enver Ikromov <owner@ph4ntom-rev.uz> wrote:
> v4: not sent to the list

What's wrong? 

At first glance, I saw v5. I was confused, because I don't remember I have
seen v4. Please not count your local version. 

> v3: https://lore.kernel.org/linux-wireless/20261104203051.564-1-owner@ph4ntom-rev.uz/
> v2: https://lore.kernel.org/linux-wireless/179371849554.20972.16232816164394690660@ph4ntom-rev.uz/
> v1 (RFC):
> https://lore.kernel.org/linux-wireless/1a196d6dfbc.35537d221913104450.3720060455831933789@zoho.com
> /




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

end of thread, other threads:[~2026-11-15  3:12 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-13 23:52 [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 01/10] wifi: rtw89: avoid NULL vif dereference in rtw89_ops_tx() Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 02/10] wifi: rtw89: handle negative rate index for injected frames Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 03/10] wifi: rtw89: fix RX filter configuration for monitor mode Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 04/10] wifi: rtw89: protect pure_monitor_mode_vif with RCU Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 05/10] wifi: rtw89: use AP self role for monitor interfaces Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 06/10] wifi: rtw89: transmit frames from " Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 07/10] wifi: rtw89: send injected control frames through the management path Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 08/10] wifi: rtw89: skip host-side TX hooks for injected frames Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 09/10] wifi: rtw89: honor ASSIGN_SEQ, retries, and NO_ACK " Enver Ikromov
2026-11-13 23:52 ` [PATCH rtw-next v5 10/10] wifi: rtw89: restore channel when leaving idle on non-chanctx devices Enver Ikromov
2026-11-15  3:12 ` [PATCH rtw-next v5 00/10] wifi: rtw89: monitor mode RX filter and raw frame injection fixes 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

all inboxes | Powered by JetHome®