Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH v5 04/21] wifi: mt76: mt7925: populate EHT 320MHz MCS map in sta_rec
From: Sean Wang @ 2026-04-25 19:49 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi
  Cc: linux-wireless, linux-mediatek, Javier Tia, Marcin FM,
	Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
	Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
	Thibaut François, 张旭涵, Sean Wang
In-Reply-To: <20260425195011.790265-1-sean.wang@kernel.org>

From: Javier Tia <floss@jetm.me>

The sta_rec_eht structure has a mcs_map_bw320 field, and the channel
width mapping includes NL80211_CHAN_WIDTH_320, but the 320MHz MCS/NSS
map was never copied from the station's EHT capabilities to the MCU TLV.
This prevents negotiation of 320MHz channel width even when both the
hardware and firmware advertise support for it.

Add the missing memcpy for the 320MHz MCS map, matching the existing
pattern for BW20, BW80, and BW160.

Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Reviewed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 016c534f748c..f403d9d925e3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1667,6 +1667,7 @@ mt7925_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta)
 		memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, sizeof(eht->mcs_map_bw20));
 	memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80));
 	memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160));
+	memcpy(eht->mcs_map_bw320, &mcs_map->bw._320, sizeof(eht->mcs_map_bw320));
 }
 
 static void
-- 
2.43.0


^ permalink raw reply related

* [PATCH v5 03/21] wifi: mt76: mt7925: handle 320MHz bandwidth in RXV and TXS
From: Sean Wang @ 2026-04-25 19:49 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi
  Cc: linux-wireless, linux-mediatek, Javier Tia, Marcin FM,
	Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
	Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
	Thibaut François, 张旭涵, Sean Wang
In-Reply-To: <20260425195011.790265-1-sean.wang@kernel.org>

From: Javier Tia <floss@jetm.me>

The RX vector (RXV) and TX status (TXS) parsing in mac.c lack handling
for 320MHz channel width. When the hardware reports 320MHz in the
bandwidth field, mt7925_mac_fill_rx_rate() returns -EINVAL and
mt7925_mac_add_txs_skb() records no bandwidth stats.

Add IEEE80211_STA_RX_BW_320 cases to both functions. The RXV parser
also handles BW_320+1 since the hardware can report 320MHz in two
adjacent encoding positions.

Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Reviewed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
 drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index c47bd812b66b..d681005cc6ff 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -339,6 +339,11 @@ mt7925_mac_fill_rx_rate(struct mt792x_dev *dev,
 	case IEEE80211_STA_RX_BW_160:
 		status->bw = RATE_INFO_BW_160;
 		break;
+	/* RXV can report 320 in two positions */
+	case IEEE80211_STA_RX_BW_320:
+	case IEEE80211_STA_RX_BW_320 + 1:
+		status->bw = RATE_INFO_BW_320;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -997,6 +1002,10 @@ mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid,
 	stats->tx_mode[mode]++;
 
 	switch (FIELD_GET(MT_TXS0_BW, txs)) {
+	case IEEE80211_STA_RX_BW_320:
+		rate.bw = RATE_INFO_BW_320;
+		stats->tx_bw[4]++;
+		break;
 	case IEEE80211_STA_RX_BW_160:
 		rate.bw = RATE_INFO_BW_160;
 		stats->tx_bw[3]++;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v5 02/21] wifi: mt76: mt7925: add 320MHz bandwidth to bss_rlm_tlv
From: Sean Wang @ 2026-04-25 19:49 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi
  Cc: linux-wireless, linux-mediatek, Javier Tia,
	张旭涵, Marcin FM, Cristian-Florin Radoi,
	George Salukvadze, Evgeny Kapusta, Samu Toljamo, Ariel Rosenfeld,
	Chapuis Dario, Thibaut François, Sean Wang
In-Reply-To: <20260425195011.790265-1-sean.wang@kernel.org>

From: Javier Tia <floss@jetm.me>

bss_rlm_tlv() in mt7925_mcu_bss_rlm_tlv() has no case for
NL80211_CHAN_WIDTH_320. When associated to a 320MHz BSS, the switch
falls through to default and sends bw=0 (CMD_CBW_20MHZ) to firmware
via BSS_RLM TLV. Firmware then configures the RX radio for 20MHz
and cannot decode the AP's 320MHz frames, resulting in complete data
path failure at 320MHz.

Add the missing NL80211_CHAN_WIDTH_320 case with CMD_CBW_320MHZ and
center_chan2.

Tested on ASUS RT-BE92U: 320MHz throughput goes from 0 Mbps to
841 Mbps (iperf3 -t30 -P8), PHY 4803 Mbps EHT-MCS11.

Reported-by: 张旭涵 <Loong.0x00@gmail.com>
Closes: https://github.com/openwrt/mt76/issues/927
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Reviewed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 37cdf3e8a067..016c534f748c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -2334,6 +2334,10 @@ void mt7925_mcu_bss_rlm_tlv(struct sk_buff *skb, struct mt76_phy *phy,
 	case NL80211_CHAN_WIDTH_160:
 		req->bw = CMD_CBW_160MHZ;
 		break;
+	case NL80211_CHAN_WIDTH_320:
+		req->bw = CMD_CBW_320MHZ;
+		req->center_chan2 = ieee80211_frequency_to_channel(freq2);
+		break;
 	case NL80211_CHAN_WIDTH_5:
 		req->bw = CMD_CBW_5MHZ;
 		break;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v5 01/21] wifi: mt76: mt7925: fix stale pointer comparisons in change_vif_links
From: Sean Wang @ 2026-04-25 19:49 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi
  Cc: linux-wireless, linux-mediatek, Javier Tia, Marcin FM,
	Cristian-Florin Radoi, George Salukvadze, Evgeny Kapusta,
	Samu Toljamo, Ariel Rosenfeld, Chapuis Dario,
	Thibaut François, 张旭涵, Sean Wang
In-Reply-To: <20260425195011.790265-1-sean.wang@kernel.org>

From: Javier Tia <floss@jetm.me>

In the error path of mt7925_change_vif_links(), the free: label iterates
over link_ids to clean up, but compares against `mconf` and `mlink`
which hold stale values from the last loop iteration rather than the
current link_id being freed.

Use array-indexed access (mconfs[link_id] / mlinks[link_id]) to compare
against the correct per-link pointers.

Fixes: 69acd6d910b0 ("wifi: mt76: mt7925: add mt7925_change_vif_links")
Tested-by: Marcin FM <marcin@lgic.pl>
Tested-by: Cristian-Florin Radoi <radoi.chris@gmail.com>
Tested-by: George Salukvadze <giosal90@gmail.com>
Tested-by: Evgeny Kapusta <3193631@gmail.com>
Tested-by: Samu Toljamo <samu.toljamo@gmail.com>
Tested-by: Ariel Rosenfeld <ariel.rosenfeld.750@gmail.com>
Tested-by: Chapuis Dario <chapuisdario4@gmail.com>
Tested-by: Thibaut François <tibo@humeurlibre.fr>
Tested-by: 张旭涵 <Loong.0x00@gmail.com>
Reviewed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Javier Tia <floss@jetm.me>
---
 drivers/net/wireless/mediatek/mt76/mt7925/main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 73d3722739d0..4d8ca04a128b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -2179,9 +2179,9 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 		rcu_assign_pointer(mvif->link_conf[link_id], NULL);
 		rcu_assign_pointer(mvif->sta.link[link_id], NULL);
 
-		if (mconf != &mvif->bss_conf)
+		if (mconfs[link_id] != &mvif->bss_conf)
 			devm_kfree(dev->mt76.dev, mconfs[link_id]);
-		if (mlink != &mvif->sta.deflink)
+		if (mlinks[link_id] != &mvif->sta.deflink)
 			devm_kfree(dev->mt76.dev, mlinks[link_id]);
 	}
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v5 00/21] wifi: mt76: mt7925: MT7927 (Filogic 380) support
From: Sean Wang @ 2026-04-25 19:49 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang

This series adds support for MediaTek MT7927 (Filogic 380), a combo
WiFi 7 + BT 5.4 device, in the mt7925 driver.

Patches 1-5 add generic 320MHz EHT support without MT7927-specific
references.

Patches 6-9 add the initial MT7927 enablement pieces, including chip ID
helpers, firmware paths, per-chip IRQ map handling, and disabling ASPM
and runtime PM for MT7927.

Patches 10-21 add the remaining MT7927 support needed to make the device
usable, including connac3 helper cleanup, non-MLD link removal handling,
inactive BSS deactivation tolerance, WFSYS reset support, common DMA queue
helper refactoring, MT7927 PCIe DMA configuration, BSS band assignment
sync, MBMC event handling, CNM ops enablement, and PCIe/USB device support.

The follow-up MT7927 support patches are folded into this version so users
and testers can apply one complete series for MT7927 enablement.

Tested hardware:
- ASUS ROG Crosshair X870E Hero (BT 0489:e13a, WiFi 14c3:6639)
- ASUS ProArt X870E-Creator WiFi (BT 0489:e13a / 13d3:3588, WiFi 14c3:6639)
- ASUS ROG Strix X870-I (WiFi 14c3:7927)
- ASUS ROG Strix X870-F Gaming WiFi (BT 0489:e13a, WiFi 14c3:7927)
- ASUS ROG Strix X870E-E (BT 13d3:3588, WiFi 14c3:7927)
- ASUS ROG STRIX B850-E GAMING WIFI (BT 0489:e13a, WiFi 14c3:7927)
- Gigabyte X870E Aorus Master X3D (BT 0489:e10f, WiFi 14c3:7927)
- Gigabyte Z790 AORUS MASTER X (BT 0489:e10f, WiFi 14c3:7927)
- Gigabyte Z790 AORUS ELITE X WiFi7 (BT 0489:e10f, WiFi 14c3:7927)
- MSI MEG X870E ACE MAX (BT 0489:e110, WiFi 14c3:7927)
- Lenovo Legion Pro 7 16ARX9 (BT 0489:e0fa, WiFi 14c3:7927)
- Lenovo Legion Pro 7 16AFR10H (BT 0489:e0fa, WiFi 14c3:7927)
- TP-Link Archer TBE550E PCIe (BT 0489:e116, WiFi 14c3:7927)
- EDUP EP-MT7927BE M.2 card (WiFi 14c3:7927)
- Foxconn/Azurewave M.2 modules (WiFi 14c3:6639)
- AMD RZ738 reference design (WiFi 14c3:0738)

Tested on Arch Linux, CachyOS, EndeavourOS, Fedora (Bazzite), NixOS,
openSUSE Tumbleweed, and Ubuntu across kernels 6.13-6.19.

Changes since v4:
- Add Reviewed-by tags to patches 1-9.
- Add MT7927 follow-up support:
  - replace is_mt7925() with is_connac3()
  - use link-specific removal for non-MLD STA
  - tolerate inactive BSS deactivation
  - add MT7927 WFSYS reset support
  - factor out common DMA queue allocation
  - switch mt7925 DMA init to common mt792x queue helpers
  - add MT7927-specific PCIe DMA support
  - sync MT7927 BSS band assignment
  - add MBMC event handling
  - enable CNM ops for MT7927
  - add MT7927 PCIe support
  - add MT7927 USB support

Changes since v3:
- Dropped patches 9-13 (DMA, HW init, band_idx, PCI IDs). Sean Wang
  will submit these as part of a generic layer refactor. The CNM quirk
  (v3 patch 13 feedback) will be folded into mt792x_get_mac80211_ops()
  as part of that work.
- Fixed is_320mhz_supported() to use mt76_chip() directly instead of
  is_mt7927() which was not yet defined at that point (patch 5).
- Trimmed verbose inline comments in ASPM/PM patch per review feedback.
  Detail kept in commit message (patch 9).
- Added tested hardware: ASUS ROG STRIX B850-E GAMING WIFI.

Changes since v2 (suggested by Sean Wang):
- Fixed is_320mhz_supported() to check for MT7927 only, not the
  entire mt7925 family. MT7925 does not support 320MHz (patch 5).
- Dropped phy_cap_info[7] 320MHz additions (NON_OFDMA_UL_MU_MIMO
  and MU_BEAMFORMER) to keep capabilities conservative (patch 5).
- Disabled runtime PM for MT7927 (patch 9). The combo chip shares a
  CONNINFRA power domain between WiFi and BT; SET_OWN/CLR_OWN
  transitions crash BT firmware. Discovered via user reports of BT
  lockups after enabling power_save=1 (Reported-by: Nitin Gurram).

Changes since v1 (suggested by Sean Wang):
- Reorganized from 18 patches into 13 across 8 logical groups
- Common 320MHz patches first, chip-specific changes later
- Introduced mt792x_dma_config struct to reuse mt7925_dma_init()
- Replaced is_mt7927() with is_320mhz_supported() in common patches

Link to v4: https://lore.kernel.org/linux-wireless/20260326-mt7927-wifi-support-v4-v4-0-8ab465addcfe@jetm.me/
Link to v3: https://lore.kernel.org/linux-wireless/20260325-mt7927-wifi-support-v2-v3-0-826feb8fef8e@jetm.me/
Link to v2: https://lore.kernel.org/linux-wireless/20260319-mt7927-wifi-support-v2-v2-0-d627a7fad70d@jetm.me/
Link to v1: https://lore.kernel.org/linux-wireless/20260306-mt7927-wifi-support-v1-0-c77e7445511d@jetm.me/

Javier Tia (9):
  wifi: mt76: mt7925: fix stale pointer comparisons in change_vif_links
  wifi: mt76: mt7925: add 320MHz bandwidth to bss_rlm_tlv
  wifi: mt76: mt7925: handle 320MHz bandwidth in RXV and TXS
  wifi: mt76: mt7925: populate EHT 320MHz MCS map in sta_rec
  wifi: mt76: mt7925: advertise EHT 320MHz capabilities for 6GHz band
  wifi: mt76: mt7925: add MT7927 chip ID helpers
  wifi: mt76: mt7925: add MT7927 firmware paths
  wifi: mt76: mt7925: use irq_map for chip-specific interrupt handling
  wifi: mt76: mt7925: disable ASPM and runtime PM for MT7927

Sean Wang (12):
  wifi: mt76: connac: replace is_mt7925() with is_connac3()
  wifi: mt76: mt7925: use link-specific removal for non-MLD STA
  wifi: mt76: connac: tolerate inactive BSS deactivation
  wifi: mt76: mt792x: add MT7927 WFSYS reset support
  wifi: mt76: mt792x: factor out common DMA queue allocation
  wifi: mt76: mt7925: switch DMA init to common mt792x queue helpers
  wifi: mt76: mt792x: add MT7927-specific PCIe DMA support
  wifi: mt76: mt7925: sync MT7927 BSS band assignment
  wifi: mt76: mt7925: add MBMC event handling
  wifi: mt76: mt792x: enable CNM ops for MT7927
  wifi: mt76: mt7925: add MT7927 PCIe support
  wifi: mt76: mt7925: add MT7927 USB support

 .../net/wireless/mediatek/mt76/mt76_connac.h  |  16 ++
 .../wireless/mediatek/mt76/mt76_connac_mac.c  |   4 +-
 .../wireless/mediatek/mt76/mt76_connac_mcu.c  |  13 +-
 .../wireless/mediatek/mt76/mt76_connac_mcu.h  |  26 ++-
 .../net/wireless/mediatek/mt76/mt7925/init.c  |  12 +-
 .../net/wireless/mediatek/mt76/mt7925/mac.c   |   9 +
 .../net/wireless/mediatek/mt76/mt7925/main.c  |  80 ++++++-
 .../net/wireless/mediatek/mt76/mt7925/mcu.c   |  43 +++-
 .../net/wireless/mediatek/mt76/mt7925/mcu.h   |   3 +-
 .../wireless/mediatek/mt76/mt7925/mt7925.h    |  25 +++
 .../net/wireless/mediatek/mt76/mt7925/pci.c   | 133 +++++++++---
 .../wireless/mediatek/mt76/mt7925/pci_mac.c   |  12 +-
 .../net/wireless/mediatek/mt76/mt7925/usb.c   |  15 ++
 drivers/net/wireless/mediatek/mt76/mt792x.h   |  25 +++
 .../net/wireless/mediatek/mt76/mt792x_core.c  |  11 +
 .../net/wireless/mediatek/mt76/mt792x_dma.c   | 198 ++++++++++++++++--
 .../net/wireless/mediatek/mt76/mt792x_regs.h  |  23 ++
 .../net/wireless/mediatek/mt76/mt792x_usb.c   |   2 +-
 drivers/net/wireless/mediatek/mt76/usb.c      |   1 +
 19 files changed, 578 insertions(+), 73 deletions(-)


base-commit: e8c819df02436f2c2379766946735e1f06a7c923
-- 
2.43.0


^ permalink raw reply

* [PATCH rtw-next] wifi: rtl8xxxu: Detect the maximum supported channel width
From: Bitterblue Smith @ 2026-04-25 19:38 UTC (permalink / raw)
  To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih, Jes Sorensen, art1310

Some devices malfunction when connected to a network with 40 MHz channel
width, because they don't support that.

RTL8188FU, RTL8192FU, and RTL8710BU (RTL8188GU) have a way to signal
this (and some other capabilities) to the driver. Get this information
from the hardware and advertise 40 MHz support only when the hardware
can handle it. We assume the other chips can always handle it.

RTL8710BU needs a different way to retrieve this information, which will
be implemented some other time.

Fixes: dbf9b7bb0edf ("wifi: rtl8xxxu: Enable 40 MHz width by default")
Cc: stable@vger.kernel.org
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221394
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
 drivers/net/wireless/realtek/rtl8xxxu/8188e.c |  1 +
 drivers/net/wireless/realtek/rtl8xxxu/8188f.c |  1 +
 drivers/net/wireless/realtek/rtl8xxxu/8192c.c |  1 +
 drivers/net/wireless/realtek/rtl8xxxu/8192e.c |  1 +
 drivers/net/wireless/realtek/rtl8xxxu/8192f.c |  1 +
 drivers/net/wireless/realtek/rtl8xxxu/8710b.c |  1 +
 drivers/net/wireless/realtek/rtl8xxxu/8723a.c |  1 +
 drivers/net/wireless/realtek/rtl8xxxu/8723b.c |  1 +
 drivers/net/wireless/realtek/rtl8xxxu/core.c  | 64 +++++++++++++++++--
 drivers/net/wireless/realtek/rtl8xxxu/regs.h  |  2 +
 .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  7 ++
 11 files changed, 76 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
index 93f11ea26b90..3f3fe5b1d486 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
@@ -1867,6 +1867,7 @@ struct rtl8xxxu_fileops rtl8188eu_fops = {
 	.has_tx_report = 1,
 	.init_reg_pkt_life_time = 1,
 	.gen2_thermal_meter = 1,
+	.hw_feature_report = 0,
 	.max_sec_cam_num = 32,
 	.adda_1t_init = 0x0b1b25a0,
 	.adda_1t_path_on = 0x0bdb25a0,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c
index 13a6d9b22edb..4aac2a598ba7 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c
@@ -1746,6 +1746,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = {
 	.init_reg_rxfltmap = 1,
 	.init_reg_pkt_life_time = 1,
 	.init_reg_hmtfr = 1,
+	.hw_feature_report = 1,
 	.ampdu_max_time = 0x70,
 	.ustime_tsf_edca = 0x28,
 	.max_aggr_num = 0x0c14,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
index cbf72fa317d3..56657051c82e 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
@@ -724,6 +724,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = {
 	.tx_desc_size = sizeof(struct rtl8xxxu_txdesc32),
 	.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16),
 	.supports_ap = 1,
+	.hw_feature_report = 0,
 	.max_macid_num = 32,
 	.max_sec_cam_num = 32,
 	.adda_1t_init = 0x0b1b25a0,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c
index bc2c5c07c4de..6e6ccd23660c 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c
@@ -1752,6 +1752,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = {
 	.has_s0s1 = 0,
 	.gen2_thermal_meter = 1,
 	.needs_full_init = 1,
+	.hw_feature_report = 0,
 	.supports_ap = 1,
 	.max_macid_num = 128,
 	.max_sec_cam_num = 64,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c
index 94cb5809bed0..57c902f2c2de 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c
@@ -2075,6 +2075,7 @@ struct rtl8xxxu_fileops rtl8192fu_fops = {
 	.init_reg_rxfltmap = 1,
 	.init_reg_pkt_life_time = 1,
 	.init_reg_hmtfr = 1,
+	.hw_feature_report = 1,
 	.ampdu_max_time = 0x5e,
 	.ustime_tsf_edca = 0x50,
 	.max_aggr_num = 0x1f1f,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c
index 5bf04ea82f6f..43804677349e 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c
@@ -1853,6 +1853,7 @@ struct rtl8xxxu_fileops rtl8710bu_fops = {
 	.init_reg_rxfltmap = 1,
 	.init_reg_pkt_life_time = 1,
 	.init_reg_hmtfr = 1,
+	.hw_feature_report = 0, /* TODO, it's different */
 	.ampdu_max_time = 0x5e,
 	/*
 	 * The RTL8710BU vendor driver uses 0x50 here and it works fine,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c
index 303f89395952..621703d8ae2d 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c
@@ -633,6 +633,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = {
 	.rx_agg_buf_size = 16000,
 	.tx_desc_size = sizeof(struct rtl8xxxu_txdesc32),
 	.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16),
+	.hw_feature_report = 0,
 	.max_sec_cam_num = 32,
 	.adda_1t_init = 0x0b1b25a0,
 	.adda_1t_path_on = 0x0bdb25a0,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c
index bb747420ff7e..52b0e90338ef 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c
@@ -1747,6 +1747,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = {
 	.gen2_thermal_meter = 1,
 	.needs_full_init = 1,
 	.init_reg_hmtfr = 1,
+	.hw_feature_report = 0,
 	.ampdu_max_time = 0x5e,
 	.ustime_tsf_edca = 0x50,
 	.max_aggr_num = 0x0c14,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 2c56d7df92a7..506633b3b51c 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -14,6 +14,7 @@
  */
 
 #include <linux/firmware.h>
+#include <linux/iopoll.h>
 #include "regs.h"
 #include "rtl8xxxu.h"
 
@@ -3936,6 +3937,46 @@ static inline u8 rtl8xxxu_get_macid(struct rtl8xxxu_priv *priv,
 	return sta_info->macid;
 }
 
+static void rtl8xxxu_request_hw_feature(struct rtl8xxxu_priv *priv)
+{
+	if (!priv->fops->hw_feature_report)
+		return;
+
+	rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, C2H_HW_FEATURE_DUMP);
+}
+
+static int rtl8xxxu_dump_hw_feature(struct rtl8xxxu_priv *priv)
+{
+	static const u8 bw_map[8] = { 0, 0, 160, 5, 10, 20, 40, 80 };
+	struct rtl8xxxu_hw_feature *hw_feature = &priv->hw_feature;
+	u8 feature[13];
+	int i, ret;
+	u8 id, bw;
+
+	if (!priv->fops->hw_feature_report) {
+		hw_feature->max_bw = 40;
+		return 0;
+	}
+
+	ret = read_poll_timeout(rtl8xxxu_read8, id,
+				id == C2H_HW_FEATURE_REPORT,
+				10000, 800000, false,
+				priv, REG_C2HEVT_MSG_NORMAL);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(feature); i++)
+		feature[i] = rtl8xxxu_read8(priv, REG_C2HEVT_MSG_NORMAL + 2 + i);
+
+	rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, 0);
+
+	bw = u8_get_bits(feature[6], GENMASK(2, 0));
+
+	hw_feature->max_bw = bw_map[bw];
+
+	return 0;
+}
+
 static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 {
 	struct rtl8xxxu_priv *priv = hw->priv;
@@ -3982,6 +4023,8 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 	 */
 	rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary);
 
+	rtl8xxxu_request_hw_feature(priv);
+
 	for (int retry = 5; retry >= 0 ; retry--) {
 		ret = rtl8xxxu_download_firmware(priv);
 		dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret);
@@ -3997,6 +4040,12 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 	if (ret)
 		goto exit;
 
+	ret = rtl8xxxu_dump_hw_feature(priv);
+	if (ret) {
+		dev_err(dev, "failed to dump hw feature\n");
+		goto exit;
+	}
+
 	if (fops->phy_init_antenna_selection)
 		fops->phy_init_antenna_selection(priv);
 
@@ -7876,15 +7925,20 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
 	sband->ht_cap.ht_supported = true;
 	sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
 	sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
-	sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-			    IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+	sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20;
+
+	if (priv->hw_feature.max_bw >= 40) {
+		sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+		sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+	} else {
+		dev_info(&udev->dev, "hardware doesn't support HT40\n");
+	}
+
 	memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs));
 	sband->ht_cap.mcs.rx_mask[0] = 0xff;
 	sband->ht_cap.mcs.rx_mask[4] = 0x01;
-	if (priv->rf_paths > 1) {
+	if (priv->rf_paths > 1)
 		sband->ht_cap.mcs.rx_mask[1] = 0xff;
-		sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
-	}
 	sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
 
 	hw->wiphy->bands[NL80211_BAND_2GHZ] = sband;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/regs.h b/drivers/net/wireless/realtek/rtl8xxxu/regs.h
index 0741db8d08bf..eafaef1438b9 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/regs.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/regs.h
@@ -447,6 +447,8 @@
 /* 8188EU */
 #define REG_32K_CTRL			0x0194
 #define REG_C2HEVT_MSG_NORMAL		0x01a0
+#define C2H_HW_FEATURE_REPORT		0x19
+#define C2H_HW_FEATURE_DUMP		0xfd
 /* 8192EU/8723BU/8812 */
 #define REG_C2HEVT_CMD_ID_8723B		0x01ae
 #define REG_C2HEVT_CLEAR		0x01af
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 188f4bbe99cd..b667e5d413ac 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -1790,11 +1790,17 @@ struct rtl8xxxu_cfo_tracking {
 #define RTL8XXXU_MAX_SEC_CAM_NUM	64
 #define RTL8XXXU_SEC_DEFAULT_KEY_NUM	4
 
+struct rtl8xxxu_hw_feature {
+	u8 max_bw;
+};
+
 struct rtl8xxxu_priv {
 	struct ieee80211_hw *hw;
 	struct usb_device *udev;
 	struct rtl8xxxu_fileops *fops;
 
+	struct rtl8xxxu_hw_feature hw_feature;
+
 	spinlock_t tx_urb_lock;
 	struct list_head tx_urb_free_list;
 	int tx_urb_free_count;
@@ -2011,6 +2017,7 @@ struct rtl8xxxu_fileops {
 	u8 init_reg_pkt_life_time:1;
 	u8 init_reg_hmtfr:1;
 	u8 supports_concurrent:1;
+	u8 hw_feature_report:1;
 	u8 ampdu_max_time;
 	u8 ustime_tsf_edca;
 	u16 max_aggr_num;
-- 
2.53.0


^ permalink raw reply related

* [PATCH rtw-next] wifi: rtlwifi: rtl8821ae: Fix C2H bit location in RX descriptor
From: Bitterblue Smith @ 2026-04-25 19:32 UTC (permalink / raw)
  To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih

Bit 28 of double word 2 in the RX descriptor indicates if the packet is
a normal 802.11 frame, or a message from the wifi firmware to the
driver (Card 2 Host).

Commit f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation
macros") mistakenly made the driver look for this bit in double word 1,
causing packet loss and Bluetooth coexistence problems.

Fixes: f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros")
Cc: <stable@vger.kernel.org>
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
 drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h
index 0609039b5322..17d37433e312 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h
@@ -291,7 +291,7 @@ static inline int get_rx_desc_paggr(__le32 *__pdesc)
 
 static inline int get_rx_status_desc_rpt_sel(__le32 *__pdesc)
 {
-	return le32_get_bits(*(__pdesc + 1), BIT(28));
+	return le32_get_bits(*(__pdesc + 2), BIT(28));
 }
 
 static inline int get_rx_desc_rxmcs(__le32 *__pdesc)
-- 
2.53.0


^ permalink raw reply related

* [PATCH] wifi: mt76: mt7921u: add MT7902 USB support
From: Sean Wang @ 2026-04-25 16:09 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang

From: Sean Wang <sean.wang@mediatek.com>

Add the 0e8d:7902 USB ID and select the MT7902 WM firmware.  Use the
same USB queue mapping as mt7921/mt7925 so MT7902U can bind and probe
through mt7921u driver.

Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
 drivers/net/wireless/mediatek/mt76/mt7921/usb.c | 2 ++
 drivers/net/wireless/mediatek/mt76/usb.c        | 1 +
 2 files changed, 3 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
index 17057e68bf21..d86969a096ac 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
@@ -13,6 +13,8 @@
 #include "../mt76_connac2_mac.h"
 
 static const struct usb_device_id mt7921u_device_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7902, 0xff, 0xff, 0xff),
+		.driver_info = (kernel_ulong_t)MT7902_FIRMWARE_WM },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff),
 		.driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM },
 	/* Comfast CF-952AX */
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index 632ae755c7a6..d16439416891 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -925,6 +925,7 @@ mt76u_ac_to_hwq(struct mt76_dev *dev, struct mt76_queue *q, u8 qid)
 		q->ep = q->hw_idx + 1;
 		break;
 	}
+	case 0x7902:
 	case 0x7961:
 	case 0x7925:
 		q->hw_idx = mt76_ac_to_hwq(ac);

base-commit: e8c819df02436f2c2379766946735e1f06a7c923
-- 
2.43.0


^ permalink raw reply related

* [PATCH 3/3] wifi: mt76: mt7925: program BA state on active links
From: sean.wang @ 2026-04-25 15:47 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi
  Cc: linux-wireless, linux-mediatek, Sean Wang, Yao Ting Hsieh
In-Reply-To: <20260425154721.738101-1-sean.wang@kernel.org>

From: Sean Wang <sean.wang@mediatek.com>

With MLO, traffic for one TID can be sent on any active link. Programming
BA state only on the default link leaves the other active links out of
sync.

Program BA state on all active links instead.

Fixes: 766ea2cf5a39 ("Revert "wifi: mt76: mt7925: Update mt7925_mcu_uni_[tx,rx]_ba for MLO"")
Tested-by: Yao Ting Hsieh <yao-ting.hsieh@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
 .../net/wireless/mediatek/mt76/mt7925/main.c  | 10 +--
 .../net/wireless/mediatek/mt76/mt7925/mcu.c   | 67 ++++++++++++++-----
 .../wireless/mediatek/mt76/mt7925/mt7925.h    |  4 +-
 3 files changed, 59 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 73d3722739d0..7c20fd9522cf 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1372,22 +1372,22 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	case IEEE80211_AMPDU_RX_START:
 		mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn,
 				   params->buf_size);
-		mt7925_mcu_uni_rx_ba(dev, params, true);
+		mt7925_mcu_uni_rx_ba(dev, params, vif, true);
 		break;
 	case IEEE80211_AMPDU_RX_STOP:
 		mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid);
-		mt7925_mcu_uni_rx_ba(dev, params, false);
+		mt7925_mcu_uni_rx_ba(dev, params, vif, false);
 		break;
 	case IEEE80211_AMPDU_TX_OPERATIONAL:
 		mtxq->aggr = true;
 		mtxq->send_bar = false;
-		mt7925_mcu_uni_tx_ba(dev, params, true);
+		mt7925_mcu_uni_tx_ba(dev, params, vif, true);
 		break;
 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
 		mtxq->aggr = false;
 		clear_bit(tid, &msta->deflink.wcid.ampdu_state);
-		mt7925_mcu_uni_tx_ba(dev, params, false);
+		mt7925_mcu_uni_tx_ba(dev, params, vif, false);
 		break;
 	case IEEE80211_AMPDU_TX_START:
 		set_bit(tid, &msta->deflink.wcid.ampdu_state);
@@ -1396,7 +1396,7 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	case IEEE80211_AMPDU_TX_STOP_CONT:
 		mtxq->aggr = false;
 		clear_bit(tid, &msta->deflink.wcid.ampdu_state);
-		mt7925_mcu_uni_tx_ba(dev, params, false);
+		mt7925_mcu_uni_tx_ba(dev, params, vif, false);
 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 		break;
 	}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index c720d21c1727..b6f7e4a94f57 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -651,32 +651,69 @@ mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif,
 				     MCU_UNI_CMD(STA_REC_UPDATE), true);
 }
 
-/** starec & wtbl **/
 int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev,
 			 struct ieee80211_ampdu_params *params,
-			 bool enable)
+			 struct ieee80211_vif *vif, bool enable)
 {
-	struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv;
-	struct mt792x_vif *mvif = msta->vif;
+	struct ieee80211_sta *sta = params->sta;
+	struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+	struct ieee80211_link_sta *link_sta;
+	unsigned int link_id;
+
+	for_each_sta_active_link(vif, sta, link_sta, link_id) {
+		struct mt792x_link_sta *mlink;
+		struct mt792x_bss_conf *mconf;
+		int ret;
+
+		mlink = mt792x_sta_to_link(msta, link_id);
+		if (!mlink)
+			return -EINVAL;
 
-	if (enable && !params->amsdu)
-		msta->deflink.wcid.amsdu = false;
+		mconf = mt792x_vif_to_link(msta->vif, link_id);
+		if (!mconf)
+			return -EINVAL;
+
+		if (enable && !params->amsdu)
+			mlink->wcid.amsdu = false;
 
-	return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params,
-				 &msta->deflink.wcid,
-				 enable, true);
+		ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, params,
+					&mlink->wcid, enable, true);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
 }
 
 int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
 			 struct ieee80211_ampdu_params *params,
-			 bool enable)
+			 struct ieee80211_vif *vif, bool enable)
 {
-	struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv;
-	struct mt792x_vif *mvif = msta->vif;
+	struct ieee80211_sta *sta = params->sta;
+	struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+	struct ieee80211_link_sta *link_sta;
+	unsigned int link_id;
+
+	for_each_sta_active_link(vif, sta, link_sta, link_id) {
+		struct mt792x_link_sta *mlink;
+		struct mt792x_bss_conf *mconf;
+		int ret;
+
+		mlink = mt792x_sta_to_link(msta, link_id);
+		if (!mlink)
+			return -EINVAL;
 
-	return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params,
-				 &msta->deflink.wcid,
-				 enable, false);
+		mconf = mt792x_vif_to_link(msta->vif, link_id);
+		if (!mconf)
+			return -EINVAL;
+
+		ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, params,
+					&mlink->wcid, enable, false);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
 }
 
 static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 46b480f7d813..01d033fb1433 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -293,10 +293,10 @@ int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev,
 				 bool enable);
 int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev,
 			 struct ieee80211_ampdu_params *params,
-			 bool enable);
+			 struct ieee80211_vif *vif, bool enable);
 int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
 			 struct ieee80211_ampdu_params *params,
-			 bool enable);
+			 struct ieee80211_vif *vif, bool enable);
 void mt7925_mlo_pm_work(struct work_struct *work);
 void mt7925_scan_work(struct work_struct *work);
 void mt7925_roc_work(struct work_struct *work);
-- 
2.43.0


^ permalink raw reply related

* [PATCH 2/3] wifi: mt76: mt7925: pass WCID explicitly to mt7925_mcu_sta_ba()
From: sean.wang @ 2026-04-25 15:47 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi
  Cc: linux-wireless, linux-mediatek, Sean Wang, Yao Ting Hsieh
In-Reply-To: <20260425154721.738101-1-sean.wang@kernel.org>

From: Sean Wang <sean.wang@mediatek.com>

mt7925_mcu_sta_ba() currently hides the BA target behind an implicit WCID
lookup.

Pass the WCID explicitly so the caller controls which BA context is being
programmed.

No functional change intended.

Tested-by: Yao Ting Hsieh <yao-ting.hsieh@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 37cdf3e8a067..c720d21c1727 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -624,9 +624,8 @@ void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb)
 static int
 mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif,
 		  struct ieee80211_ampdu_params *params,
-		  bool enable, bool tx)
+		  struct mt76_wcid *wcid, bool enable, bool tx)
 {
-	struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv;
 	struct sta_rec_ba_uni *ba;
 	struct sk_buff *skb;
 	struct tlv *tlv;
@@ -664,6 +663,7 @@ int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev,
 		msta->deflink.wcid.amsdu = false;
 
 	return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params,
+				 &msta->deflink.wcid,
 				 enable, true);
 }
 
@@ -675,6 +675,7 @@ int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev,
 	struct mt792x_vif *mvif = msta->vif;
 
 	return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params,
+				 &msta->deflink.wcid,
 				 enable, false);
 }
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH 1/3] wifi: mt76: mt7925: keep TX BA state in the primary WCID
From: sean.wang @ 2026-04-25 15:47 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi
  Cc: linux-wireless, linux-mediatek, Sean Wang, Yao Ting Hsieh

From: Sean Wang <sean.wang@mediatek.com>

For MLO, the same TID can run over different links. Keeping TX BA state in
a link WCID makes the state depend on which link starts aggregation first.

Store it in the primary WCID instead, so the BA state stays stable across
links.

Fixes: 44eb173bdd4f ("wifi: mt76: mt7925: add link handling in mt7925_txwi_free")
Tested-by: Yao Ting Hsieh <yao-ting.hsieh@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
 drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index c47bd812b66b..f025c0efeda2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -841,7 +841,6 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb,
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_link_sta *link_sta;
-	struct mt792x_link_sta *mlink;
 	struct mt792x_sta *msta;
 	bool is_8023;
 	u16 fc, tid;
@@ -880,14 +879,14 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb,
 
 	msta = (struct mt792x_sta *)sta->drv_priv;
 
-	if (sta->mlo && msta->deflink_id != IEEE80211_LINK_UNSPECIFIED)
-		mlink = rcu_dereference(msta->link[msta->deflink_id]);
-	else
-		mlink = &msta->deflink;
-
-	if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state)) {
+	/* Packets belonging to the same TID can be transmitted over multiple
+	 * links. Keep the TX BA session state in the primary link so all links
+	 * share the same AMPDU bookkeeping.
+	 */
+	if (!test_and_set_bit(tid, &msta->deflink.wcid.ampdu_state)) {
 		if (ieee80211_start_tx_ba_session(sta, tid, 0))
-			clear_bit(tid, &mlink->wcid.ampdu_state);
+			clear_bit(tid, &msta->deflink.wcid.ampdu_state);
+
 	}
 }
 

base-commit: e8c819df02436f2c2379766946735e1f06a7c923
-- 
2.43.0


^ permalink raw reply related

* [PATCH] wifi: mt76: mt792x: skip MLD header rewrite for 802.3 encap TX
From: Sean Wang @ 2026-04-25 14:46 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi; +Cc: linux-wireless, linux-mediatek, Sean Wang

From: Sean Wang <sean.wang@mediatek.com>

mt792x_tx() rewrites addr1/addr2/addr3 by treating skb->data as
an 802.11 header for MLD traffic.

That is only valid for native 802.11 frames. Direct 802.3 TX can also
reach this path with IEEE80211_TX_CTL_HW_80211_ENCAP set, where
skb->data is not an 802.11 header.

Skip the MLD header rewrite for HW-encap packets to avoid corrupting
802.3 frame contents.

Fixes: ebb1406813c6 ("wifi: mt76: mt7925: add link handling to txwi")
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
 drivers/net/wireless/mediatek/mt76/mt792x_core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index 152cfcca2f90..157e48e33412 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -105,7 +105,8 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
 		wcid = &mvif->sta.deflink.wcid;
 	}
 
-	if (vif && control->sta && ieee80211_vif_is_mld(vif)) {
+	if (vif && control->sta && ieee80211_vif_is_mld(vif) &&
+	    !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) {
 		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 		struct ieee80211_link_sta *link_sta;
 		struct ieee80211_bss_conf *conf;

base-commit: e8c819df02436f2c2379766946735e1f06a7c923
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] wifi: mac80211: restore monitor injection when coexisting with another VIF
From: Brite @ 2026-04-25  2:43 UTC (permalink / raw)
  To: Lachlan Hodges
  Cc: Johannes Berg, linux-wireless, linux-kernel, stable, fjhhz1997,
	oscar.alfonso.diaz
In-Reply-To: <4i5nyqdrtpgm575dd3swyp7662wjdxu3hky7ucgwnuwigx5ge4@tc474ip5qxtc>



On April 25, 2026 1:47:28 PM GMT+12:00, Lachlan Hodges <lachlan.hodges@morsemicro.com> wrote:
>Hi,
>
>I will leave implementation discussion to Johannes, but I have some
>generic feedback;
>
Thanks for the feedback and now i know why the code was flagged as llm created. My approach to finding the vm freeze issue followed by the 5ghz deauth not working, was done using debug prints everywhere possible, with added delays between function calls(the delay was added because the vm froze otherwise, without any dmesg logs). Since I didn't have the proper knowledge, the fixes i tried initially (spread across 6 or 7 files) led to other issues, intermittent failures etc. Everything was done inside a kali VM with no comments, full of messy code, not using git commits to revert etc. i had to start from scratch but then i added comments alongside. Even though the initial patch fixed every issue, being too invasive, I tried to trim down as much as I could which landed the sole chandef and then the 5ghz patch. I didn't pay attention to improving the comments when removing code sections. I also had very limited time to spare for this and my intention as I said in the airgeddon discord channel was to send a cleaned up code to the kernel devs so that they could get a hint at what the issue is and come up with a proper fix. The commit message is what i summed up from doing all my research and testing. I didn't know the format to submit a patch, so i used information from AI, Google, previous threads/replies etc here to submit an email. I didn't check if AI changed any comments. 
As I mentioned earlier, a community had been waiting for so long to have this issue fixed. My sole intention was to find anything that helps with resolving this. I've also packaged 6.18, 6.19 and 7.0 with the patch and uploaded it for the users now but as Oscar said the proper way would be a fix in the upstream and backporting it.
If v2 patch by Johannes(no need for sole_chandef) + 5ghz patch from me fixes the whole issue(I've tested this today) please look into improving it and providing a fix.
Thanks

^ permalink raw reply

* Re: [PATCH] wifi: mac80211: restore monitor injection when coexisting with another VIF
From: Lachlan Hodges @ 2026-04-25  1:47 UTC (permalink / raw)
  To: Brite
  Cc: Johannes Berg, linux-wireless, linux-kernel, stable, fjhhz1997,
	oscar.alfonso.diaz
In-Reply-To: <20260424120807.25005-1-brite.airgeddon@gmail.com>

Hi,

I will leave implementation discussion to Johannes, but I have some
generic feedback;

> + * Context: Must be called with wiphy->mtx held.
> + *          Always process context - GFP_KERNEL is safe and appropriate.

This comment seems redundant given the lockdep assert?

> + */
> +void ieee80211_update_sole_chandef(struct ieee80211_local *local)
> +{
> +	struct ieee80211_chanctx      *ctx, *found = NULL;
> +	struct ieee80211_sole_chandef *snap = NULL;
> +	struct ieee80211_sole_chandef *old;

Don't align the local names i.e

	struct ieee80211_chanctx *ctx, *found = NULL;
	struct ieee80211_sole_chandef *snap = NULL;
	...

> +	if (found) {
> +		snap = kmalloc(sizeof(*snap), GFP_KERNEL);
> +		if (snap)
> +			snap->def = found->conf.def;
> +		/* alloc failure -> snap == NULL -> publish NULL below */

Same here - this comment adds no value

>  struct ieee80211_chanctx_user_iter {
>  	struct ieee80211_chan_req *chanreq;
>  	struct ieee80211_sub_if_data *sdata;
> @@ -729,6 +784,9 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,
>  				     const struct ieee80211_chan_req *chanreq)
>  {
>  	_ieee80211_change_chanctx(local, ctx, old_ctx, chanreq, NULL);
> +
> +	/* Hook 4/4: channel parameters changed; refresh snapshot */
> +	ieee80211_update_sole_chandef(local);

Without the context of this patch, there is no way to understand
what this is doing (same with 3/4). The comment doesn't help and
the general idea of a "sole chandef" seems strange. hw.conf.chandef
is also a sole chandef?

> +/* Defined in chan.c */
> +void ieee80211_update_sole_chandef(struct ieee80211_local *local);

Another comment not needed
 
> +	/*
> +	 * All interfaces are gone by this point, so every chanctx has been
> +	 * freed and ieee80211_update_sole_chandef() has already published
> +	 * NULL. Assert the invariant.
> +	 */
> +	WARN_ON_ONCE(rcu_access_pointer(local->sole_chandef));

Seems unnecessary?
 
> -	if (chanctx_conf)
> +	if (chanctx_conf) {
>  		chandef = &chanctx_conf->def;
> -	else
> -		goto fail_rcu;
> +	} else {
> +		/*
> +		 * Real-chanctx drivers (e.g. mt76) do not assign a chanctx to
> +		 * the monitor VIF, so vif.bss_conf.chanctx_conf is NULL here.
> +		 * Fall back to the sole_chandef snapshot maintained by
> +		 * ieee80211_update_sole_chandef(). NULL means MCC or no active
> +		 * channel - drop the frame.
> +		 *
> +		 * The snapshot is valid for this whole function: it is freed
> +		 * via kfree_rcu() after a full grace period, and we are inside
> +		 * rcu_read_lock() throughout.
> +		 */

is the bottom half of this comment really needed?

> +		struct ieee80211_sole_chandef *sole =
> +			rcu_dereference(local->sole_chandef);
> +		chandef = sole ? &sole->def : NULL;

nit: space after local declaration

> +		list_for_each_entry_rcu(tmp_sdata, &local->interfaces, list) {
> +			struct ieee80211_chanctx_conf *tx_conf;
> +
> +			if (!ieee80211_sdata_running(tmp_sdata))
> +				continue;
> +			if (tmp_sdata->vif.type == NL80211_IFTYPE_MONITOR ||
> +			    tmp_sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
> +				continue;
> +
> +			tx_conf = rcu_dereference(tmp_sdata->vif.bss_conf.chanctx_conf);
> +

nit: remove this space after tx_conf = ..

lachlan

^ permalink raw reply

* Re: [PATCH] wifi: mac80211: restore monitor injection when coexisting with another VIF
From: Brite @ 2026-04-25  0:34 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless, linux-kernel, stable, fjhhz1997,
	oscar.alfonso.diaz
In-Reply-To: <9f7df38831598001ac6cd79ab4fb95b4b6e042fd.camel@sipsolutions.net>



On April 25, 2026 1:55:46 AM GMT+12:00, Johannes Berg <johannes@sipsolutions.net> wrote:
>I don't believe that all this complexity is necessary, and the code
>changes have are fairly clearly LLM-created w/o such disclosures.
>Dropping.
>
If it helps in any way - just tested your v2 patch which causes VM freeze but adding the 5ghz surrogate patch solves the freeze and also the issue with 5ghz deauth in ap/monitor mode coexistence. Tested working on 2.4/5ghz standalone and 2.4/5ghz ap/coexistence mode using ath9k_htc and mt7921u. Also tested side by side ap/deauth coexistence mode running evil twin attack using airgeddon multi instance mode including channel change monitor and no issues at all.

Brite

^ permalink raw reply

* Re: [PATCH 1/9] bitfield: add FIELD_GET_SIGNED()
From: David Laight @ 2026-04-24 21:37 UTC (permalink / raw)
  To: Yury Norov
  Cc: Johannes Berg, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Andy Lutomirski, Peter Zijlstra,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Ping-Ke Shih, Richard Cochran, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Alexandre Belloni,
	Yury Norov, Rasmus Villemoes, Hans de Goede, Linus Walleij,
	Sakari Ailus, Salah Triki, Achim Gratz, Ben Collins, linux-kernel,
	linux-iio, linux-wireless, netdev, linux-rtc
In-Reply-To: <aeub59FBHbCy-KKP@yury>

On Fri, 24 Apr 2026 12:35:51 -0400
Yury Norov <ynorov@nvidia.com> wrote:

...
> > Any chance it'd be simple to generate u32_get_bits_signed() etc.? Could
> > be especially useful for le32_get_bits_signed() for example, to have the
> > endian conversion built-in unlike FIELD_GET_SIGNED().  
> 
> Maybe this:
> 
>         x = FIELD_GET_SIGNED(mask, le32_to_cpu(reg))

But if you are going to follow it by:
	  x1 = FIELD_GET_SIGNED(mask1, le32_to_cpu(reg))

you really want to to the byteswap once, best as:
	reg = le32_to_cpu(struct->member);

	David
	
> 
> Thanks,
> Yury
> 


^ permalink raw reply

* Re: [PATCH] wifi: mac80211: restore monitor injection when coexisting with another VIF
From: Brite @ 2026-04-24 17:22 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless, linux-kernel, stable, fjhhz1997,
	oscar.alfonso.diaz
In-Reply-To: <9f7df38831598001ac6cd79ab4fb95b4b6e042fd.camel@sipsolutions.net>



On April 25, 2026 1:55:46 AM GMT+12:00, Johannes Berg <johannes@sipsolutions.net> wrote:
>
>I don't believe that all this complexity is necessary, and the code
>changes have are fairly clearly LLM-created w/o such disclosures.
>Dropping.
>
Are you saying that the patch itself is created by llm? If yes, is that even possible? I do accept that yourself or experienced devs could come up with the simplest of a solution. My initial patch was spread across 6 or 7 files with a lot of debug lines added to find out the location of vm freeze. It has taken a lot of time to narrow it down to this patch. It's totally ok if you're dropping this but if you could at least see what this code does and do a proper minimal fix yourself, that would help out a lot of people in the community. 

The only time I used the help of ai and google was during the initial stage trying to understand the different variables, structures, pointers etc. After that it was just me adding a lot of debug lines to all suspected functions but the vm froze even before anything got printed in dmesg. Then i added delay between the debugs and was finally able to see where the freeze happened. This might be totally unnecessary for an experienced dev like you but since I'm new, I found this the easiest way to get to the root of the issue. 
I also took help from ai and google to help me prepare the patch file in the required format to be sent. 
I don't understand why you think the whole patch is generated by llm. I wonder if it was that easy to be done.

^ permalink raw reply

* Re: [PATCH 0/9] bitfield: add FIELD_GET_SIGNED()
From: Yury Norov @ 2026-04-24 17:10 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Andy Lutomirski, Peter Zijlstra, David Lechner,
	Nuno Sá, Andy Shevchenko, Ping-Ke Shih, Richard Cochran,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Alexandre Belloni, Yury Norov, Rasmus Villemoes,
	Hans de Goede, Linus Walleij, Sakari Ailus, Salah Triki,
	Achim Gratz, Ben Collins, linux-kernel, linux-iio, linux-wireless,
	netdev, linux-rtc
In-Reply-To: <aeuRMiws8zCdkGXX@yury>

On Fri, Apr 24, 2026 at 11:50:10AM -0400, Yury Norov wrote:
> On Fri, Apr 24, 2026 at 01:09:27PM +0100, Jonathan Cameron wrote:
> > On Fri, 17 Apr 2026 13:36:11 -0400
> > Yury Norov <ynorov@nvidia.com> wrote:
> > 
> > > The bitfields are designed in assumption that fields contain unsigned
> > > integer values, thus extracting the values from the field implies
> > > zero-extending.
> > > 
> > > Some drivers need to sign-extend their fields, and currently do it like:
> > > 
> > > 	dc_re += sign_extend32(FIELD_GET(0xfff000, tmp), 11);
> > > 	dc_im += sign_extend32(FIELD_GET(0xfff, tmp), 11);
> > > 
> > > It's error-prone because it relies on user to provide the correct
> > > index of the most significant bit.
> > > 
> > > This series adds a signed version of FIELD_GET(), which is the more
> > > convenient and compiles (on x86_64) to just a couple instructions:
> > > shl and sar.
> > > 
> > > Patch #1 adds FIELD_GET_SIGNED(), and the rest of the series applies it
> > > tree-wide.
> > > 
> > 
> > Just a quick heads up that I'm beginning to assume that this series
> > will land in some form.  If it does can we do it as an immutable branch
> > as I'm suggesting it gets used in some other patches in that should land
> > in the new cycle.
> 
> I'm going to submit v2 soon, as seemingly the discussion is boiled
> down, and then will likely merge it with my tree. I'll create an
> immutable branch for you before the end of day.

Here it is:

https://github.com/norov/linux/pull/new/fgsv2

It builds well for me, but I'll wait for a while for robots feedback
before making it 'officially' immutable.

Thanks,
Yury

^ permalink raw reply

* Re: [PATCH 1/9] bitfield: add FIELD_GET_SIGNED()
From: Yury Norov @ 2026-04-24 16:35 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Andy Lutomirski, Peter Zijlstra, Jonathan Cameron,
	David Lechner, Nuno Sá, Andy Shevchenko, Ping-Ke Shih,
	Richard Cochran, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Alexandre Belloni, Yury Norov,
	Rasmus Villemoes, Hans de Goede, Linus Walleij, Sakari Ailus,
	Salah Triki, Achim Gratz, Ben Collins, linux-kernel, linux-iio,
	linux-wireless, netdev, linux-rtc
In-Reply-To: <6170788fcab2ec835597e3d7411928d36850c20a.camel@sipsolutions.net>

On Mon, Apr 20, 2026 at 10:43:08AM +0200, Johannes Berg wrote:
> On Fri, 2026-04-17 at 13:36 -0400, Yury Norov wrote:
> > The bitfields are designed in assumption that fields contain unsigned
> > integer values, thus extracting the values from the field implies
> > zero-extending.
> > 
> > Some drivers need to sign-extend their fields, and currently do it like:
> > 
> > 	dc_re += sign_extend32(FIELD_GET(0xfff000, tmp), 11);
> > 	dc_im += sign_extend32(FIELD_GET(0xfff, tmp), 11);
> 
> That's indeed pretty awful...
> 
> 
> > +#define FIELD_GET_SIGNED(mask, reg)					\
> > 
> 
> [...]
> 
> I (personally) tend to prefer the "__MAKE_OP" versions (*_get_bits()
> etc.), in particular because WiFi and firmware interfaces deal a lot
> with fixed endian fields.

I don't like that __MAKE_OP magic because whatever it generates is not
greppable. And because we disable strict type checks for kernel, but
this API claims to typecheck the parameters for the user. So, the
following compiles well:

        u64 val = 0;
        ret = le16_get_bits(val, GENMASK(15, 10));

I don't like autogeneration in general. We generate, for example,
be32_get_bits(), but never use it. We don't even know the level of
the bloat.
 
> Any chance it'd be simple to generate u32_get_bits_signed() etc.? Could
> be especially useful for le32_get_bits_signed() for example, to have the
> endian conversion built-in unlike FIELD_GET_SIGNED().

Maybe this:

        FIELD_GET_SIGNED(mask, le32_to_cpu(reg))

Thanks,
Yury

^ permalink raw reply

* Re: [PATCH] NFC: trf7970a: Ignore antenna noise when checking for RF field
From: Mark Greer @ 2026-04-24 16:20 UTC (permalink / raw)
  To: Paul Geurts, sameo, linux-wireless, netdev, linux-kernel; +Cc: martijn.de.gouw
In-Reply-To: <20260422100930.581237-1-paul.geurts@prodrive-technologies.com>


On 4/22/26 3:09 AM, Paul Geurts wrote:
> The main channel Received Signal Strength Indicator (RSSI) measurement
> is used to determine whether an RF field is present or not. RSSI != 0
> is interpreted as an RF Field is present. This does not take RF noise
> and measurement inaccuracy into account, and results in false positives
> in the field.
> 
> Define a noise level and make sure the RF field is only interpreted as
> present when the RSSI is above the noise level.
> 
> Fixes: 851ee3cbf850 ("NFC: trf7970a: Don't turn on RF if there is already an RF field")
> Signed-off-by: Paul Geurts <paul.geurts@prodrive-technologies.com>
> ---
>   drivers/nfc/trf7970a.c | 3 ++-
>   1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
> index d17c701c7888..08c27bb438b5 100644
> --- a/drivers/nfc/trf7970a.c
> +++ b/drivers/nfc/trf7970a.c
> @@ -317,6 +317,7 @@
>   #define TRF7970A_RSSI_OSC_STATUS_RSSI_MASK	(BIT(2) | BIT(1) | BIT(0))
>   #define TRF7970A_RSSI_OSC_STATUS_RSSI_X_MASK	(BIT(5) | BIT(4) | BIT(3))
>   #define TRF7970A_RSSI_OSC_STATUS_RSSI_OSC_OK	BIT(6)
> +#define TRF7970A_RSSI_OSC_STATUS_RSSI_NOISE_LEVEL	1
>   
>   #define TRF7970A_SPECIAL_FCN_REG1_COL_7_6		BIT(0)
>   #define TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL		BIT(1)
> @@ -1300,7 +1301,7 @@ static int trf7970a_is_rf_field(struct trf7970a *trf, bool *is_rf_field)
>   	if (ret)
>   		return ret;
>   
> -	if (rssi & TRF7970A_RSSI_OSC_STATUS_RSSI_MASK)
> +	if ((rssi & TRF7970A_RSSI_OSC_STATUS_RSSI_MASK) > TRF7970A_RSSI_OSC_STATUS_RSSI_NOISE_LEVEL)
>   		*is_rf_field = true;
>   	else
>   		*is_rf_field = false;

Given that false positives are happening in the field and assuming that 
a value of 1 is enough to filter out those false positives, this seems 
like a reasonable solution.

Reviewed-by: Mark Greer <mgreer@animalcreek.com>

^ permalink raw reply

* Re: [PATCH 0/9] bitfield: add FIELD_GET_SIGNED()
From: Yury Norov @ 2026-04-24 15:50 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Andy Lutomirski, Peter Zijlstra, David Lechner,
	Nuno Sá, Andy Shevchenko, Ping-Ke Shih, Richard Cochran,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Alexandre Belloni, Yury Norov, Rasmus Villemoes,
	Hans de Goede, Linus Walleij, Sakari Ailus, Salah Triki,
	Achim Gratz, Ben Collins, linux-kernel, linux-iio, linux-wireless,
	netdev, linux-rtc
In-Reply-To: <20260424130927.349ad3ae@jic23-huawei>

On Fri, Apr 24, 2026 at 01:09:27PM +0100, Jonathan Cameron wrote:
> On Fri, 17 Apr 2026 13:36:11 -0400
> Yury Norov <ynorov@nvidia.com> wrote:
> 
> > The bitfields are designed in assumption that fields contain unsigned
> > integer values, thus extracting the values from the field implies
> > zero-extending.
> > 
> > Some drivers need to sign-extend their fields, and currently do it like:
> > 
> > 	dc_re += sign_extend32(FIELD_GET(0xfff000, tmp), 11);
> > 	dc_im += sign_extend32(FIELD_GET(0xfff, tmp), 11);
> > 
> > It's error-prone because it relies on user to provide the correct
> > index of the most significant bit.
> > 
> > This series adds a signed version of FIELD_GET(), which is the more
> > convenient and compiles (on x86_64) to just a couple instructions:
> > shl and sar.
> > 
> > Patch #1 adds FIELD_GET_SIGNED(), and the rest of the series applies it
> > tree-wide.
> > 
> 
> Just a quick heads up that I'm beginning to assume that this series
> will land in some form.  If it does can we do it as an immutable branch
> as I'm suggesting it gets used in some other patches in that should land
> in the new cycle.

I'm going to submit v2 soon, as seemingly the discussion is boiled
down, and then will likely merge it with my tree. I'll create an
immutable branch for you before the end of day.

Thanks,
Yury

^ permalink raw reply

* Re: [PATCH] wifi: ath11k: Add two missing Lenovo IDs to the quirk table
From: Santiago Ruano Rincón @ 2026-04-24 15:10 UTC (permalink / raw)
  To: Kyle Farnung
  Cc: Jeff Johnson, linux-wireless@vger.kernel.org,
	ath11k@lists.infradead.org, linux-kernel@vger.kernel.org,
	Mark Pearson, koike@igalia.com, Kyle Farnung, 1132343
In-Reply-To: <IA2P223MB1199153665E2AFF709BD2F52D02B2@IA2P223MB1199.NAMP223.PROD.OUTLOOK.COM>

El 24/04/26 a las 02:07, Kyle Farnung escribió:
> > From: Santiago Ruano Rincón <santiagorr@riseup.net>
> > Sent: Thursday, April 23, 2026 2:14 PM
> > To: Jeff Johnson; linux-wireless@vger.kernel.org; ath11k@lists.infradead.org; linux-kernel@vger.kernel.org
> > Cc: Mark Pearson; kfarnung@outlook.com; koike@igalia.com
> > Subject: [PATCH] wifi: ath11k: Add two missing Lenovo IDs to the quirk table
> > 
> > Commit 0eb002c93c3b4 ("wifi: ath11k: Add missing platform IDs for quirk
> > table") added some Lenovo platform IDs to the quirk table to address a
> > wakeup from suspend issue [1].  However, at least two more platform ID
> > are missing in that table: P14s Gen 5 AMD, as reported by Kyle Farnung [2]
> > and P14s Gen 3 AMD.  This commit adds one ID for each.
> > 
> > [1] https://bugzilla.kernel.org/show_bug.cgi?id=219196
> > [2] https://bugzilla.kernel.org/show_bug.cgi?id=219196#c23
> > 
> > Tested-on: P14s G3 AMD.
> 
> Lenovo products have a pair of IDs, you'll want 21J6 [3] and 21MF [4] as
> well. I submitted a patch myself [5], but I've been investigating another
> symptom.

Thanks for the notice. I will adapt my patch to remove the conflict with yours.

> 
> I have the same "wake on power removed" issue, but I have another issue
> that's more annoying where the wifi just dies randomly. I only have the
> wake issue when power is disconnected, not when power is connected again.
> I mostly leave my device connected, so I only realized the behavior while
> doing the regression testing requested in the other patch.

I've also been hit by a similar annoying wifi randomly dying issue that
you describe [deb#1132343].  With the patch that I attached applied to
7.0.0, my P14s G3 AMD laptop has been running for more that 3 days, with
several suspends / weak ups, either with power connected or
disconnected.  And I haven't encountered that problem since it booted so
I concluded that the patch fixed it.  But chances are that I am wrong.

[deb#1132343] https://bugs.debian.org/1132343

The latest bad version I tested where the wifi died was 6.9.11.  I plan
test the patch on top of it.

> 
> [3] https://pcsupport.lenovo.com/jm/en/products/laptops-and-netbooks/thinkpad-p-series-laptops/thinkpad-p14s-gen-3-type-21j5-21j6/21j5
> [4] https://pcsupport.lenovo.com/us/en/products/laptops-and-netbooks/thinkpad-p-series-laptops/thinkpad-p14s-gen-5-type-21me-21mf/21me/21me000pge
> [5] https://lore.kernel.org/all/ba4d194b-6d31-4d8a-a6a6-da116f9f56ac@oss.qualcomm.com/

[snip]

^ permalink raw reply

* Re: [PATCH 1/9] bitfield: add FIELD_GET_SIGNED()
From: David Laight @ 2026-04-24 14:50 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Yury Norov, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Andy Lutomirski, Peter Zijlstra,
	Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Ping-Ke Shih, Richard Cochran, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Alexandre Belloni,
	Yury Norov, Rasmus Villemoes, Hans de Goede, Linus Walleij,
	Sakari Ailus, Salah Triki, Achim Gratz, Ben Collins, linux-kernel,
	linux-iio, linux-wireless, netdev, linux-rtc
In-Reply-To: <6170788fcab2ec835597e3d7411928d36850c20a.camel@sipsolutions.net>

On Mon, 20 Apr 2026 10:43:08 +0200
Johannes Berg <johannes@sipsolutions.net> wrote:

...
> I (personally) tend to prefer the "__MAKE_OP" versions (*_get_bits()
> etc.), in particular because WiFi and firmware interfaces deal a lot
> with fixed endian fields.
> 
> Any chance it'd be simple to generate u32_get_bits_signed() etc.? Could
> be especially useful for le32_get_bits_signed() for example, to have the
> endian conversion built-in unlike FIELD_GET_SIGNED().

The problem is that the number of options explodes.
You need PREP/GET/GET_SIGNED functions x_64() x_64le() x_64be() x_32() x_32le()
x_32be() x_16le() and x_16be().
I make that 24 functions.
For UPDATE you also need x_16() and x_8() for another 10.
So at least 34 definitions - it is all getting silly.
(And I've excluded the pointless 8/16 bit functions where the result
is promoted to signed int.)

Then you have the problem that some (common) architectures don't have
byteswap instructions, but do have byteswapping memory accesses (maybe not
16bit). Which means that is it generally better to byteswap the value
before passing to FIELD_GET() and after building the full 'word' using
FIELD_PREP() (and just | for single bits).
(Except it is probably always better to byteswap constants.)

It is also worth nothing that FIELD_PREP() will reject requests to
fill signed fields with negative constants.

	David

> 
> johannes
> 


^ permalink raw reply

* [PATCH 2/2] wifi: ath11k: fix error path leaks in some WMI calls
From: Nicolas Escande @ 2026-04-24 14:48 UTC (permalink / raw)
  To: ath12k; +Cc: linux-wireless
In-Reply-To: <20260424144813.1708214-1-nico.escande@gmail.com>

This is the same pattern that was previously identified as problematic:
direct 'return ath11k_wmi_cmd_send(...)' will leak the skb in the error
path if it is not explicitly handled.

Fixes: c417b247ba04 ("ath11k: implement hardware data filter")
Fixes: 9cbd7fc9be82 ("ath11k: support MAC address randomization in scan")
Fixes: ba9177fcef21 ("ath11k: Add basic WoW functionalities")
Fixes: fec4b898f369 ("ath11k: Add WoW net-detect functionality")
Fixes: c3c36bfe998b ("ath11k: support ARP and NS offload")
Fixes: a16d9b50cfba ("ath11k: support GTK rekey offload")
Fixes: 652f69ed9c1b ("ath11k: Add support for SAR")
Fixes: 0f84a156aa3b ("ath11k: Handle keepalive during WoWLAN suspend and resume")
Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
---
 drivers/net/wireless/ath/ath11k/wmi.c | 112 ++++++++++++++++++++++----
 1 file changed, 96 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 024c2aad9fb4..dca6e011cc40 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -9299,7 +9299,7 @@ int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
 {
 	struct wmi_hw_data_filter_cmd *cmd;
 	struct sk_buff *skb;
-	int len;
+	int ret, len;
 
 	len = sizeof(*cmd);
 	skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9324,7 +9324,13 @@ int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
 		   "hw data filter enable %d filter_bitmap 0x%x\n",
 		   enable, filter_bitmap);
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_HW_DATA_FILTER_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
@@ -9389,7 +9395,7 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
 	struct sk_buff *skb;
 	struct wmi_scan_prob_req_oui_cmd *cmd;
 	u32 prob_req_oui;
-	int len;
+	int ret, len;
 
 	prob_req_oui = (((u32)mac_addr[0]) << 16) |
 		       (((u32)mac_addr[1]) << 8) | mac_addr[2];
@@ -9408,7 +9414,13 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
 	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "scan prob req oui %d\n",
 		   prob_req_oui);
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_SCAN_PROB_REQ_OUI_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
@@ -9418,6 +9430,7 @@ int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
 	struct wmi_wow_add_del_event_cmd *cmd;
 	struct sk_buff *skb;
 	size_t len;
+	int ret;
 
 	len = sizeof(*cmd);
 	skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9435,7 +9448,13 @@ int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
 	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow add wakeup event %s enable %d vdev_id %d\n",
 		   wow_wakeup_event(event), enable, vdev_id);
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
@@ -9448,6 +9467,7 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
 	struct sk_buff *skb;
 	u8 *ptr;
 	size_t len;
+	int ret;
 
 	len = sizeof(*cmd) +
 	      sizeof(*tlv) +			/* array struct */
@@ -9540,7 +9560,13 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
 	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow add pattern vdev_id %d pattern_id %d pattern_offset %d\n",
 		   vdev_id, pattern_id, pattern_offset);
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_WOW_ADD_WAKE_PATTERN_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
@@ -9548,6 +9574,7 @@ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
 	struct wmi_wow_del_pattern_cmd *cmd;
 	struct sk_buff *skb;
 	size_t len;
+	int ret;
 
 	len = sizeof(*cmd);
 	skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9566,7 +9593,13 @@ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
 	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow del pattern vdev_id %d pattern_id %d\n",
 		   vdev_id, pattern_id);
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_WOW_DEL_WAKE_PATTERN_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 static struct sk_buff *
@@ -9710,6 +9743,7 @@ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
 			      struct wmi_pno_scan_req  *pno_scan)
 {
 	struct sk_buff *skb;
+	int ret;
 
 	if (pno_scan->enable)
 		skb = ath11k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan);
@@ -9719,7 +9753,13 @@ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
 	if (IS_ERR_OR_NULL(skb))
 		return -ENOMEM;
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 static void ath11k_wmi_fill_ns_offload(struct ath11k *ar,
@@ -9837,6 +9877,7 @@ int ath11k_wmi_arp_ns_offload(struct ath11k *ar,
 	u8 *buf_ptr;
 	size_t len;
 	u8 ns_cnt, ns_ext_tuples = 0;
+	int ret;
 
 	offload = &arvif->arp_ns_offload;
 	ns_cnt = offload->ipv6_count;
@@ -9875,7 +9916,13 @@ int ath11k_wmi_arp_ns_offload(struct ath11k *ar,
 	if (ns_ext_tuples)
 		ath11k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1);
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_SET_ARP_NS_OFFLOAD_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
@@ -9883,7 +9930,7 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
 {
 	struct wmi_gtk_rekey_offload_cmd *cmd;
 	struct ath11k_rekey_data *rekey_data = &arvif->rekey_data;
-	int len;
+	int ret, len;
 	struct sk_buff *skb;
 	__le64 replay_ctr;
 
@@ -9917,14 +9964,20 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
 
 	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "offload gtk rekey vdev: %d %d\n",
 		   arvif->vdev_id, enable);
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID offload\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
 				 struct ath11k_vif *arvif)
 {
 	struct wmi_gtk_rekey_offload_cmd *cmd;
-	int len;
+	int ret, len;
 	struct sk_buff *skb;
 
 	len = sizeof(*cmd);
@@ -9941,7 +9994,13 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
 
 	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "get gtk rekey vdev_id: %d\n",
 		   arvif->vdev_id);
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID getinfo\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val)
@@ -9951,6 +10010,7 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va
 	struct sk_buff *skb;
 	u8 *buf_ptr;
 	u32 len, sar_len_aligned, rsvd_len_aligned;
+	int ret;
 
 	sar_len_aligned = roundup(BIOS_SAR_TABLE_LEN, sizeof(u32));
 	rsvd_len_aligned = roundup(BIOS_SAR_RSVD1_LEN, sizeof(u32));
@@ -9981,7 +10041,13 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va
 	tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
 		      FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);
 
-	return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
+	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
@@ -9992,6 +10058,7 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
 	struct sk_buff *skb;
 	u8 *buf_ptr;
 	u32 len, rsvd_len_aligned;
+	int ret;
 
 	rsvd_len_aligned = roundup(BIOS_SAR_RSVD2_LEN, sizeof(u32));
 	len = sizeof(*cmd) + TLV_HDR_SIZE + rsvd_len_aligned;
@@ -10011,7 +10078,13 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
 	tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
 		      FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);
 
-	return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
+	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_sta_keepalive(struct ath11k *ar,
@@ -10022,6 +10095,7 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
 	struct wmi_sta_keepalive_arp_resp *arp;
 	struct sk_buff *skb;
 	size_t len;
+	int ret;
 
 	len = sizeof(*cmd) + sizeof(*arp);
 	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
@@ -10053,7 +10127,13 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
 		   "sta keepalive vdev %d enabled %d method %d interval %d\n",
 		   arg->vdev_id, arg->enabled, arg->method, arg->interval);
 
-	return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_STA_KEEPALIVE_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 bool ath11k_wmi_supports_6ghz_cc_ext(struct ath11k *ar)
-- 
2.53.0


^ permalink raw reply related

* [PATCH 1/2] wifi: ath11k: fix error path leaks in some WMI WOW calls
From: Nicolas Escande @ 2026-04-24 14:48 UTC (permalink / raw)
  To: ath12k; +Cc: linux-wireless
In-Reply-To: <20260424144813.1708214-1-nico.escande@gmail.com>

Fix two instances where we used to directly return the result of
ath11k_wmi_cmd_send(...). Because we did not check the return value, we
also did not free the skb in the error path.

Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands")
Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
---
 drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 40747fba3b0c..024c2aad9fb4 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -9332,6 +9332,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
 	struct wmi_wow_host_wakeup_ind *cmd;
 	struct sk_buff *skb;
 	size_t len;
+	int ret;
 
 	len = sizeof(*cmd);
 	skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9345,14 +9346,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
 
 	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n");
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_wow_enable(struct ath11k *ar)
 {
 	struct wmi_wow_enable_cmd *cmd;
 	struct sk_buff *skb;
-	int len;
+	int ret, len;
 
 	len = sizeof(*cmd);
 	skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9367,7 +9374,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar)
 	cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED;
 	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n");
 
-	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
+	ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
+	if (ret) {
+		ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n");
+		dev_kfree_skb(skb);
+	}
+
+	return ret;
 }
 
 int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
-- 
2.53.0


^ permalink raw reply related


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