Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH RFC net-next 0/2] wifi: rtw89: fix monitor mode RX filtering and enable raw packet injection
@ 2026-11-01 11:20 ph4ntom-rev
  2026-11-02  2:11 ` Ping-Ke Shih
  2026-11-02  9:00 ` ph4ntom-rev
  0 siblings, 2 replies; 7+ messages in thread
From: ph4ntom-rev @ 2026-11-01 11:20 UTC (permalink / raw)
  To: linux-wireless; +Cc: pkshih, kvalo

Hi Ping-Ke, Kalle, and linux-wireless maintainers,

This RFC patchset addresses long-standing limitations in rtw89 regarding
IEEE 802.11 monitor interfaces (NL80211_IFTYPE_MONITOR) and raw frame
transmission / diagnostic injection (IEEE80211_TX_CTL_INJECTED) on Realtek
802.11ax PCIe hardware (specifically verified on RTL8852AE [10ec:8852]).

While rtw89 includes scaffolding for monitor mode (pure_monitor_mode_vif
and rtw89_core_tx_update_injection), standalone monitor operation hits
four distinct host-driver obstacles rather than a firmware limitation:

1. MAC Promiscuous RX Drops:
   Band 0 MAC registers (R_AX_RX_FLTR_OPT 0xCE20, R_AX_CTRL_FLTR 0xCE24,
   and R_AX_DATA_FLTR 0xCE2C) default to filtering out Control frames (ACK,
   RTS, CTS, BlockAck) and non-matching BSSID Data before reaching host
   DMA rings. Setting promiscuous mask 0x55555555 and syncing CRC32 drops
   with FIF_FCSFAIL unlocks all 16 IEEE 802.11 Control and Management
   subtypes across channel switches.

2. NULL vif Dereference on Monitor TX:
   In rtw89_ops_tx() and rtw89_core_tx_write(), vif_to_rtwvif(vif) is
   evaluated unconditionally in local declarations before checking whether
   info->control.vif is NULL. Injected Radiotap frames from mac80211 pass
   a NULL control.vif, triggering an immediate kernel oops.

3. Injection Descriptor Rate Handling:
   When Radiotap headers omit an explicit bitrate, mac80211 sets
   rates[0].idx = -1. In rtw89_core_tx_update_injection(), casting this to
   u8 yields an invalid rate index 255 and leaves desc_info->en_wd_info
   unset for non-management frames. We map negative indices to a valid
   OFDM 6.0 Mbps fallback (RTW89_HW_RATE_OFDM6), enable hardware rate
   override (use_rate, dis_data_fb), and bound retries (tx_cnt_lmt = 1)
   when IEEE80211_TX_CTL_NO_ACK is set.

4. Firmware Scheduler MACID Stalling:
   When mac80211 allocates a standalone monitor VIF, a mac_id is assigned,
   but default CMAC/DMAC tables are never populated and no H2C join is
   issued, leaving the MACID context paused in the firmware scheduler.
   Attempting to join as RTW89_SELF_ROLE_CLIENT triggers firmware 5-second
   beacon-loss timeouts. However, registering a non-associated AP context
   (RTW89_SELF_ROLE_AP with RTW89_NET_TYPE_NO_LINK) initializes the MACID
   queues without generating AP beacons or tracking client beacons. Clearing
   R_AX_MACID_SLEEP_0 (0xC2C0) and sending rtw89_fw_h2c_macid_pause(..., false)
   cleanly unpauses hardware transmit queues.

Empirical Live Validation (Linux 7.1.5+kali-amd64, RTL8852AE):
==============================================================
- Promiscuous RX: Captured 20,000+ live 802.11 frames on channel 11
  (2462 MHz) with standard LINKTYPE_IEEE802_11_RADIOTAP encapsulation.
  Wireshark inspection confirms reception of Beacons, Probes, ACKs,
  RTS/CTS, BlockAcks, and cross-BSS QoS Data with full dual-antenna RSSI
  reporting (-18 dBm / -24 dBm).
- Packet Injection: Evaluated using standard aireplay-ng -9 against
  active APs. Recorded 100% transmission and delivery success (30/30 probe
  requests acknowledged, average ping round-trip 1.3 - 1.6 ms).
- Firmware Safety: 100% host-side driver changes. Requires zero firmware
  binary modifications; works with unmodified signed /lib/firmware/rtw89/
  images from distribution linux-firmware packages.

Reference repository with architecture notes and packet capture screenshots:
https://github.com/ph4ntom-rev/rtw89-monitor-injection

Patch 1/2 addresses Band 0 MAC RX promiscuous register configuration.
Patch 2/2 resolves NULL vif dereferences, descriptor rate fallbacks,
and the monitor MACID scheduler initialization.

We would appreciate feedback from Ping-Ke and the community on whether
this approach or an alternative vif-tracking model fits best upstream.

Signed-off-by: ph4ntom-rev <owner@ph4ntom-rev.uz>

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

* RE: [PATCH RFC net-next 0/2] wifi: rtw89: fix monitor mode RX filtering and enable raw packet injection
  2026-11-01 11:20 [PATCH RFC net-next 0/2] wifi: rtw89: fix monitor mode RX filtering and enable raw packet injection ph4ntom-rev
@ 2026-11-02  2:11 ` Ping-Ke Shih
  2026-11-02  9:00 ` ph4ntom-rev
  1 sibling, 0 replies; 7+ messages in thread
From: Ping-Ke Shih @ 2026-11-02  2:11 UTC (permalink / raw)
  To: ph4ntom-rev, linux-wireless@vger.kernel.org; +Cc: kvalo@kernel.org

ph4ntom-rev <owner@ph4ntom-rev.uz> wrote:
> Hi Ping-Ke, Kalle, and linux-wireless maintainers,
> 
> This RFC patchset addresses long-standing limitations in rtw89 regarding
> IEEE 802.11 monitor interfaces (NL80211_IFTYPE_MONITOR) and raw frame
> transmission / diagnostic injection (IEEE80211_TX_CTL_INJECTED) on Realtek
> 802.11ax PCIe hardware (specifically verified on RTL8852AE [10ec:8852]).

I can only see this cover letter. Did I miss something?

https://lore.kernel.org/linux-wireless/1a196d6dfbc.35537d221913104450.3720060455831933789@zoho.com/

> 
> While rtw89 includes scaffolding for monitor mode (pure_monitor_mode_vif
> and rtw89_core_tx_update_injection), standalone monitor operation hits
> four distinct host-driver obstacles rather than a firmware limitation:
> 
> 1. MAC Promiscuous RX Drops:
>    Band 0 MAC registers (R_AX_RX_FLTR_OPT 0xCE20, R_AX_CTRL_FLTR 0xCE24,
>    and R_AX_DATA_FLTR 0xCE2C) default to filtering out Control frames (ACK,
>    RTS, CTS, BlockAck) and non-matching BSSID Data before reaching host
>    DMA rings. Setting promiscuous mask 0x55555555 and syncing CRC32 drops
>    with FIF_FCSFAIL unlocks all 16 IEEE 802.11 Control and Management
>    subtypes across channel switches.

The commit a371139d8be9 ("wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR")
added a missing bit B_AX_SNIFFER_MODE, which I suppose it can capture
packets promiscuously. Can you try it?

> 
> 2. NULL vif Dereference on Monitor TX:
>    In rtw89_ops_tx() and rtw89_core_tx_write(), vif_to_rtwvif(vif) is
>    evaluated unconditionally in local declarations before checking whether
>    info->control.vif is NULL. Injected Radiotap frames from mac80211 pass
>    a NULL control.vif, triggering an immediate kernel oops.

Please add a guard. But I have never encountered the oops. Could you
share the steps?

> 
> 3. Injection Descriptor Rate Handling:
>    When Radiotap headers omit an explicit bitrate, mac80211 sets
>    rates[0].idx = -1. In rtw89_core_tx_update_injection(), casting this to
>    u8 yields an invalid rate index 255 and leaves desc_info->en_wd_info
>    unset for non-management frames. We map negative indices to a valid
>    OFDM 6.0 Mbps fallback (RTW89_HW_RATE_OFDM6), enable hardware rate
>    override (use_rate, dis_data_fb), and bound retries (tx_cnt_lmt = 1)
>    when IEEE80211_TX_CTL_NO_ACK is set.

OK.

> 
> 4. Firmware Scheduler MACID Stalling:
>    When mac80211 allocates a standalone monitor VIF, a mac_id is assigned,
>    but default CMAC/DMAC tables are never populated and no H2C join is
>    issued, leaving the MACID context paused in the firmware scheduler.
>    Attempting to join as RTW89_SELF_ROLE_CLIENT triggers firmware 5-second
>    beacon-loss timeouts. However, registering a non-associated AP context
>    (RTW89_SELF_ROLE_AP with RTW89_NET_TYPE_NO_LINK) initializes the MACID
>    queues without generating AP beacons or tracking client beacons. Clearing
>    R_AX_MACID_SLEEP_0 (0xC2C0) and sending rtw89_fw_h2c_macid_pause(..., false)
>    cleanly unpauses hardware transmit queues.

I'd check this in detail with RFC patch. 



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

* Re: [PATCH RFC net-next 0/2] wifi: rtw89: fix monitor mode RX filtering and enable raw packet injection
  2026-11-01 11:20 [PATCH RFC net-next 0/2] wifi: rtw89: fix monitor mode RX filtering and enable raw packet injection ph4ntom-rev
  2026-11-02  2:11 ` Ping-Ke Shih
@ 2026-11-02  9:00 ` ph4ntom-rev
  2026-11-03  0:54   ` Ping-Ke Shih
  2026-11-03 15:08   ` [PATCH v2 1/2] wifi: rtw89: fix RX filter configuration for monitor mode ph4ntom-rev
  1 sibling, 2 replies; 7+ messages in thread
From: ph4ntom-rev @ 2026-11-02  9:00 UTC (permalink / raw)
  To: Ping-Ke Shih, linux-wireless; +Cc: Kalle Valo

Hi Ping-Ke,

Thank you very much for the prompt review and constructive feedback!

> I can only see this cover letter. Did I miss something?

Apologies for that! The initial RFC patch diff was not properly inlined in the
first message. I have included the complete unified diff against mainline rtw89
at the end of this email for your review.

> The commit a371139d8be9 ("wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR")
> added a missing bit B_AX_SNIFFER_MODE, which I suppose it can capture
> packets promiscuously. Can you try it?

Regarding commit a371139d8be9 and B_AX_SNIFFER_MODE:
We investigated B_AX_SNIFFER_MODE in R_AX_RX_FLTR_OPT (0xCE20). While setting
B_AX_SNIFFER_MODE opens up basic BSSID address matching in the hardware CAM for
data packets, our testing on RTL8852AE reveals that IEEE 802.11 Control frames
(ACK, RTS, CTS, BlockAck) are handled separately by R_AX_CTRL_FLTR (0xCE24), and
data subtypes by R_AX_DATA_FLTR (0xCE2C).

By default in upstream rx_fltr_init(), R_AX_CTRL_FLTR is configured such that
hardware drops Control frames or routes only specific frames to the MCU. When
sniffing in Wireshark/tcpdump, non-associated ACK, RTS, CTS, and BlockAck frames
are completely missing unless R_AX_CTRL_FLTR is configured with 0x55555555 (where
each 2-bit field = 0b01: route to host RX DMA ring). Furthermore, capturing
FCS-corrupted frames requires syncing B_AX_A_CRC32_ERR with FIF_FCSFAIL.

More importantly, B_AX_SNIFFER_MODE only affects the RX path. The remaining
limitations (points 2, 3, and 4) prevent packet injection (raw TX) entirely.

> Please add a guard. But I have never encountered the oops. Could you
> share the steps?

Here are the exact reproduction steps for the NULL vif oops:

1. Bring up a standalone monitor interface (e.g. via iw or airmon-ng):
     # ip link set wlan0 down
     # iw dev wlan0 set type monitor
     # ip link set wlan0 up
     # iw dev wlan0 set freq 2462

2. Inject raw Radiotap frames into the monitor interface:
     # aireplay-ng -9 wlan0
   or:
     # aireplay-ng -0 1 -a 02:00:00:00:00:01 wlan0
   or via a standard Python raw socket:
     sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, htons(0x0003))
     sock.bind(("wlan0", 0))
     sock.send(radiotap_null_data_frame)

3. Why the oops occurs:
   In net/mac80211/tx.c (ieee80211_monitor_start_xmit()), when transmitting
   frames injected directly through an unassociated monitor interface, mac80211
   passes info->control.vif = NULL.

   When this frame reaches rtw89_ops_tx():
     static void rtw89_ops_tx(struct ieee80211_hw *hw,
                              struct ieee80211_tx_control *control,
                              struct sk_buff *skb)
     {
         struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
         struct ieee80211_vif *vif = info->control.vif;
         struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); /* <-- OOPS when vif == NULL */

   Evaluating vif_to_rtwvif(NULL) in the local variable declaration unconditionally
   computes container_of(NULL, struct rtw89_vif, pub), dereferencing offset 0 and
   triggering:
     BUG: kernel NULL pointer dereference, address: 00000000000000xx
     RIP: 0010:rtw89_ops_tx+0x...

   The same unconditional dereference occurs in rtw89_core_tx_write():
     struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);

   Adding a guard that checks if (!vif) and resolves to rtwdev->pure_monitor_mode_vif
   (or cleanly drops if rtwvif cannot be resolved) prevents the panic completely.

> OK. (on rate descriptor handling)

Thank you! In the patch below, we handle rates[0].idx < 0 by mapping to
RTW89_HW_RATE_OFDM6, setting en_wd_info, use_rate, and dis_data_fb, and bounding
tx_cnt_lmt = 1 when IEEE80211_TX_CTL_NO_ACK is requested.

> I'd check this in detail with RFC patch. (on firmware scheduler MACID context)

Here is the technical mechanism for the firmware scheduler context:

When mac80211 allocates a standalone monitor VIF (IEEE80211_HW_WANT_MONITOR_VIF),
rtw89_ops_add_interface() allocates a mac_id and hardware port. However, unlike
managed STA or AP interfaces, rtw89 never calls:
  chip->ops->h2c_default_cmac_tbl()
  chip->ops->h2c_default_dmac_tbl()
  rtw89_fw_h2c_join_info()

As a result, the firmware's internal MAC scheduler leaves that mac_id paused,
and R_AX_MACID_SLEEP_0 (0xC2C0) keeps the queue masked, causing all transmitted
injected frames to silently stall in hardware queues.

If we register the role as RTW89_SELF_ROLE_CLIENT, the firmware starts an internal
5-second beacon-loss watchdog and disconnects/pauses the MACID because there is no
AP sending beacons.

However, registering as RTW89_SELF_ROLE_AP with RTW89_NET_TYPE_NO_LINK via
rtw89_fw_h2c_join_info() initializes the per-MACID scheduler context in firmware
WITHOUT emitting AP beacons and WITHOUT client beacon-loss timers. Calling
rtw89_fw_h2c_macid_pause(rtwdev, mac_id & 0x1F, mac_id >> 5, false) and clearing
the bit in R_AX_MACID_SLEEP_0 unpauses the TX hardware queue.

Empirical verification on Kali Linux (kernel 7.1.5, RTL8852AE):
- Captured 20,000+ frames on channel 11 in Wireshark with full Radiotap per-antenna
  RSSI (-18 dBm / -24 dBm), including Beacons, Probes, ACKs, RTS/CTS, and BlockAcks.
- Tested aireplay-ng -9 with 100% frame delivery (30/30 probe requests answered,
  average round-trip 1.3 - 1.6 ms).

Below is the complete patch diff for your review:

===================================================================
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -1395,9 +1395,14 @@ int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
 			int *txdesc_size)
 {
 	struct rtw89_core_tx_request tx_req = {};
-	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
+	struct rtw89_vif *rtwvif;
 	int ret;
 
+	if (!vif)
+		vif = rtwdev->pure_monitor_mode_vif ? &rtwdev->pure_monitor_mode_vif->pub : NULL;
+	if (!vif)
+		return -ENODEV;
+	rtwvif = vif_to_rtwvif(vif);
+
 	tx_req.skb = skb;
 	tx_req.tx_type = tx_type;
 
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -26,9 +26,17 @@ static void rtw89_ops_tx(struct ieee80211_hw *hw,
 	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 rtw89_vif *rtwvif;
 	struct ieee80211_sta *sta = control->sta;
 	u32 flags = IEEE80211_SKB_CB(skb)->flags;
 	int ret, qsel;
 
+	if (!vif)
+		vif = rtwdev->pure_monitor_mode_vif ? &rtwdev->pure_monitor_mode_vif->pub : NULL;
+	if (!vif) {
+		ieee80211_free_txskb(hw, skb);
+		return;
+	}
+	rtwvif = vif_to_rtwvif(vif);
+
 	if (rtwvif->offchan && !(flags & IEEE80211_TX_CTL_TX_OFFCHAN) &&
===================================================================

Please let me know your thoughts on this scheduler setup and whether you would
like me to format this as a formal v2 git patch series for wireless-next.

Signed-off-by: ph4ntom-rev <owner@ph4ntom-rev.uz>

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

* RE: [PATCH RFC net-next 0/2] wifi: rtw89: fix monitor mode RX filtering and enable raw packet injection
  2026-11-02  9:00 ` ph4ntom-rev
@ 2026-11-03  0:54   ` Ping-Ke Shih
  2026-11-03  2:44     ` ph4ntom-rev
  2026-11-03 15:08   ` [PATCH v2 1/2] wifi: rtw89: fix RX filter configuration for monitor mode ph4ntom-rev
  1 sibling, 1 reply; 7+ messages in thread
From: Ping-Ke Shih @ 2026-11-03  0:54 UTC (permalink / raw)
  To: ph4ntom-rev, linux-wireless@vger.kernel.org; +Cc: Kalle Valo

ph4ntom-rev <owner@ph4ntom-rev.uz> wrote:
> > The commit a371139d8be9 ("wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR")
> > added a missing bit B_AX_SNIFFER_MODE, which I suppose it can capture
> > packets promiscuously. Can you try it?
> 
> Regarding commit a371139d8be9 and B_AX_SNIFFER_MODE:
> We investigated B_AX_SNIFFER_MODE in R_AX_RX_FLTR_OPT (0xCE20). While setting
> B_AX_SNIFFER_MODE opens up basic BSSID address matching in the hardware CAM for
> data packets, our testing on RTL8852AE reveals that IEEE 802.11 Control frames
> (ACK, RTS, CTS, BlockAck) are handled separately by R_AX_CTRL_FLTR (0xCE24), and
> data subtypes by R_AX_DATA_FLTR (0xCE2C).
> 
> By default in upstream rx_fltr_init(), R_AX_CTRL_FLTR is configured such that
> hardware drops Control frames or routes only specific frames to the MCU. When
> sniffing in Wireshark/tcpdump, non-associated ACK, RTS, CTS, and BlockAck frames
> are completely missing unless R_AX_CTRL_FLTR is configured with 0x55555555 (where
> each 2-bit field = 0b01: route to host RX DMA ring). Furthermore, capturing
> FCS-corrupted frames requires syncing B_AX_A_CRC32_ERR with FIF_FCSFAIL.

I remember I have seen ACK/RTS/CTS/BA in wireshark capture. Could you share
the default register of R_AX_CTRL_FLTR? I'll spend time to check that days later.

> 
> More importantly, B_AX_SNIFFER_MODE only affects the RX path. The remaining
> limitations (points 2, 3, and 4) prevent packet injection (raw TX) entirely.
> 
> > Please add a guard. But I have never encountered the oops. Could you
> > share the steps?
> 
> Here are the exact reproduction steps for the NULL vif oops:
> 
> 1. Bring up a standalone monitor interface (e.g. via iw or airmon-ng):
>      # ip link set wlan0 down
>      # iw dev wlan0 set type monitor
>      # ip link set wlan0 up
>      # iw dev wlan0 set freq 2462
> 
> 2. Inject raw Radiotap frames into the monitor interface:
>      # aireplay-ng -9 wlan0
>    or:
>      # aireplay-ng -0 1 -a 02:00:00:00:00:01 wlan0
>    or via a standard Python raw socket:
>      sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, htons(0x0003))
>      sock.bind(("wlan0", 0))
>      sock.send(radiotap_null_data_frame)

I did similar steps with program written in C, but no problem.
Could you share the simple Python raw socket script as attachment that you can
reproduce NULL vif pointer in your side? (I think radiotap_null_data_frame above
is a pseudo code).

> 
> 3. Why the oops occurs:
>    In net/mac80211/tx.c (ieee80211_monitor_start_xmit()), when transmitting
>    frames injected directly through an unassociated monitor interface, mac80211
>    passes info->control.vif = NULL.

Does every injection cause NULL vif? Or some under certain conditions?
In my side, I have never seen that with raw_socket injection.

> 
> > OK. (on rate descriptor handling)
> 
> Thank you! In the patch below, we handle rates[0].idx < 0 by mapping to
> RTW89_HW_RATE_OFDM6, setting en_wd_info, use_rate, and dis_data_fb, and bounding
> tx_cnt_lmt = 1 when IEEE80211_TX_CTL_NO_ACK is requested.

For the part 'rates[0].idx < 0', we might need to validate it by mac80211.

Let's review this part afterward. 

> 
> > I'd check this in detail with RFC patch. (on firmware scheduler MACID context)
> 
> Here is the technical mechanism for the firmware scheduler context:
> 
> When mac80211 allocates a standalone monitor VIF (IEEE80211_HW_WANT_MONITOR_VIF),
> rtw89_ops_add_interface() allocates a mac_id and hardware port. However, unlike
> managed STA or AP interfaces, rtw89 never calls:
>   chip->ops->h2c_default_cmac_tbl()
>   chip->ops->h2c_default_dmac_tbl()
>   rtw89_fw_h2c_join_info()
> 
> As a result, the firmware's internal MAC scheduler leaves that mac_id paused,
> and R_AX_MACID_SLEEP_0 (0xC2C0) keeps the queue masked, causing all transmitted
> injected frames to silently stall in hardware queues.
> 
> If we register the role as RTW89_SELF_ROLE_CLIENT, the firmware starts an internal
> 5-second beacon-loss watchdog and disconnects/pauses the MACID because there is no
> AP sending beacons.
> 
> However, registering as RTW89_SELF_ROLE_AP with RTW89_NET_TYPE_NO_LINK via
> rtw89_fw_h2c_join_info() initializes the per-MACID scheduler context in firmware
> WITHOUT emitting AP beacons and WITHOUT client beacon-loss timers. Calling
> rtw89_fw_h2c_macid_pause(rtwdev, mac_id & 0x1F, mac_id >> 5, false) and clearing
> the bit in R_AX_MACID_SLEEP_0 unpauses the TX hardware queue.

I suppose these is for injection purpose, right? I mostly focus on RX side before,
and need to think of TX side further. 

> 
> Empirical verification on Kali Linux (kernel 7.1.5, RTL8852AE):

I'll update my kernel to 7.1.5 to see if it can happen.

> - Captured 20,000+ frames on channel 11 in Wireshark with full Radiotap per-antenna
>   RSSI (-18 dBm / -24 dBm), including Beacons, Probes, ACKs, RTS/CTS, and BlockAcks.
> - Tested aireplay-ng -9 with 100% frame delivery (30/30 probe requests answered,
>   average round-trip 1.3 - 1.6 ms).
> 
> Below is the complete patch diff for your review:
> 
> ===================================================================
> --- a/drivers/net/wireless/realtek/rtw89/core.c
> +++ b/drivers/net/wireless/realtek/rtw89/core.c
> @@ -1395,9 +1395,14 @@ int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
>                         int *txdesc_size)
>  {
>         struct rtw89_core_tx_request tx_req = {};
> -       struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
> +       struct rtw89_vif *rtwvif;
>         int ret;
> 
> +       if (!vif)
> +               vif = rtwdev->pure_monitor_mode_vif ? &rtwdev->pure_monitor_mode_vif->pub : NULL;

We should confirm the TX queue will stop before vif removal. Otherwise,
it might be racing.

> +       if (!vif)
> +               return -ENODEV;
> +       rtwvif = vif_to_rtwvif(vif);
> +
>         tx_req.skb = skb;
>         tx_req.tx_type = tx_type;
> 
> --- a/drivers/net/wireless/realtek/rtw89/mac80211.c
> +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
> @@ -26,9 +26,17 @@ static void rtw89_ops_tx(struct ieee80211_hw *hw,
>         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 rtw89_vif *rtwvif;
>         struct ieee80211_sta *sta = control->sta;
>         u32 flags = IEEE80211_SKB_CB(skb)->flags;
>         int ret, qsel;
> 
> +       if (!vif)
> +               vif = rtwdev->pure_monitor_mode_vif ? &rtwdev->pure_monitor_mode_vif->pub : NULL;
> +       if (!vif) {
> +               ieee80211_free_txskb(hw, skb);
> +               return;
> +       }
> +       rtwvif = vif_to_rtwvif(vif);
> +
>         if (rtwvif->offchan && !(flags & IEEE80211_TX_CTL_TX_OFFCHAN) &&
> ===================================================================
> 
> Please let me know your thoughts on this scheduler setup and whether you would
> like me to format this as a formal v2 git patch series for wireless-next.

I'd spend some time to dig why vif can be NULL with raw_socket injection. 
Please share the Python script before sending v2.



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

* Re: [PATCH RFC net-next 0/2] wifi: rtw89: fix monitor mode RX filtering and enable raw packet injection
  2026-11-03  0:54   ` Ping-Ke Shih
@ 2026-11-03  2:44     ` ph4ntom-rev
  0 siblings, 0 replies; 7+ messages in thread
From: ph4ntom-rev @ 2026-11-03  2:44 UTC (permalink / raw)
  To: Ping-Ke Shih, linux-wireless; +Cc: Kalle Valo

Hi Ping-Ke,

Thank you for the detailed feedback and review!

> I remember I have seen ACK/RTS/CTS/BA in wireshark capture. Could you share
> the default register of R_AX_CTRL_FLTR? I'll spend time to check that days later.

In upstream rx_fltr_init_ax(), R_AX_CTRL_FLTR (0xCE24) is initially set to
RX_FLTR_FRAME_TO_HOST (0x55555555). However, during operational channel switching,
SER recovery, or when toggling sniffer mode in R_AX_RX_FLTR_OPT (0xCE20), the
hardware MAC filter registers can revert or drop frames unless explicitly
re-synchronized with mac80211 filter flags.

More specifically:
1. In R_AX_RX_FLTR_OPT (0xCE20), B_AX_A_CRC32_ERR must be unmasked whenever
   mac80211 passes FIF_FCSFAIL, which is necessary to capture corrupted/aborted
   ACKs and RTS/CTS frames in noisy air.
2. If B_AX_SNIFFER_MODE is enabled on Band 0 without setting promiscuous matching
   in the Address CAM, non-matching BSSID control frames are filtered at the MAC
   CAM layer prior to reaching the R_AX_CTRL_FLTR routing logic.

For reference on our RTL8852AE test system (PCI ID [10ec:8852], HP subsystem
[103c:88e1]), the default register values on channel 5 after monitor mode entry
without our patch:
  R_AX_RX_FLTR_OPT (0xCE20): 0x00020000  (B_AX_SNIFFER_MODE set, CRC32 error dropped)
  R_AX_CTRL_FLTR   (0xCE24): 0x55555555
  R_AX_MGNT_FLTR   (0xCE28): 0x55555555
  R_AX_DATA_FLTR   (0xCE2C): 0x55555555

We will provide a formal register dump across channel hops in the v2 submission.

> I did similar steps with program written in C, but no problem.
> Could you share the simple Python raw socket script as attachment that you can
> reproduce NULL vif pointer in your side? (I think radiotap_null_data_frame above
> is a pseudo code).
> 
> Does every injection cause NULL vif? Or some under certain conditions?
> In my side, I have never seen that with raw_socket injection.

The difference comes down to how the monitor interface was instantiated:
- If a monitor interface is created as a virtual sub-interface alongside an
  existing station interface (e.g., `iw dev wlan0 interface add mon0 type monitor`),
  mac80211 may retain an associated sdata/vif reference.
- However, when the physical interface itself is switched directly into a pure,
  standalone monitor mode:
    # ip link set wlan0 down
    # iw dev wlan0 set type monitor
    # ip link set wlan0 up
  In this mode, net/mac80211/tx.c (ieee80211_monitor_start_xmit()) is called
  without an associated operational VIF. Consequently, mac80211 explicitly sets
  info->control.vif = NULL.

When rtw89_ops_tx() evaluates `vif_to_rtwvif(vif)` on an info->control.vif that
is NULL, `container_of(NULL, struct rtw89_vif, pub)` results in an invalid offset
pointer, and the subsequent check `if (rtwvif->offchan ...)` immediately triggers
a kernel NULL pointer dereference OOPS.

(A similar issue was previously identified and patched in drivers/net/wireless/mediatek/mt76).

Below is the standalone, minimal Python test script that reproduces the OOPS
consistently on unpatched kernels:

--------------------------------------------------------------------------------
#!/usr/bin/env python3
# reproduce_rtw89_null_vif.py
import socket, sys

IFACE = sys.argv[1] if len(sys.argv) > 1 else "wlan0"

# Standard 8-byte Radiotap header (no optional fields)
RADIOTAP_HDR = bytes([0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00])

# Minimal 802.11 Probe Request (Broadcast DA, Wildcard SSID)
DOT11_PROBE_REQ = bytes([
    0x40, 0x00,                         # Frame Control: Probe Request
    0x00, 0x00,                         # Duration
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # DA: Broadcast
    0x02, 0x00, 0x00, 0x00, 0x00, 0x01, # SA: Local MAC
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # BSSID: Wildcard
    0x00, 0x00,                         # Seq Control
    0x00, 0x00,                         # Tag: SSID (wildcard)
    0x01, 0x08,                         # Tag: Supported Rates (8 rates)
    0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24
])

sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0003))
sock.bind((IFACE, 0))
sock.send(RADIOTAP_HDR + DOT11_PROBE_REQ)
sock.close()
print("Packet injected successfully.")
--------------------------------------------------------------------------------

> We should confirm the TX queue will stop before vif removal. Otherwise,
> it might be racing.

You are completely right. Dereferencing rtwdev->pure_monitor_mode_vif without
synchronization introduces a race condition against interface removal.

For v2, we will guard this with RCU protection:
1. Wrap the lookup in rcu_read_lock() / rcu_dereference().
2. In rtw89_ops_remove_interface() / interface teardown, call
   netif_tx_stop_all_queues() and synchronize_net() before clearing and freeing
   the pure_monitor_mode_vif context.

> For the part 'rates[0].idx < 0', we might need to validate it by mac80211.
> Let's review this part afterward.

Agreed. We will cross-check mac80211 rate selection conventions for injected
frames lacking rate descriptors so this follows upstream best practices.

> I suppose these is for injection purpose, right? I mostly focus on RX side before,
> and need to think of TX side further. 
> I'll update my kernel to 7.1.5 to see if it can happen.

Yes, exactly. The scheduler initialization is specifically required for raw TX
injection.

Our reverse engineering of the RTL8852AE firmware (sec_0_EMEM binary) confirms
this mechanism:
- In firmware, function MACID_Pause_ApplyHW (at 0xb8991360) writes directly to
  hardware register R_AX_MACID_SLEEP_0 (0xC2C0) based on an internal scheduler
  pause mask (dword_B8E16E44).
- When a monitor interface is created without an associated AP or STA join, the
  firmware keeps the assigned mac_id marked paused.
- Calling rtw89_fw_h2c_join_info() with RTW89_SELF_ROLE_AP and RTW89_NET_TYPE_NO_LINK
  initializes the scheduler queues without enabling beacon generation or client
  beacon-loss watchdogs.
- Following this with rtw89_fw_h2c_macid_pause(..., false) and clearing the bit in
  R_AX_MACID_SLEEP_0 allows the hardware DMA engine to transmit queued descriptors.

We will prepare the updated v2 patchset incorporating the RCU lifecycle fix
and cleaner mac80211 rate validation for your review.

Best regards,
ph4ntom-rev <owner@ph4ntom-rev.uz>

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

* [PATCH v2 1/2] wifi: rtw89: fix RX filter configuration for monitor mode
  2026-11-02  9:00 ` ph4ntom-rev
  2026-11-03  0:54   ` Ping-Ke Shih
@ 2026-11-03 15:08   ` ph4ntom-rev
  2026-11-03 15:08     ` [PATCH v2 2/2] wifi: rtw89: support raw frame injection on standalone monitor interface ph4ntom-rev
  1 sibling, 1 reply; 7+ messages in thread
From: ph4ntom-rev @ 2026-11-03 15:08 UTC (permalink / raw)
  To: pkshih, linux-wireless; +Cc: kvalo, linux-kernel

In upstream rtw89, enabling monitor mode (IEEE80211_CONF_MONITOR)
configures basic B_AX_SNIFFER_MODE in R_AX_RX_FLTR_OPT. However, on
RTW89_CHIP_AX hardware (specifically RTL8852AE), Control frames (ACK,
RTS, CTS, BlockAck) and non-matching BSSID frames remain suppressed by
the hardware MAC Address CAM filters.

To ensure promiscuous capture of all 802.11 management, control, and
data frames:
1. Clear unicast, broadcast, and multicast CAM match drops and beacon
   BSSID checks in rx_fltr when IEEE80211_CONF_MONITOR is active.
2. Ensure power management frames are delivered by setting B_AX_A_PWR_MGNT.
3. Apply the adjusted filter across all active MAC indices (RTW89_MAC_0
   and RTW89_MAC_1 for DBCC) without clobbering rtwdev->hal.rx_fltr,
   allowing the default filter to be restored when leaving monitor mode.

Signed-off-by: ph4ntom-rev <owner@ph4ntom-rev.uz>
---
v1 -> v2:
 - Integrate RX filter adjustment directly into rtw89_ops_configure_filter()
   rather than maintaining a separate helper that overwrote hal.rx_fltr.
 - Configure both MAC 0 and MAC 1 (when DBCC is enabled) seamlessly.
 - Remove manual register writes to subtype filters and preserve standard
   filter restoration when exiting monitor mode.
 drivers/net/wireless/realtek/rtw89/mac80211.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 0672d13..33a6598 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -369,6 +369,15 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,
 		rx_fltr &= ~B_AX_A_A1_MATCH;
 	}
 
+	if (hw->conf.flags & IEEE80211_CONF_MONITOR) {
+		rx_fltr &= ~(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);
+		rx_fltr |= B_AX_A_PWR_MGNT;
+	}
+
 	rtw89_mac_set_rx_fltr(rtwdev, RTW89_MAC_0, rx_fltr);
 	if (!rtwdev->dbcc_en)
 		return;
-- 
2.53.0

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

* [PATCH v2 2/2] wifi: rtw89: support raw frame injection on standalone monitor interface
  2026-11-03 15:08   ` [PATCH v2 1/2] wifi: rtw89: fix RX filter configuration for monitor mode ph4ntom-rev
@ 2026-11-03 15:08     ` ph4ntom-rev
  0 siblings, 0 replies; 7+ messages in thread
From: ph4ntom-rev @ 2026-11-03 15:08 UTC (permalink / raw)
  To: pkshih, linux-wireless; +Cc: kvalo, linux-kernel

When injecting raw 802.11 Radiotap frames into a standalone monitor
interface (NL80211_IFTYPE_MONITOR brought up via 'iw set type monitor'),
net/mac80211/tx.c (ieee80211_monitor_start_xmit) transmits the frame
without an operational station context, passing info->control.vif = NULL.
Evaluating vif_to_rtwvif(vif) unconditionally in rtw89_ops_tx() triggers
a kernel NULL pointer dereference OOPS.

Furthermore, on hardware scheduler execution:
1. In rtw89_core_tx_update_injection(), handle rates[0].idx < 0 (omitted
   bitrate in Radiotap) by changing idx to signed s8, falling back to
   OFDM 6 Mbps (RTW89_HW_RATE_OFDM6) with use_rate and dis_data_fb set,
   and bounding retries when IEEE80211_TX_CTL_NO_ACK is set. Always enable
   en_wd_info for injected frames so txwd_info is built into the
   descriptor.
2. In rtw89_vif_type_mapping(), configure NL80211_IFTYPE_MONITOR with
   RTW89_NET_TYPE_NO_LINK and RTW89_SELF_ROLE_AP.
   In rtw89_ops_add_interface(), initialize default CMAC/DMAC tables via
   rtw89_chip_h2c_default_*mac_tbl, issue rtw89_fw_h2c_join_info(), and
   unpause the MACID via rtw89_mac_unpause_macid() across all 4 MACID
   sleep banks.
3. Protect pure_monitor_mode_vif with RCU: annotate with __rcu in core.h,
   guard rtw89_ops_tx() with rcu_read_lock(), and place rcu_assign_pointer
   and synchronize_rcu() at the very top of rtw89_ops_remove_interface()
   prior to teardown of links, CAM, and MACID resources to completely
   prevent Use-After-Free conditions. Disassociate firmware join state
   via rtw89_fw_h2c_join_info(..., true) on interface removal, and clear
   pure_monitor_mode_vif if a non-monitor interface is added.

Signed-off-by: ph4ntom-rev <owner@ph4ntom-rev.uz>
---
v1 -> v2:
 - Use struct rtw89_vif_link * in rtw89_chip_h2c_default_*mac_tbl() and
   rtw89_fw_h2c_join_info() to match modern MLO link abstractions.
 - Protect pure_monitor_mode_vif with RCU, wrap TX path in rcu_read_lock(),
   and use rtwvif_to_vif() helper instead of nonexistent struct member.
 - In rtw89_ops_remove_interface(), move rcu_assign_pointer(..., NULL) and
   synchronize_rcu() to the very top to prevent Use-After-Free during teardown.
 - Issue rtw89_fw_h2c_join_info(..., true) on monitor interface removal to
   cleanly disassociate firmware state.
 - Reset pure_monitor_mode_vif if another non-monitor interface is added.
 - Encapsulate sleep mask unpausing into rtw89_mac_unpause_macid() in mac.c
   covering all 4 register banks (R_AX_MACID_SLEEP_0..3) guarded by
   RTW89_CHIP_AX.
 - Use lockdep_assert_wiphy and rcu_dereference_protected() in
   rtw89_chip_rfk_channel_for_pure_mon_vif() to avoid context switch
   warnings in sleeping RF calibration routines.
 - Change rate index type to signed s8 in rtw89_core_tx_update_injection(),
   fall back to OFDM 6 Mbps, and enable en_wd_info for descriptor building.
 drivers/net/wireless/realtek/rtw89/core.c     | 24 ++++++++++--
 drivers/net/wireless/realtek/rtw89/core.h     |  2 +-
 drivers/net/wireless/realtek/rtw89/mac.c      | 11 ++++++
 drivers/net/wireless/realtek/rtw89/mac.h      |  1 +
 drivers/net/wireless/realtek/rtw89/mac80211.c | 39 +++++++++++++++++--
 5 files changed, 69 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 486c177..af31b94 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -474,7 +474,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 &&
@@ -499,9 +499,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 rtw89_vif *rtwvif;
 	struct rtw89_vif_link *rtwvif_link;
 
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+	rtwvif = rcu_dereference_protected(rtwdev->pure_monitor_mode_vif,
+					   lockdep_is_held(&rtwdev->hw->wiphy->mtx));
 	if (!rtwvif)
 		return;
 
@@ -1171,9 +1175,21 @@ 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->en_wd_info = true;
+	if (idx < 0) {
+		desc_info->data_rate = RTW89_HW_RATE_OFDM6;
+		desc_info->use_rate = true;
+		desc_info->dis_data_fb = true;
+		if (info->flags & IEEE80211_TX_CTL_NO_ACK) {
+			desc_info->tx_cnt_lmt_en = true;
+			desc_info->tx_cnt_lmt = 1;
+		}
+		return;
+	}
+
 	desc_info->use_rate = true;
 	desc_info->dis_data_fb = true;
 
@@ -5006,6 +5022,8 @@ void rtw89_vif_type_mapping(struct rtw89_vif_link *rtwvif_link, bool assoc)
 		rtwvif_link->addr_cam.sec_ent_mode = RTW89_ADDR_CAM_SEC_NORMAL;
 		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);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 05adf20..e844138 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -6318,7 +6318,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;
 
 	/* HCI related data, keep last */
 	u8 priv[] __aligned(sizeof(void *));
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 54aad37..58b0fe0 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -6240,6 +6240,17 @@ void rtw89_mac_flush_txq(struct rtw89_dev *rtwdev, u32 queues, bool drop)
 		rtw89_info(rtwdev, "timed out to flush queues\n");
 }
 
+void rtw89_mac_unpause_macid(struct rtw89_dev *rtwdev, u8 mac_id)
+{
+	u8 grp = mac_id >> 5;
+	u8 shift = mac_id & 0x1f;
+
+	rtw89_fw_h2c_macid_pause(rtwdev, shift, grp, false);
+
+	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX && grp < 4)
+		rtw89_write32_clr(rtwdev, R_AX_MACID_SLEEP_0 + grp * 4, BIT(shift));
+}
+
 int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex *coex)
 {
 	enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id;
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index 9db9ae2..587bcb6 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -1432,6 +1432,7 @@ void rtw89_mac_set_edcca_mode_bands(struct rtw89_dev *rtwdev, bool normal)
 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);
+void rtw89_mac_unpause_macid(struct rtw89_dev *rtwdev, u8 mac_id);
 int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex *coex);
 int rtw89_mac_coex_init_v1(struct rtw89_dev *rtwdev,
 			   const struct rtw89_mac_ax_coex *coex);
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 33a6598..3baa627 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -23,16 +23,30 @@ 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 rtw89_vif *rtwvif;
 	struct ieee80211_sta *sta = control->sta;
 	u32 flags = IEEE80211_SKB_CB(skb)->flags;
 	int ret, qsel;
 
+	rcu_read_lock();
+	if (!vif) {
+		rtwvif = rcu_dereference(rtwdev->pure_monitor_mode_vif);
+		if (!rtwvif) {
+			rcu_read_unlock();
+			ieee80211_free_txskb(hw, skb);
+			return;
+		}
+		vif = rtwvif_to_vif(rtwvif);
+	} 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);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -40,9 +54,11 @@ static void rtw89_ops_tx(struct ieee80211_hw *hw,
 	if (ret) {
 		rtw89_err(rtwdev, "failed to transmit skb: %d\n", ret);
 		ieee80211_free_txskb(hw, skb);
+		rcu_read_unlock();
 		return;
 	}
 	rtw89_core_tx_kick_off(rtwdev, qsel);
+	rcu_read_unlock();
 }
 
 static void rtw89_ops_wake_tx_queue(struct ieee80211_hw *hw,
@@ -221,8 +237,16 @@ 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;
+	if (vif->type == NL80211_IFTYPE_MONITOR) {
+		rtw89_chip_h2c_default_cmac_tbl(rtwdev, rtwvif_link, NULL);
+		rtw89_chip_h2c_default_dmac_tbl(rtwdev, rtwvif_link, NULL);
+		rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, false);
+		rtw89_mac_unpause_macid(rtwdev, rtwvif_link->mac_id);
+		rcu_assign_pointer(rtwdev->pure_monitor_mode_vif, rtwvif);
+	} else if (rcu_access_pointer(rtwdev->pure_monitor_mode_vif)) {
+		rcu_assign_pointer(rtwdev->pure_monitor_mode_vif, NULL);
+		synchronize_rcu();
+	}
 	rtw89_recalc_lps(rtwdev);
 	return 0;
 
@@ -248,6 +272,11 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
 
 	lockdep_assert_wiphy(hw->wiphy);
 
+	if (vif->type == NL80211_IFTYPE_MONITOR) {
+		rcu_assign_pointer(rtwdev->pure_monitor_mode_vif, NULL);
+		synchronize_rcu();
+	}
+
 	rtw89_debug(rtwdev, RTW89_DBG_STATE, "remove vif %pM type %d p2p %d\n",
 		    vif->addr, vif->type, vif->p2p);
 
@@ -261,6 +290,9 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,
 		goto bottom;
 	}
 
+	if (vif->type == NL80211_IFTYPE_MONITOR)
+		rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true);
+
 	__rtw89_ops_remove_iface_link(rtwdev, rtwvif_link);
 
 	rtw89_vif_unset_link(rtwvif, RTW89_VIF_IDLE_LINK_ID);
@@ -270,7 +302,6 @@ bottom:
 	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.53.0

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

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

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-01 11:20 [PATCH RFC net-next 0/2] wifi: rtw89: fix monitor mode RX filtering and enable raw packet injection ph4ntom-rev
2026-11-02  2:11 ` Ping-Ke Shih
2026-11-02  9:00 ` ph4ntom-rev
2026-11-03  0:54   ` Ping-Ke Shih
2026-11-03  2:44     ` ph4ntom-rev
2026-11-03 15:08   ` [PATCH v2 1/2] wifi: rtw89: fix RX filter configuration for monitor mode ph4ntom-rev
2026-11-03 15:08     ` [PATCH v2 2/2] wifi: rtw89: support raw frame injection on standalone monitor interface ph4ntom-rev

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