Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH rtw-next 2/9] wifi: rtw89: add multicolor LED support for RTL8852CU valve board
From: Ping-Ke Shih @ 2026-07-17  6:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: gary.chang, sc.lee, wenjie.tsai
In-Reply-To: <20260717061910.54466-1-pkshih@realtek.com>

From: Johnson Tsai <wenjie.tsai@realtek.com>

Add multicolor LED support for the RTL8852CU valve board to reflect
wireless connection status (by default, green LED is ON when associated
and OFF when disconnected).

Extend the rtw89 LED subsystem with a multicolor path via led_classdev_mc,
for board-level variants with more than one LED GPIO channel.

Add a new RTW89_LEDS_MC Kconfig option and support multicolor LEDs
through led_classdev_mc, with per-channel caching to minimize
redundant register writes. The RTL8852CU valve board is wired up as
the first user, driving a multicolor WRGB LED over four GPIO channels
(8, 18, 16, and 17) that map to LED_COLOR_ID_WHITE/RED/GREEN/BLUE.

Usage:
- Auto-triggered (default):
  Green LED ON when connected, OFF when disconnected

- Manual override (e.g., set red; channel order: WHITE RED GREEN BLUE):
    echo "0 1 0 0" > /sys/class/leds/rtw89-phyX-multicolor/multi_intensity
    echo 1         > /sys/class/leds/rtw89-phyX-multicolor/brightness

Signed-off-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/Kconfig    |  7 ++
 drivers/net/wireless/realtek/rtw89/Makefile   |  1 +
 drivers/net/wireless/realtek/rtw89/core.c     |  1 +
 drivers/net/wireless/realtek/rtw89/core.h     | 15 +++-
 drivers/net/wireless/realtek/rtw89/led.c      | 26 ++++--
 drivers/net/wireless/realtek/rtw89/led.h      | 13 +++
 drivers/net/wireless/realtek/rtw89/led_mc.c   | 81 +++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/reg.h      |  2 +
 .../net/wireless/realtek/rtw89/rtw8851be.c    |  1 +
 .../net/wireless/realtek/rtw89/rtw8851bu.c    |  1 +
 .../net/wireless/realtek/rtw89/rtw8852ae.c    |  1 +
 .../net/wireless/realtek/rtw89/rtw8852au.c    |  1 +
 .../net/wireless/realtek/rtw89/rtw8852be.c    |  1 +
 .../net/wireless/realtek/rtw89/rtw8852bte.c   |  1 +
 .../net/wireless/realtek/rtw89/rtw8852bu.c    |  1 +
 .../net/wireless/realtek/rtw89/rtw8852ce.c    |  1 +
 .../net/wireless/realtek/rtw89/rtw8852cu.c    | 54 +++++++++++++
 .../net/wireless/realtek/rtw89/rtw8922ae.c    |  2 +
 .../net/wireless/realtek/rtw89/rtw8922au.c    |  1 +
 .../net/wireless/realtek/rtw89/rtw8922de.c    |  2 +
 20 files changed, 207 insertions(+), 6 deletions(-)
 create mode 100644 drivers/net/wireless/realtek/rtw89/led_mc.c

diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig
index be31eaab7621..7200a944260f 100644
--- a/drivers/net/wireless/realtek/rtw89/Kconfig
+++ b/drivers/net/wireless/realtek/rtw89/Kconfig
@@ -197,4 +197,11 @@ config RTW89_LEDS
 	imply MAC80211_LEDS
 	default y
 
+config RTW89_LEDS_MC
+	bool
+	depends on RTW89_LEDS
+	depends on LEDS_CLASS_MULTICOLOR
+	imply LEDS_TRIGGER_TIMER
+	default y
+
 endif
diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile
index 3d47f13d1e66..5536714e0268 100644
--- a/drivers/net/wireless/realtek/rtw89/Makefile
+++ b/drivers/net/wireless/realtek/rtw89/Makefile
@@ -93,6 +93,7 @@ rtw89_8922au-objs := rtw8922au.o
 
 rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o
 rtw89_core-$(CONFIG_RTW89_LEDS) += led.o
+rtw89_core-$(CONFIG_RTW89_LEDS_MC) += led_mc.o
 
 obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o
 rtw89_pci-y := pci.o pci_be.o
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index bb04f649bfb0..b87a72700b99 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -7701,6 +7701,7 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device,
 	rtwdev->ops = ops;
 	rtwdev->chip = chip;
 	rtwdev->variant = variant;
+	rtwdev->board = info->board;
 	rtwdev->fw.req.firmware = firmware;
 	rtwdev->fw.fw_format = fw_format;
 	rtwdev->support_mlo = support_mlo;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 41cc519317c9..70cf6cbf4e8a 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -11,6 +11,7 @@
 #include <linux/firmware.h>
 #include <linux/iopoll.h>
 #include <linux/leds.h>
+#include <linux/led-class-multicolor.h>
 #include <linux/workqueue.h>
 #include <net/mac80211.h>
 
@@ -5279,8 +5280,12 @@ struct rtw89_chanctx_listener {
 #define RTW89_NHM_TH_NUM 11
 #define RTW89_NHM_RPT_NUM 12
 
+#define RTW89_LED_MAX_NUM  4
+
 struct rtw89_led_gpio_entry {
 	u8 pin;
+	unsigned int color;
+	u8 intensity;
 	struct rtw89_reg3_def pinmux;
 	struct rtw89_reg2_def mode;
 	struct rtw89_reg2_def out;
@@ -5295,7 +5300,9 @@ struct rtw89_led {
 	bool registered;
 	const struct rtw89_led_desc *desc;
 	struct led_classdev led;
-	enum led_brightness brightness_cache;
+	struct led_classdev_mc led_mc;
+	struct mc_subled subled[RTW89_LED_MAX_NUM];
+	enum led_brightness brightness_cache[RTW89_LED_MAX_NUM];
 	char name[32];
 };
 
@@ -5454,6 +5461,10 @@ struct rtw89_chip_variant {
 	const struct rtw89_qta_def *qta_def_override;
 };
 
+struct rtw89_board_variant {
+	const struct rtw89_led_desc *led_desc;
+};
+
 union rtw89_bus_info {
 	const struct rtw89_pci_info *pci;
 	const struct rtw89_usb_info *usb;
@@ -5462,6 +5473,7 @@ union rtw89_bus_info {
 struct rtw89_driver_info {
 	const struct rtw89_chip_info *chip;
 	const struct rtw89_chip_variant *variant;
+	const struct rtw89_board_variant *board;
 	const struct dmi_system_id *quirks;
 	unsigned long dev_id_quirks; /* bitmap of rtw89_quirks */
 	union rtw89_bus_info bus;
@@ -7136,6 +7148,7 @@ struct rtw89_dev {
 	struct rtw89_hw_scan_info scan_info;
 	const struct rtw89_chip_info *chip;
 	const struct rtw89_chip_variant *variant;
+	const struct rtw89_board_variant *board;
 	const struct rtw89_pci_info *pci_info;
 	const struct rtw89_rfe_parms *rfe_parms;
 	struct rtw89_hal hal;
diff --git a/drivers/net/wireless/realtek/rtw89/led.c b/drivers/net/wireless/realtek/rtw89/led.c
index 64e7eaa7ee39..ae74b7075d7c 100644
--- a/drivers/net/wireless/realtek/rtw89/led.c
+++ b/drivers/net/wireless/realtek/rtw89/led.c
@@ -45,7 +45,7 @@ static int rtw89_led_set(struct led_classdev *led, enum led_brightness brightnes
 	const struct rtw89_led_desc *desc = rtw_led->desc;
 	const struct rtw89_led_gpio_entry *e = &desc->gpios[0];
 
-	if (rtw_led->brightness_cache == brightness) {
+	if (rtw_led->brightness_cache[0] == brightness) {
 		rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u skip (no change)\n",
 			    e->pin);
 		return 0;
@@ -56,7 +56,7 @@ static int rtw89_led_set(struct led_classdev *led, enum led_brightness brightnes
 	rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u brightness=%u\n",
 		    e->pin, brightness);
 	rtw89_led_gpio_set(rtwdev, e, brightness);
-	rtw_led->brightness_cache = brightness;
+	rtw_led->brightness_cache[0] = brightness;
 
 	wiphy_unlock(rtwdev->hw->wiphy);
 
@@ -97,15 +97,27 @@ void rtw89_led_init(struct rtw89_dev *rtwdev)
 {
 	const struct rtw89_led_desc *desc = &rtw89_common_led_desc;
 	struct rtw89_led *rtw_led = &rtwdev->led;
+	const struct rtw89_board_variant *board = rtwdev->board;
 	int ret;
+	int i;
 
 	/* single-GPIO monochrome LED is the only supported layout */
 	BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1);
 
+	if (board)
+		desc = board->led_desc;
+	if (!desc->n_gpio || desc->n_gpio > RTW89_LED_MAX_NUM)
+		return;
+
 	rtw_led->desc = desc;
-	rtw_led->brightness_cache = LED_OFF;
+	for (i = 0; i < ARRAY_SIZE(rtw_led->brightness_cache); i++)
+		rtw_led->brightness_cache[i] = LED_OFF;
+
+	if (desc->n_gpio == 1)
+		ret = rtw89_led_sc_init(rtwdev, desc);
+	else
+		ret = rtw89_led_mc_init(rtwdev, desc);
 
-	ret = rtw89_led_sc_init(rtwdev, desc);
 	if (ret)
 		return;
 
@@ -115,9 +127,13 @@ void rtw89_led_init(struct rtw89_dev *rtwdev)
 void rtw89_led_deinit(struct rtw89_dev *rtwdev)
 {
 	struct rtw89_led *rtw_led = &rtwdev->led;
+	const struct rtw89_led_desc *desc = rtw_led->desc;
 
 	if (!rtw_led->registered)
 		return;
 
-	rtw89_led_sc_deinit(rtwdev);
+	if (desc->n_gpio == 1)
+		rtw89_led_sc_deinit(rtwdev);
+	else
+		rtw89_led_mc_deinit(rtwdev);
 }
diff --git a/drivers/net/wireless/realtek/rtw89/led.h b/drivers/net/wireless/realtek/rtw89/led.h
index 27e782cff7c7..f5ccfe1d1752 100644
--- a/drivers/net/wireless/realtek/rtw89/led.h
+++ b/drivers/net/wireless/realtek/rtw89/led.h
@@ -21,4 +21,17 @@ static inline void rtw89_led_init(struct rtw89_dev *rtwdev) {}
 static inline void rtw89_led_deinit(struct rtw89_dev *rtwdev) {}
 #endif
 
+#ifdef CONFIG_RTW89_LEDS_MC
+int rtw89_led_mc_init(struct rtw89_dev *rtwdev, const struct rtw89_led_desc *desc);
+void rtw89_led_mc_deinit(struct rtw89_dev *rtwdev);
+#else
+static inline int rtw89_led_mc_init(struct rtw89_dev *rtwdev,
+				    const struct rtw89_led_desc *desc)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void rtw89_led_mc_deinit(struct rtw89_dev *rtwdev) {}
+#endif
+
 #endif
diff --git a/drivers/net/wireless/realtek/rtw89/led_mc.c b/drivers/net/wireless/realtek/rtw89/led_mc.c
new file mode 100644
index 000000000000..7e9243baef68
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/led_mc.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2026 Realtek Corporation
+ */
+
+#include "core.h"
+#include "debug.h"
+#include "led.h"
+
+static int rtw89_led_mc_brightness_set(struct led_classdev *led,
+				       enum led_brightness brightness)
+{
+	struct led_classdev_mc *mcdev = lcdev_to_mccdev(led);
+	struct rtw89_led *rtw_led = container_of(mcdev, struct rtw89_led, led_mc);
+	struct rtw89_dev *rtwdev = container_of(rtw_led, struct rtw89_dev, led);
+	const struct rtw89_led_desc *desc = rtw_led->desc;
+	const struct rtw89_led_gpio_entry *e;
+	unsigned int i;
+
+	led_mc_calc_color_components(mcdev, brightness);
+
+	wiphy_lock(rtwdev->hw->wiphy);
+
+	for (i = 0; i < mcdev->num_colors; i++) {
+		e = &desc->gpios[i];
+		if (rtw_led->brightness_cache[i] == mcdev->subled_info[i].brightness) {
+			rtw89_debug(rtwdev, RTW89_DBG_LED,
+				    "led_mc_set: pin=%u skip (no change)\n", e->pin);
+			continue;
+		}
+
+		rtw89_debug(rtwdev, RTW89_DBG_LED, "led_mc_set: pin=%u brightness=%u\n",
+			    e->pin, mcdev->subled_info[i].brightness);
+		rtw89_led_gpio_set(rtwdev, e, mcdev->subled_info[i].brightness);
+		rtw_led->brightness_cache[i] = mcdev->subled_info[i].brightness;
+	}
+
+	wiphy_unlock(rtwdev->hw->wiphy);
+
+	return 0;
+}
+
+int rtw89_led_mc_init(struct rtw89_dev *rtwdev, const struct rtw89_led_desc *desc)
+{
+	struct rtw89_led *rtw_led = &rtwdev->led;
+	struct led_classdev_mc *led_mc = &rtw_led->led_mc;
+	int i, ret;
+
+	led_mc->num_colors = desc->n_gpio;
+	led_mc->subled_info = rtw_led->subled;
+
+	for (i = 0; i < desc->n_gpio; i++) {
+		const struct rtw89_led_gpio_entry *e = &desc->gpios[i];
+
+		rtw_led->subled[i].color_index = e->color;
+		rtw_led->subled[i].intensity = e->intensity;
+		rtw_led->subled[i].brightness = 0;
+		rtw_led->subled[i].channel = i;
+
+		rtw89_led_gpio_config(rtwdev, e);
+	}
+
+	snprintf(rtw_led->name, sizeof(rtw_led->name), "rtw89-%s-multicolor",
+		 wiphy_name(rtwdev->hw->wiphy));
+	led_mc->led_cdev.name = rtw_led->name;
+	led_mc->led_cdev.brightness_set_blocking = rtw89_led_mc_brightness_set;
+	led_mc->led_cdev.max_brightness = LED_ON;
+	led_mc->led_cdev.default_trigger = ieee80211_get_assoc_led_name(rtwdev->hw);
+
+	ret = led_classdev_multicolor_register(rtwdev->dev, led_mc);
+	if (ret)
+		rtw89_warn(rtwdev, "failed to register multicolor LED, ret=%d\n", ret);
+
+	return ret;
+}
+
+void rtw89_led_mc_deinit(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_led *rtw_led = &rtwdev->led;
+
+	led_classdev_multicolor_unregister(&rtw_led->led_mc);
+}
diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 3bf2865b81ab..053028b9ef16 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -205,6 +205,8 @@
 #define MAC_AX_HCI_SEL_PCIE_USB 3
 #define MAC_AX_HCI_SEL_MULTI_SDIO 4
 
+#define R_AX_GPIO_16_TO_18_EXT_CTRL 0x0150
+
 #define R_AX_HALT_H2C_CTRL 0x0160
 #define R_AX_HALT_H2C 0x0168
 #define B_AX_HALT_H2C_TRIGGER BIT(0)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c
index 640672eb0d26..459d2e778b83 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c
@@ -72,6 +72,7 @@ static const struct rtw89_pci_info rtw8851b_pci_info = {
 static const struct rtw89_driver_info rtw89_8851be_info = {
 	.chip = &rtw8851b_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c
index 34ba5661d771..1e827205f254 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c
@@ -30,6 +30,7 @@ static const struct rtw89_usb_info rtw8851b_usb_info = {
 static const struct rtw89_driver_info rtw89_8851bu_info = {
 	.chip = &rtw8851b_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c
index 64306cdc1ee4..142a45a2e718 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c
@@ -70,6 +70,7 @@ static const struct rtw89_pci_info rtw8852a_pci_info = {
 static const struct rtw89_driver_info rtw89_8852ae_info = {
 	.chip = &rtw8852a_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852au.c b/drivers/net/wireless/realtek/rtw89/rtw8852au.c
index 29b7f7769370..065f4e5b17af 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852au.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852au.c
@@ -32,6 +32,7 @@ static const struct rtw89_usb_info rtw8852a_usb_info = {
 static const struct rtw89_driver_info rtw89_8852au_info = {
 	.chip = &rtw8852a_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c
index 5bc0a6a99d1d..1c622a99c070 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c
@@ -72,6 +72,7 @@ static const struct rtw89_pci_info rtw8852b_pci_info = {
 static const struct rtw89_driver_info rtw89_8852be_info = {
 	.chip = &rtw8852b_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c
index 49a72ca835ac..73c7ebd33ab4 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c
@@ -78,6 +78,7 @@ static const struct rtw89_pci_info rtw8852bt_pci_info = {
 static const struct rtw89_driver_info rtw89_8852bte_info = {
 	.chip = &rtw8852bt_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c
index 308d3d570ff3..de79a19a2824 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c
@@ -30,6 +30,7 @@ static const struct rtw89_usb_info rtw8852b_usb_info = {
 static const struct rtw89_driver_info rtw89_8852bu_info = {
 	.chip = &rtw8852b_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c
index 3c64c0539205..bb5d5745aa62 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c
@@ -101,6 +101,7 @@ static const struct dmi_system_id rtw8852c_pci_quirks[] = {
 static const struct rtw89_driver_info rtw89_8852ce_info = {
 	.chip = &rtw8852c_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = rtw8852c_pci_quirks,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
index 81ee96b0a048..51dd8fb05366 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
@@ -32,6 +32,7 @@ static const struct rtw89_usb_info rtw8852c_usb_info = {
 static const struct rtw89_driver_info rtw89_8852cu_info = {
 	.chip = &rtw8852c_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
@@ -39,9 +40,62 @@ static const struct rtw89_driver_info rtw89_8852cu_info = {
 	},
 };
 
+static const struct rtw89_led_gpio_entry rtw8852cu_valve_led_gpios[] = {
+	{
+		.pin = 8,
+		.color = LED_COLOR_ID_WHITE,
+		.intensity = 0,
+		.pinmux = {.addr = R_AX_GPIO8_15_FUNC_SEL,
+			   .mask = GENMASK(3, 0),
+			   .data = 0xf},
+		.mode = {.addr = R_AX_GPIO_EXT_CTRL + 2, .data = BIT(0) | BIT(8)},
+		.out = {.addr = R_AX_GPIO_EXT_CTRL + 1, .data = BIT(0)},
+	}, {
+		.pin = 18,
+		.color = LED_COLOR_ID_RED,
+		.intensity = 0,
+		.pinmux = {.addr = R_AX_GPIO16_23_FUNC_SEL,
+			   .mask = GENMASK(11, 8),
+			   .data = 0xf},
+		.mode = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 2,
+			 .data = BIT(2) | BIT(10)},
+		.out = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 1, .data = BIT(2)},
+	}, {
+		.pin = 16,
+		.color = LED_COLOR_ID_GREEN,
+		.intensity = 1,
+		.pinmux = {.addr = R_AX_GPIO16_23_FUNC_SEL,
+			   .mask = GENMASK(3, 0),
+			   .data = 0xf},
+		.mode = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 2,
+			 .data = BIT(0) | BIT(8)},
+		.out = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 1, .data = BIT(0)},
+	}, {
+		.pin = 17,
+		.color = LED_COLOR_ID_BLUE,
+		.intensity = 0,
+		.pinmux = {.addr = R_AX_GPIO16_23_FUNC_SEL,
+			   .mask = GENMASK(7, 4),
+			   .data = 0xf},
+		.mode = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 2,
+			 .data = BIT(1) | BIT(9)},
+		.out = {.addr = R_AX_GPIO_16_TO_18_EXT_CTRL + 1, .data = BIT(1)},
+	},
+};
+
+static const struct rtw89_led_desc rtw8852cu_valve_led_desc = {
+	.gpios = rtw8852cu_valve_led_gpios,
+	.n_gpio = ARRAY_SIZE(rtw8852cu_valve_led_gpios),
+};
+
+static const struct rtw89_board_variant rtw89_8852cu_valve_board = {
+	.led_desc = &rtw8852cu_valve_led_desc,
+};
+
 static const struct rtw89_driver_info rtw89_8852cu_valve_info = {
 	.chip = &rtw8852c_chip_info,
 	.variant = NULL,
+	.board = &rtw89_8852cu_valve_board,
 	.quirks = NULL,
 	.dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS),
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c
index 5527a8db393b..8f3a1cd463d9 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c
@@ -76,6 +76,7 @@ static const struct rtw89_pci_info rtw8922a_pci_info = {
 static const struct rtw89_driver_info rtw89_8922ae_info = {
 	.chip = &rtw8922a_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
@@ -86,6 +87,7 @@ static const struct rtw89_driver_info rtw89_8922ae_info = {
 static const struct rtw89_driver_info rtw89_8922ae_vs_info = {
 	.chip = &rtw8922a_chip_info,
 	.variant = &rtw8922ae_vs_variant,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922au.c b/drivers/net/wireless/realtek/rtw89/rtw8922au.c
index 2b81de501d62..56c79b1ec865 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922au.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922au.c
@@ -31,6 +31,7 @@ static const struct rtw89_usb_info rtw8922a_usb_info = {
 static const struct rtw89_driver_info rtw89_8922au_info = {
 	.chip = &rtw8922a_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c
index a1a81c338be3..09400a0efcfd 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922de.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c
@@ -72,6 +72,7 @@ static const struct rtw89_pci_info rtw8922d_pci_info = {
 static const struct rtw89_driver_info rtw89_8922de_vs_info = {
 	.chip = &rtw8922d_chip_info,
 	.variant = &rtw8922de_vs_variant,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
@@ -82,6 +83,7 @@ static const struct rtw89_driver_info rtw89_8922de_vs_info = {
 static const struct rtw89_driver_info rtw89_8922de_info = {
 	.chip = &rtw8922d_chip_info,
 	.variant = NULL,
+	.board = NULL,
 	.quirks = NULL,
 	.dev_id_quirks = 0,
 	.bus = {
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 1/9] wifi: rtw89: add LED support to reflect the wireless association status
From: Ping-Ke Shih @ 2026-07-17  6:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: gary.chang, sc.lee, wenjie.tsai
In-Reply-To: <20260717061910.54466-1-pkshih@realtek.com>

From: Johnson Tsai <wenjie.tsai@realtek.com>

Add a new RTW89_LEDS Kconfig option, along with LED structures to describe
flexible GPIO mappings for chip common LED definition. Core LED lifecycle
and registration logic default to the mac80211 association trigger, and
chips are wired up as the first user with a single-GPIO monochrome LED.

Usage:
- Auto-triggered (default):
  ON when connected, OFF when disconnected
- Manual override:
    echo <1|0> > /sys/class/leds/rtw89-phyX/brightness

Signed-off-by: Johnson Tsai <wenjie.tsai@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/Kconfig  |   7 ++
 drivers/net/wireless/realtek/rtw89/Makefile |   1 +
 drivers/net/wireless/realtek/rtw89/core.c   |   3 +
 drivers/net/wireless/realtek/rtw89/core.h   |  22 ++++
 drivers/net/wireless/realtek/rtw89/debug.h  |   1 +
 drivers/net/wireless/realtek/rtw89/led.c    | 123 ++++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/led.h    |  24 ++++
 7 files changed, 181 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw89/led.c
 create mode 100644 drivers/net/wireless/realtek/rtw89/led.h

diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig
index 43e3b0ef44da..be31eaab7621 100644
--- a/drivers/net/wireless/realtek/rtw89/Kconfig
+++ b/drivers/net/wireless/realtek/rtw89/Kconfig
@@ -190,4 +190,11 @@ config RTW89_DEBUGFS
 
 	  If unsure, say Y to simplify debug problems
 
+config RTW89_LEDS
+	bool
+	depends on RTW89_CORE
+	depends on LEDS_CLASS=y || LEDS_CLASS=MAC80211
+	imply MAC80211_LEDS
+	default y
+
 endif
diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile
index 475bad743d75..3d47f13d1e66 100644
--- a/drivers/net/wireless/realtek/rtw89/Makefile
+++ b/drivers/net/wireless/realtek/rtw89/Makefile
@@ -92,6 +92,7 @@ obj-$(CONFIG_RTW89_8922AU) += rtw89_8922au.o
 rtw89_8922au-objs := rtw8922au.o
 
 rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o
+rtw89_core-$(CONFIG_RTW89_LEDS) += led.o
 
 obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o
 rtw89_pci-y := pci.o pci_be.o
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index b4b0a451fed7..bb04f649bfb0 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -11,6 +11,7 @@
 #include "core.h"
 #include "efuse.h"
 #include "fw.h"
+#include "led.h"
 #include "mac.h"
 #include "phy.h"
 #include "ps.h"
@@ -7588,6 +7589,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
 
 	rtw89_rfkill_polling_init(rtwdev);
 	rtw89_btc_init(rtwdev);
+	rtw89_led_init(rtwdev);
 
 	return 0;
 
@@ -7601,6 +7603,7 @@ static void rtw89_core_unregister_hw(struct rtw89_dev *rtwdev)
 {
 	struct ieee80211_hw *hw = rtwdev->hw;
 
+	rtw89_led_deinit(rtwdev);
 	rtw89_rfkill_polling_deinit(rtwdev);
 	ieee80211_unregister_hw(hw);
 }
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index c0795e0d10cd..41cc519317c9 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -10,6 +10,7 @@
 #include <linux/dmi.h>
 #include <linux/firmware.h>
 #include <linux/iopoll.h>
+#include <linux/leds.h>
 #include <linux/workqueue.h>
 #include <net/mac80211.h>
 
@@ -5278,6 +5279,26 @@ struct rtw89_chanctx_listener {
 #define RTW89_NHM_TH_NUM 11
 #define RTW89_NHM_RPT_NUM 12
 
+struct rtw89_led_gpio_entry {
+	u8 pin;
+	struct rtw89_reg3_def pinmux;
+	struct rtw89_reg2_def mode;
+	struct rtw89_reg2_def out;
+};
+
+struct rtw89_led_desc {
+	const struct rtw89_led_gpio_entry *gpios;
+	u8 n_gpio;
+};
+
+struct rtw89_led {
+	bool registered;
+	const struct rtw89_led_desc *desc;
+	struct led_classdev led;
+	enum led_brightness brightness_cache;
+	char name[32];
+};
+
 struct rtw89_chip_info {
 	enum rtw89_core_chip_id chip_id;
 	enum rtw89_chip_gen chip_gen;
@@ -7245,6 +7266,7 @@ struct rtw89_dev {
 
 	struct rtw89_debugfs *debugfs;
 	struct rtw89_vif *pure_monitor_mode_vif;
+	struct rtw89_led led;
 
 	/* HCI related data, keep last */
 	u8 priv[] __aligned(sizeof(void *));
diff --git a/drivers/net/wireless/realtek/rtw89/debug.h b/drivers/net/wireless/realtek/rtw89/debug.h
index 7cdceb24f52d..f53d8acbd0d2 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.h
+++ b/drivers/net/wireless/realtek/rtw89/debug.h
@@ -32,6 +32,7 @@ enum rtw89_debug_mask {
 	RTW89_DBG_ACPI = BIT(21),
 	RTW89_DBG_EDCCA = BIT(22),
 	RTW89_DBG_PS = BIT(23),
+	RTW89_DBG_LED = BIT(24),
 
 	RTW89_DBG_UNEXP = BIT(31),
 };
diff --git a/drivers/net/wireless/realtek/rtw89/led.c b/drivers/net/wireless/realtek/rtw89/led.c
new file mode 100644
index 000000000000..64e7eaa7ee39
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/led.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2026 Realtek Corporation
+ */
+
+#include "core.h"
+#include "debug.h"
+#include "led.h"
+#include "reg.h"
+
+static const struct rtw89_led_gpio_entry rtw89_common_led_gpios[] = {{
+	.pin = 8,
+	.pinmux = {.addr = R_AX_GPIO8_15_FUNC_SEL, .mask = GENMASK(3, 0), .data = 0xf},
+	.mode = {.addr = R_AX_GPIO_EXT_CTRL + 2, .data = BIT(0) | BIT(8)},
+	.out = {.addr = R_AX_GPIO_EXT_CTRL + 1, .data = BIT(0)},
+}};
+
+static const struct rtw89_led_desc rtw89_common_led_desc = {
+	.gpios = rtw89_common_led_gpios,
+	.n_gpio = ARRAY_SIZE(rtw89_common_led_gpios),
+};
+
+void rtw89_led_gpio_config(struct rtw89_dev *rtwdev,
+			   const struct rtw89_led_gpio_entry *e)
+{
+	rtw89_write32_mask(rtwdev, e->pinmux.addr, e->pinmux.mask, e->pinmux.data);
+	rtw89_write16_clr(rtwdev, e->mode.addr, e->mode.data);
+}
+
+void rtw89_led_gpio_set(struct rtw89_dev *rtwdev,
+			const struct rtw89_led_gpio_entry *e,
+			enum led_brightness brightness)
+{
+	if (brightness == LED_OFF) {
+		rtw89_write16_clr(rtwdev, e->mode.addr, e->mode.data);
+	} else {
+		rtw89_write16_set(rtwdev, e->mode.addr, e->mode.data);
+		rtw89_write8_clr(rtwdev, e->out.addr, e->out.data);
+	}
+}
+
+static int rtw89_led_set(struct led_classdev *led, enum led_brightness brightness)
+{
+	struct rtw89_led *rtw_led = container_of(led, struct rtw89_led, led);
+	struct rtw89_dev *rtwdev = container_of(rtw_led, struct rtw89_dev, led);
+	const struct rtw89_led_desc *desc = rtw_led->desc;
+	const struct rtw89_led_gpio_entry *e = &desc->gpios[0];
+
+	if (rtw_led->brightness_cache == brightness) {
+		rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u skip (no change)\n",
+			    e->pin);
+		return 0;
+	}
+
+	wiphy_lock(rtwdev->hw->wiphy);
+
+	rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u brightness=%u\n",
+		    e->pin, brightness);
+	rtw89_led_gpio_set(rtwdev, e, brightness);
+	rtw_led->brightness_cache = brightness;
+
+	wiphy_unlock(rtwdev->hw->wiphy);
+
+	return 0;
+}
+
+static int rtw89_led_sc_init(struct rtw89_dev *rtwdev, const struct rtw89_led_desc *desc)
+{
+	struct rtw89_led *rtw_led = &rtwdev->led;
+	int ret;
+
+	snprintf(rtw_led->name, sizeof(rtw_led->name), "rtw89-%s",
+		 wiphy_name(rtwdev->hw->wiphy));
+	rtw_led->led.name = rtw_led->name;
+	rtw_led->led.brightness_set_blocking = rtw89_led_set;
+	rtw_led->led.max_brightness = LED_ON;
+	rtw_led->led.default_trigger = ieee80211_get_assoc_led_name(rtwdev->hw);
+
+	ret = led_classdev_register(rtwdev->dev, &rtw_led->led);
+	if (ret) {
+		rtw89_warn(rtwdev, "failed to register LED, ret=%d\n", ret);
+		return ret;
+	}
+
+	rtw89_led_gpio_config(rtwdev, &desc->gpios[0]);
+
+	return 0;
+}
+
+static void rtw89_led_sc_deinit(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_led *rtw_led = &rtwdev->led;
+
+	led_classdev_unregister(&rtw_led->led);
+}
+
+void rtw89_led_init(struct rtw89_dev *rtwdev)
+{
+	const struct rtw89_led_desc *desc = &rtw89_common_led_desc;
+	struct rtw89_led *rtw_led = &rtwdev->led;
+	int ret;
+
+	/* single-GPIO monochrome LED is the only supported layout */
+	BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1);
+
+	rtw_led->desc = desc;
+	rtw_led->brightness_cache = LED_OFF;
+
+	ret = rtw89_led_sc_init(rtwdev, desc);
+	if (ret)
+		return;
+
+	rtw_led->registered = true;
+}
+
+void rtw89_led_deinit(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_led *rtw_led = &rtwdev->led;
+
+	if (!rtw_led->registered)
+		return;
+
+	rtw89_led_sc_deinit(rtwdev);
+}
diff --git a/drivers/net/wireless/realtek/rtw89/led.h b/drivers/net/wireless/realtek/rtw89/led.h
new file mode 100644
index 000000000000..27e782cff7c7
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/led.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2026 Realtek Corporation
+ */
+
+#ifndef __RTW89_LED_H__
+#define __RTW89_LED_H__
+
+#include "core.h"
+
+void rtw89_led_gpio_config(struct rtw89_dev *rtwdev,
+			   const struct rtw89_led_gpio_entry *e);
+void rtw89_led_gpio_set(struct rtw89_dev *rtwdev,
+			const struct rtw89_led_gpio_entry *e,
+			enum led_brightness brightness);
+
+#ifdef CONFIG_RTW89_LEDS
+void rtw89_led_init(struct rtw89_dev *rtwdev);
+void rtw89_led_deinit(struct rtw89_dev *rtwdev);
+#else
+static inline void rtw89_led_init(struct rtw89_dev *rtwdev) {}
+static inline void rtw89_led_deinit(struct rtw89_dev *rtwdev) {}
+#endif
+
+#endif
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 0/9] wifi: rtw89: add LED support and update some random settings
From: Ping-Ke Shih @ 2026-07-17  6:19 UTC (permalink / raw)
  To: linux-wireless; +Cc: gary.chang, sc.lee, wenjie.tsai

First two patches are to add LED support, including mono-/multi-color LED.
All chips can support mono-color LED triggered by association state, and
the multicolor LED is limited to board design according to VID/PID.

Patch 3 is to disable 2 GHz band support, which a specific board doesn't
want this by design.

Patches 4-5 are changes of RF TX IQ calibration, which has format and
flow changes.

Others are random registers change according to internal update.

Chih-Kang Chang (2):
  wifi: rtw89: rfk: update TXIQK H2C command format to v1
  wifi: rtw89: 8922d: bypass TXIQK when scan

David Lee (1):
  wifi: rtw89: 8852cu: add quirk to disable 2.4 GHz band

Johnson Tsai (2):
  wifi: rtw89: add LED support to reflect the wireless association
    status
  wifi: rtw89: add multicolor LED support for RTL8852CU valve board

Ping-Ke Shih (4):
  wifi: rtw89: wow: extend timeout unit to avoid SER false alarm
  wifi: rtw89: pci: set PCIe maximum TS1 for RTL8922DE
  wifi: rtw89: 8922d: reduce IO in power-on function
  wifi: rtw89: phy: set CFR to manual mode for some 2GHz channels

 drivers/net/wireless/realtek/rtw89/Kconfig    |  14 ++
 drivers/net/wireless/realtek/rtw89/Makefile   |   2 +
 drivers/net/wireless/realtek/rtw89/core.c     |   7 +
 drivers/net/wireless/realtek/rtw89/core.h     |  38 +++++
 drivers/net/wireless/realtek/rtw89/debug.h    |   1 +
 drivers/net/wireless/realtek/rtw89/fw.c       |  35 +++--
 drivers/net/wireless/realtek/rtw89/fw.h       |   7 +-
 drivers/net/wireless/realtek/rtw89/led.c      | 139 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/led.h      |  37 +++++
 drivers/net/wireless/realtek/rtw89/led_mc.c   |  81 ++++++++++
 drivers/net/wireless/realtek/rtw89/pci.h      |   3 +-
 drivers/net/wireless/realtek/rtw89/pci_be.c   |  11 +-
 drivers/net/wireless/realtek/rtw89/phy_be.c   |   8 +-
 drivers/net/wireless/realtek/rtw89/reg.h      |  12 ++
 .../net/wireless/realtek/rtw89/rtw8851be.c    |   1 +
 .../net/wireless/realtek/rtw89/rtw8851bu.c    |   1 +
 .../net/wireless/realtek/rtw89/rtw8852ae.c    |   1 +
 .../net/wireless/realtek/rtw89/rtw8852au.c    |   1 +
 .../net/wireless/realtek/rtw89/rtw8852be.c    |   1 +
 .../net/wireless/realtek/rtw89/rtw8852bte.c   |   1 +
 .../net/wireless/realtek/rtw89/rtw8852bu.c    |   1 +
 .../net/wireless/realtek/rtw89/rtw8852ce.c    |   1 +
 .../net/wireless/realtek/rtw89/rtw8852cu.c    |  57 ++++++-
 .../net/wireless/realtek/rtw89/rtw8922ae.c    |   2 +
 .../net/wireless/realtek/rtw89/rtw8922au.c    |   1 +
 drivers/net/wireless/realtek/rtw89/rtw8922d.c |  82 +++++++++--
 .../net/wireless/realtek/rtw89/rtw8922de.c    |   2 +
 27 files changed, 521 insertions(+), 26 deletions(-)
 create mode 100644 drivers/net/wireless/realtek/rtw89/led.c
 create mode 100644 drivers/net/wireless/realtek/rtw89/led.h
 create mode 100644 drivers/net/wireless/realtek/rtw89/led_mc.c


base-commit: 730dbda6dc70d29180eb2a7e9fa36823838bb042
-- 
2.25.1


^ permalink raw reply

* Re: [PATCH v2 3/3] wifi: ath12k: implement custom wake_tx_queue with flow control
From: Jose Ignacio Tornos Martinez @ 2026-07-17  6:18 UTC (permalink / raw)
  To: tamizh.raja
  Cc: ath11k, ath12k, jjohnson, jtornosm, linux-kernel, linux-wireless
In-Reply-To: <CABkEBKa26vR52mX85t4xuZbrXz0FiRc0O_NeDFSNAQpSx=RR7g@mail.gmail.com>

Hi Tamizh,

Thank you for the review. You are right, I will fix this in v3.

Best regards,
Jose Ignacio


^ permalink raw reply

* Re: [PATCH v2 2/3] wifi: ath11k: implement custom wake_tx_queue with flow control
From: Jose Ignacio Tornos Martinez @ 2026-07-17  6:17 UTC (permalink / raw)
  To: tamizh.raja
  Cc: ath11k, ath12k, jjohnson, jtornosm, linux-kernel, linux-wireless
In-Reply-To: <CABkEBKbcQYvMMRUmVgv+GyG8eNiot0acJ13Xy2236zqqr2cnXw@mail.gmail.com>

Hi Tamizh,

Thank you for the review. You are right, I will fix this in v3.

Best regards,
Jose Ignacio


^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath12k: Set DTIM policy to stick mode for station interface
From: Rameshkumar Sundaram @ 2026-07-17  4:59 UTC (permalink / raw)
  To: Daizhuang Bai, jjohnson; +Cc: ath12k, linux-wireless, linux-kernel
In-Reply-To: <20260717014302.284034-1-daizhuang.bai@oss.qualcomm.com>

On 7/17/2026 7:13 AM, Daizhuang Bai wrote:
> Currently, the station always follows the listen interval regardless of
> the DTIM value. The DTIM function does not work as expected. The default
> value of the listen interval is 5 so that the STA wakes up every 500ms
> when power save is on. This can cause a data transmission delay.
> 
> Set the DTIM policy to DTIM stick mode so that the station follows the AP
> DTIM interval rather than the listen interval, which is set in the peer
> assoc command. DTIM stick mode is preferable per the firmware team's request.
> 
> Apply this only for STA vdevs and only when STA power save is supported, to
> avoid affecting unsupported targets and P2P client vdevs.
> 
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
> 
> Signed-off-by: Daizhuang Bai <daizhuang.bai@oss.qualcomm.com>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH rtw-next] wifi: rtw89: fw: use MAC source for IO offload delay command
From: Ping-Ke Shih @ 2026-07-17  3:53 UTC (permalink / raw)
  To: Ping-Ke Shih, linux-wireless; +Cc: leo.li, rtl8821cerfe2
In-Reply-To: <20260714074811.30124-1-pkshih@realtek.com>

Ping-Ke Shih <pkshih@realtek.com> wrote:

> From: Chia-Yuan Li <leo.li@realtek.com>
> 
> The udelay/mdelay helpers set the command source to
> RTW89_FW_CMD_OFLD_SRC_OTHER (4), which does not fit the two-bit field
> RTW89_H2C_CMD_OFLD_W0_SRC (GENMASK(1, 0)). The le32_encode_bits() masks
> it down to 0 (RTW89_FW_CMD_OFLD_SRC_BB), and compiler throws
> __field_overflow() error. Fortunately it still works because firmware
> ignores the source field for a delay command.
> 
> Use RTW89_FW_CMD_OFLD_SRC_MAC as the vendor driver does, and drop the
> unused RTW89_FW_CMD_OFLD_SRC_OTHER enumerator.
> 
> Reported-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
> Closes: https://github.com/morrownr/rtw89/issues/111
> Fixes: ae3d327515f2 ("wifi: rtw89: add IO offload support via firmware")
> Signed-off-by: Chia-Yuan Li <leo.li@realtek.com>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>

1 patch(es) applied to rtw-next branch of rtw.git, thanks.

730dbda6dc70 wifi: rtw89: fw: use MAC source for IO offload delay command

---
https://github.com/pkshih/rtw.git


^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath11k: Avoid buffer overread in ath11k_wmi_tlv_op_rx()
From: Rameshkumar Sundaram @ 2026-07-17  3:51 UTC (permalink / raw)
  To: Jeff Johnson, Jeff Johnson; +Cc: linux-wireless, ath11k, linux-kernel
In-Reply-To: <20260716-ath11k_wmi_tlv_op_rx-overread-v1-1-0b972b3f1368@oss.qualcomm.com>

On 7/17/2026 1:31 AM, Jeff Johnson wrote:
> Currently, in ath11k_wmi_tlv_op_rx(), the firmware buffer is read
> without first verifying that the buffer has enough data to hold a
> header. This could result in a buffer overread.
> 
> Add an upfront length check before dereferencing skb->data as a
> wmi_cmd_hdr. The check is placed before the trace_ath11k_wmi_event()
> call to preserve the existing trace semantics (tracing the full raw
> WMI event including the header), unlike the analogous ath12k fix which
> could use skb_pull_data() directly.
> 
> Compile tested only.
> 
> Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
> Assisted-by: Claude:claude-sonnet-4-6
> Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH v3] wifi: ath11k: fix resource leak on error in ext IRQ setup
From: Baochen Qiang @ 2026-07-17  3:47 UTC (permalink / raw)
  To: ZhaoJinming, Jeff Johnson; +Cc: linux-wireless, ath11k, linux-kernel
In-Reply-To: <20260716114611.2042584-1-zhaojinming@uniontech.com>



On 7/16/2026 7:46 PM, ZhaoJinming wrote:
> In ath11k_ahb_config_irq(), when a CE request_irq() fails, the function
> returns the error immediately without freeing the CE IRQs that were
> successfully registered in previous loop iterations. The probe error
> path does not call ath11k_ahb_free_irq() either, so the previously
> registered CE IRQ handlers remain attached to the interrupt lines and
> are never released.
> 
> In ath11k_ahb_config_ext_irq(), when an external request_irq() fails,
> the error is only logged and the loop continues. The function then
> returns 0 indicating success, leaving the device in a partially
> configured state where some external IRQs are not registered. This
> causes enable_irq()/disable_irq()/free_irq() to be called on
> unregistered IRQs during runtime and remove/shutdown, triggering
> WARN_ON(!desc->action), and missing interrupt handlers lead to data
> loss.
> 
> Additionally, if alloc_netdev_dummy() fails for a later IRQ group, the
> function returns -ENOMEM without freeing the ext IRQs and napi_ndev
> that were successfully set up for earlier groups.
> 
> Fix all three issues: propagate the error up to the caller and unwind
> all successfully registered IRQs and allocated resources on failure.
> 
> Signed-off-by: ZhaoJinming <zhaojinming@uniontech.com>
> ---
> 
> Changes in v3:
> - Extract ath11k_ahb_free_ext_irq_grp() to handle per-group ext IRQ
>   cleanup and refactor ath11k_ahb_free_ext_irq() to use it. Add a NULL
>   check for napi_ndev to avoid potential NULL pointer dereference since
>   free_netdev() does not guard against NULL input.
> - On request_irq() failure in ath11k_ahb_config_ext_irq(), immediately
>   clean up the current group via ath11k_ahb_free_ext_irq_grp() before
>   jumping to the error label to unwind earlier groups.
> - Move u32 num_irq declaration before the irq_grp assignment to follow
>   declaration-before-statement ordering.
> - Extract ath11k_ahb_free_ce_irqs() to handle partial/full CE IRQ cleanup
>   and refactor ath11k_ahb_free_irq() to use it, replacing the duplicated
>   err_ce_irq cleanup loop in ath11k_ahb_config_irq().
> 
> Changes in v2:
> - Move `irq_grp` declaration from for-loop body to function scope to fix
>   compile error in err_request_irq error path.
> - Set ret = -ENOMEM before goto err_request_irq on alloc_netdev_dummy()
>   failure to avoid returning uninitialized value.
> - When ath11k_ahb_config_ext_irq() fails, unwind the already-registered
>   CE IRQs via a shared err_ce_irq cleanup path to avoid leaking them.
> 
> diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
> index f566d699d074..1a9408138d81 100644
> --- a/drivers/net/wireless/ath/ath11k/ahb.c
> +++ b/drivers/net/wireless/ath/ath11k/ahb.c
> @@ -431,36 +431,47 @@ static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab)
>  	ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id;
>  }
>  
> -static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab)
> +static void ath11k_ahb_free_ext_irq_grp(struct ath11k_base *ab,
> +				      struct ath11k_ext_irq_grp *irq_grp)
>  {
> -	int i, j;
> +	int j;
>  
> -	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
> -		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
> +	for (j = 0; j < irq_grp->num_irq; j++)
> +		free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
>  
> -		for (j = 0; j < irq_grp->num_irq; j++)
> -			free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
> +	if (!irq_grp->napi_ndev)
> +		return;


irq_grp->napi_ndev can not be NULL in exsiting code, hence this is not reachable. A silent
guard against a state that cannot happen has real downsides, regardless of what it's guarding:

- It documents a code path that doesn't exist. The next person to touch this will assume
the guarded condition is reachable and may build further logic on that false premise,
spreading a phantom case through the code.
- It discourages proving the invariant. The check silently substitutes for the reasoning
"can this actually occur here?" — the exact analysis a reader should do — which invites
less rigorous code over time.
- It hides bugs and delays them. If the invariant is ever broken by a future change, the
silent early return swallows it instead of failing where the root cause is. What should be
an immediate, on-the-spot failure becomes a displaced, delayed symptom that looks
unrelated to its cause and is far harder to debug. If a "can't happen" condition is
genuinely worth asserting, the right tool is a loud warning — never a quiet return that
masks it.

So I'd drop the guard.

>  
> -		netif_napi_del(&irq_grp->napi);
> -		free_netdev(irq_grp->napi_ndev);
> -	}
> +	netif_napi_del(&irq_grp->napi);
> +	free_netdev(irq_grp->napi_ndev);
>  }
>  
> -static void ath11k_ahb_free_irq(struct ath11k_base *ab)
> +static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab)
>  {
> -	int irq_idx;
>  	int i;
>  
> -	if (ab->hw_params.hybrid_bus_type)
> -		return ath11k_pcic_free_irq(ab);
> +	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++)
> +		ath11k_ahb_free_ext_irq_grp(ab, &ab->ext_irq_grp[i]);
> +}
>  
> -	for (i = 0; i < ab->hw_params.ce_count; i++) {
> +static void ath11k_ahb_free_ce_irqs(struct ath11k_base *ab, int max_idx)
> +{
> +	int irq_idx, i;
> +
> +	for (i = 0; i < max_idx; i++) {
>  		if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
>  			continue;
>  		irq_idx = ATH11K_IRQ_CE0_OFFSET + i;
>  		free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);
>  	}
> +}
> +
> +static void ath11k_ahb_free_irq(struct ath11k_base *ab)
> +{
> +	if (ab->hw_params.hybrid_bus_type)
> +		return ath11k_pcic_free_irq(ab);
>  
> +	ath11k_ahb_free_ce_irqs(ab, ab->hw_params.ce_count);
>  	ath11k_ahb_free_ext_irq(ab);
>  }
>  
> @@ -524,20 +535,25 @@ static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg)
>  static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab)
>  {
>  	struct ath11k_hw_params *hw = &ab->hw_params;
> +	struct ath11k_ext_irq_grp *irq_grp;
>  	int i, j;
>  	int irq;
>  	int ret;
>  
>  	for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
> -		struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
>  		u32 num_irq = 0;
>  
> +		irq_grp = &ab->ext_irq_grp[i];
> +
>  		irq_grp->ab = ab;
>  		irq_grp->grp_id = i;
>  
>  		irq_grp->napi_ndev = alloc_netdev_dummy(0);
> -		if (!irq_grp->napi_ndev)
> -			return -ENOMEM;
> +		if (!irq_grp->napi_ndev) {
> +			ret = -ENOMEM;
> +			irq_grp->num_irq = 0;
> +			goto err_request_irq;
> +		}
>  
>  		netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,
>  			       ath11k_ahb_ext_grp_napi_poll);
> @@ -585,9 +601,7 @@ static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab)
>  				}
>  			}
>  		}
> -		irq_grp->num_irq = num_irq;
> -
> -		for (j = 0; j < irq_grp->num_irq; j++) {
> +		for (j = 0; j < num_irq; j++) {
>  			int irq_idx = irq_grp->irqs[j];
>  
>  			irq = platform_get_irq_byname(ab->pdev,
> @@ -600,11 +614,23 @@ static int ath11k_ahb_config_ext_irq(struct ath11k_base *ab)
>  			if (ret) {
>  				ath11k_err(ab, "failed request_irq for %d\n",
>  					   irq);
> +				irq_grp->num_irq = j;
> +				ath11k_ahb_free_ext_irq_grp(ab, irq_grp);
> +				goto err_request_irq;
>  			}
>  		}
> +
> +		irq_grp->num_irq = num_irq;
>  	}
>  
>  	return 0;
> +
> +err_request_irq:
> +	for (i--; i >= 0; i--) {
> +		irq_grp = &ab->ext_irq_grp[i];
> +		ath11k_ahb_free_ext_irq_grp(ab, irq_grp);
> +	}
> +	return ret;
>  }
>  
>  static int ath11k_ahb_config_irq(struct ath11k_base *ab)
> @@ -629,16 +655,24 @@ static int ath11k_ahb_config_irq(struct ath11k_base *ab)
>  		ret = request_irq(irq, ath11k_ahb_ce_interrupt_handler,
>  				  IRQF_TRIGGER_RISING, irq_name[irq_idx],
>  				  ce_pipe);
> -		if (ret)
> +		if (ret) {
> +			ath11k_err(ab, "failed request_irq for %d\n", irq);
> +			ath11k_ahb_free_ce_irqs(ab, i);
>  			return ret;
> +		}
>  
>  		ab->irq_num[irq_idx] = irq;
>  	}
>  
>  	/* Configure external interrupts */
>  	ret = ath11k_ahb_config_ext_irq(ab);
> +	if (ret) {
> +		ath11k_err(ab, "failed to configure ext irq: %d\n", ret);
> +		ath11k_ahb_free_ce_irqs(ab, ab->hw_params.ce_count);
> +		return ret;
> +	}
>  
> -	return ret;
> +	return 0;
>  }
>  
>  static int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,

others look good to me. Hence with the sanity guard removed:

Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>


^ permalink raw reply

* Re: [PATCH] wifi: rtw89: wow: fix unsupported cipher debug messages
From: Ping-Ke Shih @ 2026-07-17  3:44 UTC (permalink / raw)
  To: zhaoguohan, Ping-Ke Shih, linux-wireless; +Cc: linux-kernel
In-Reply-To: <20260715060813.476245-1-zhaoguohan@kylinos.cn>

zhaoguohan@kylinos.cn wrote:

> From: GuoHan Zhao <zhaoguohan@kylinos.cn>
> 
> Correct two WoWLAN debug messages to say "unsupported cipher".
> 
> Signed-off-by: GuoHan Zhao <zhaoguohan@kylinos.cn>
> Acked-by: Ping-Ke Shih <pkshih@realtek.com>

1 patch(es) applied to rtw-next branch of rtw.git, thanks.

73aecc221e7d wifi: rtw89: wow: fix unsupported cipher debug messages

---
https://github.com/pkshih/rtw.git


^ permalink raw reply

* Re: [PATCH rtw-next 1/8] wifi: rtw89: pack I/O during bb_sethw to reduce API execution time
From: Ping-Ke Shih @ 2026-07-17  3:40 UTC (permalink / raw)
  To: Ping-Ke Shih, linux-wireless; +Cc: echuang, kevin_yang
In-Reply-To: <20260712034506.53209-2-pkshih@realtek.com>

Ping-Ke Shih <pkshih@realtek.com> wrote:

> From: Eric Huang <echuang@realtek.com>
> 
> Wrap rtw89_chip_bb_sethw() with rtw89_io_pack/unpack so all register
> writes during baseband hardware initialization are batched into a single
> bus transaction. This reduces API execution time from ~11000 us to
> ~4000 us on affected platforms.
> 
> Signed-off-by: Eric Huang <echuang@realtek.com>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>

8 patch(es) applied to rtw-next branch of rtw.git, thanks.

b45b22bbe9d5 wifi: rtw89: pack I/O during bb_sethw to reduce API execution time
07c26ead36e7 wifi: rtw89: mac: abstract register definition of firmware boot debug
b9f582e939f6 wifi: rtw89: 8922d: add TX time limit for 2GHz band
ed74acea8320 wifi: rtw89: introduce helper to get tx shape index
7054b03136e4 wifi: rtw89: add tx shape v0 to keep built-in arrays compatible during transitions
b4eaac15cbb4 wifi: rtw89: extend tx shape format for regulatory 6 GHz power type
b5bf2dc96f95 wifi: rtw89: fw: do bb_preinit before downloading firmware
3c15399ef64e wifi: rtw89: debug: add diagnosis for RF

---
https://github.com/pkshih/rtw.git


^ permalink raw reply

* Re: (subset) [PATCH v9 00/14] firmware: qcom: Add OP-TEE PAS service support
From: Bjorn Andersson @ 2026-07-17  3:23 UTC (permalink / raw)
  To: konradybcio, Sumit Garg
  Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
	netdev, linux-wireless, ath12k, linux-remoteproc, robh, krzk+dt,
	conor+dt, robin.clark, sean, akhilpo, lumag, abhinav.kumar,
	jesszhan0024, marijn.suijten, airlied, simona, vikash.garodia,
	bod, mchehab, elder, andrew+netdev, davem, edumazet, kuba, pabeni,
	jjohnson, mathieu.poirier, trilokkumar.soni, mukesh.ojha,
	pavan.kondeti, jorge.ramirez, tonyh, vignesh.viswanathan,
	srinivas.kandagatla, amirreza.zarrabi, jenswi, op-tee, apurupa,
	skare, linux-kernel, Sumit Garg
In-Reply-To: <20260702115835.167602-1-sumit.garg@kernel.org>


On Thu, 02 Jul 2026 17:28:16 +0530, Sumit Garg wrote:
> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
> 
> Qcom platforms has the legacy of using non-standard SCM calls
> splintered over the various kernel drivers. These SCM calls aren't
> compliant with the standard SMC calling conventions which is a
> prerequisite to enable migration to the FF-A specifications from Arm.
> 
> [...]

Applied, thanks!

[11/14] net: ipa: Switch to generic PAS TZ APIs
        commit: 85b88ceeeeb7bb93cd0af7ccaf2a4b010d5bc5c6

Best regards,
-- 
Bjorn Andersson <andersson@kernel.org>

^ permalink raw reply

* Re: [PATCH rtw-next 01/11] wifi: rtw89: coex: Add Wi-Fi role info version 10
From: Ping-Ke Shih @ 2026-07-17  3:07 UTC (permalink / raw)
  To: Ping-Ke Shih, linux-wireless; +Cc: ku920601
In-Reply-To: <20260712030506.43438-2-pkshih@realtek.com>

Ping-Ke Shih <pkshih@realtek.com> wrote:

> From: Ching-Te Ku <ku920601@realtek.com>
> 
> Because the new generation Bluetooth will able to work on 5/6GHz band,
> it will suffer 5/6GHz Wi-Fi, the mechanism need to cover more scenario
> with different Wi-Fi/Bluetooth combination.
> 
> Signed-off-by: Ching-Te Ku <ku920601@realtek.com>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>

11 patch(es) applied to rtw-next branch of rtw.git, thanks.

af7f59e8e876 wifi: rtw89: coex: Add Wi-Fi role info version 10
3b6dd05aee28 wifi: rtw89: coex: Rearrange coexistence control structure
404edeea3b18 wifi: rtw89: coex: Update driver outsource info to firmware version 6
a20edfbc15a5 wifi: rtw89: ceox: Update antenna & grant signal setting
5d5a5eb7ae2e wifi: rtw89: coex: Rearrange Bluetooth firmware report entry
a67bb858ba4d wifi: rtw89: coex: Refine send firmware command function
aaba33e780f5 wifi: rtw89: coex: Refine _reset_btc_var()
f59f0767348a wifi: rtw89: coex: Correct SET_RFE settings
578f9b48276c wifi: rtw89: coex: Add firmware report control report v11
aed0d7771d52 wifi: rtw89: coex: update external control length by case
c2d96cd05c17 wifi: rtw89: coex: Update coexistence version to 9.24.0

---
https://github.com/pkshih/rtw.git


^ permalink raw reply

* [PATCH v3 ath-current] wifi: ath12k: convert scan timeout to wiphy delayed work
From: Runyu Xiao @ 2026-07-17  2:56 UTC (permalink / raw)
  To: Jeff Johnson
  Cc: linux-wireless, ath12k, linux-kernel, Johannes Berg, Jianhao Xu,
	Runyu Xiao, stable

ath12k_mac_op_stop() is called with the wiphy mutex held and calls
ath12k_mac_stop(), which cancels ar->scan.timeout. The timeout worker
also takes the wiphy mutex before aborting the scan, so synchronously
cancelling it from the stop path can deadlock if the worker has started
and is waiting for the same mutex.

Do not drop the wiphy mutex inside the mac80211 stop callback. Convert
ar->scan.timeout to wiphy_delayed_work instead, so the timeout callback
runs in wiphy work context with the wiphy mutex held. This matches the
locking model used by the scan vdev cleanup work and lets stop/cancel
paths use wiphy_delayed_work_cancel() while they already hold the wiphy
mutex.

The old scan-finish path could cancel the delayed work directly from WMI
event context. With wiphy_delayed_work that cancellation must happen from
wiphy context, so keep it in scan.vdev_clean_wk. Mark scans whose cleanup
work has been queued so a timeout work item that was already queued before
cleanup runs does not abort a scan that is already finishing.

Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
Suggested-by: Johannes Berg <johannes@sipsolutions.net>
Cc: stable@vger.kernel.org
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
Changes in v3:
  - Convert scan.timeout to wiphy_delayed_work instead of dropping the
    mac80211-owned wiphy lock in ath12k_mac_op_stop().
  - Cancel the timeout from scan.vdev_clean_wk to preserve the
    scan-finish cancellation from wiphy context.
  - Add scan.finish_queued so an already queued timeout work item does not
    abort a scan that has already queued its cleanup work.

Changes in v2:
  - Rebase on ath.git ath-current.
  - Use ath-current subject tag as requested.
  - Move the synchronous scan timeout drain out of the locked stop path
    instead of adding it in the wrong start-path context.
  - Update Fixes to the ath12k commit that introduced the stop-time
    scan.timeout drain.

 drivers/net/wireless/ath/ath12k/core.c |  2 +-
 drivers/net/wireless/ath/ath12k/core.h |  3 +-
 drivers/net/wireless/ath/ath12k/mac.c  | 50 ++++++++++++++++++--------
 3 files changed, 38 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 742d4fd1b598..95673d3be9da 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1463,7 +1463,7 @@ void ath12k_core_halt(struct ath12k *ar)
 
 	ath12k_mac_scan_finish(ar);
 	ath12k_mac_peer_cleanup_all(ar);
-	cancel_delayed_work_sync(&ar->scan.timeout);
+	wiphy_delayed_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &ar->scan.timeout);
 	cancel_work_sync(&ar->regd_update_work);
 	cancel_work_sync(&ar->regd_channel_update_work);
 	cancel_work_sync(&ab->rfkill_work);
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index fc5127b5c1a3..b6fd5e9dfdb7 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -635,11 +635,12 @@ struct ath12k {
 		struct completion started;
 		struct completion completed;
 		struct completion on_channel;
-		struct delayed_work timeout;
+		struct wiphy_delayed_work timeout;
 		enum ath12k_scan_state state;
 		bool is_roc;
 		int roc_freq;
 		bool roc_notify;
+		bool finish_queued;
 		struct wiphy_work vdev_clean_wk;
 		struct ath12k_link_vif *arvif;
 	} scan;
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 51c4df32e716..f53df8d4c808 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -5162,7 +5162,7 @@ void __ath12k_mac_scan_finish(struct ath12k *ar)
 			ieee80211_remain_on_channel_expired(hw);
 		fallthrough;
 	case ATH12K_SCAN_STARTING:
-		cancel_delayed_work(&ar->scan.timeout);
+		ar->scan.finish_queued = true;
 		complete_all(&ar->scan.completed);
 		wiphy_work_queue(ar->ah->hw->wiphy, &ar->scan.vdev_clean_wk);
 		break;
@@ -5254,14 +5254,23 @@ static void ath12k_scan_abort(struct ath12k *ar)
 	spin_unlock_bh(&ar->data_lock);
 }
 
-static void ath12k_scan_timeout_work(struct work_struct *work)
+static void ath12k_scan_timeout_work(struct wiphy *wiphy,
+				     struct wiphy_work *work)
 {
-	struct ath12k *ar = container_of(work, struct ath12k,
-					 scan.timeout.work);
+	struct wiphy_delayed_work *dwork;
+	struct ath12k *ar;
+
+	dwork = container_of(work, struct wiphy_delayed_work, work);
+	ar = container_of(dwork, struct ath12k, scan.timeout);
+
+	spin_lock_bh(&ar->data_lock);
+	if (ar->scan.finish_queued) {
+		spin_unlock_bh(&ar->data_lock);
+		return;
+	}
+	spin_unlock_bh(&ar->data_lock);
 
-	wiphy_lock(ath12k_ar_to_hw(ar)->wiphy);
 	ath12k_scan_abort(ar);
-	wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy);
 }
 
 static void ath12k_mac_scan_send_complete(struct ath12k *ar,
@@ -5292,6 +5301,8 @@ static void ath12k_scan_vdev_clean_work(struct wiphy *wiphy, struct wiphy_work *
 
 	arvif = ar->scan.arvif;
 
+	wiphy_delayed_work_cancel(wiphy, &ar->scan.timeout);
+
 	/* The scan vdev has already been deleted. This can occur when a
 	 * new scan request is made on the same vif with a different
 	 * frequency, causing the scan arvif to move from one radio to
@@ -5323,6 +5334,7 @@ static void ath12k_scan_vdev_clean_work(struct wiphy *wiphy, struct wiphy_work *
 	}
 
 	ar->scan.state = ATH12K_SCAN_IDLE;
+	ar->scan.finish_queued = false;
 	ar->scan_channel = NULL;
 	ar->scan.roc_freq = 0;
 	spin_unlock_bh(&ar->data_lock);
@@ -5609,6 +5621,7 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
 		reinit_completion(&ar->scan.completed);
 		ar->scan.state = ATH12K_SCAN_STARTING;
 		ar->scan.is_roc = false;
+		ar->scan.finish_queued = false;
 		ar->scan.arvif = arvif;
 		ret = 0;
 		break;
@@ -5665,6 +5678,7 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
 
 		spin_lock_bh(&ar->data_lock);
 		ar->scan.state = ATH12K_SCAN_IDLE;
+		ar->scan.finish_queued = false;
 		spin_unlock_bh(&ar->data_lock);
 		goto exit;
 	}
@@ -5672,9 +5686,10 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
 	ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac scan started");
 
 	/* Add a margin to account for event/command processing */
-	ieee80211_queue_delayed_work(ath12k_ar_to_hw(ar), &ar->scan.timeout,
-				     msecs_to_jiffies(arg->max_scan_time +
-						      ATH12K_MAC_SCAN_TIMEOUT_MSECS));
+	wiphy_delayed_work_queue(ath12k_ar_to_hw(ar)->wiphy,
+				 &ar->scan.timeout,
+				 msecs_to_jiffies(arg->max_scan_time +
+						  ATH12K_MAC_SCAN_TIMEOUT_MSECS));
 
 exit:
 	if (arg) {
@@ -5757,6 +5772,7 @@ int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw,
 				spin_lock_bh(&ar->data_lock);
 				ar->scan.arvif = NULL;
 				ar->scan.state = ATH12K_SCAN_IDLE;
+				ar->scan.finish_queued = false;
 				ar->scan_channel = NULL;
 				ar->scan.roc_freq = 0;
 				spin_unlock_bh(&ar->data_lock);
@@ -5792,7 +5808,7 @@ void ath12k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw,
 
 		ath12k_scan_abort(ar);
 
-		cancel_delayed_work_sync(&ar->scan.timeout);
+		wiphy_delayed_work_cancel(hw->wiphy, &ar->scan.timeout);
 	}
 }
 EXPORT_SYMBOL(ath12k_mac_op_cancel_hw_scan);
@@ -9935,7 +9951,7 @@ static void ath12k_mac_stop(struct ath12k *ar)
 
 	clear_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags);
 
-	cancel_delayed_work_sync(&ar->scan.timeout);
+	wiphy_delayed_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &ar->scan.timeout);
 	wiphy_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &ar->scan.vdev_clean_wk);
 	cancel_work_sync(&ar->regd_channel_update_work);
 	cancel_work_sync(&ar->regd_update_work);
@@ -10962,6 +10978,7 @@ void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw,
 			}
 
 			ar->scan.state = ATH12K_SCAN_IDLE;
+			ar->scan.finish_queued = false;
 			ar->scan_channel = NULL;
 			ar->scan.roc_freq = 0;
 			spin_unlock_bh(&ar->data_lock);
@@ -13876,7 +13893,7 @@ int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw,
 
 	ath12k_scan_abort(ar);
 
-	cancel_delayed_work_sync(&ar->scan.timeout);
+	wiphy_delayed_work_cancel(hw->wiphy, &ar->scan.timeout);
 	wiphy_work_flush(hw->wiphy, &ar->scan.vdev_clean_wk);
 
 	return 0;
@@ -13956,6 +13973,7 @@ int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
 		reinit_completion(&ar->scan.on_channel);
 		ar->scan.state = ATH12K_SCAN_STARTING;
 		ar->scan.is_roc = true;
+		ar->scan.finish_queued = false;
 		ar->scan.arvif = arvif;
 		ar->scan.roc_freq = chan->center_freq;
 		ar->scan.roc_notify = true;
@@ -13998,6 +14016,7 @@ int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
 
 		spin_lock_bh(&ar->data_lock);
 		ar->scan.state = ATH12K_SCAN_IDLE;
+		ar->scan.finish_queued = false;
 		spin_unlock_bh(&ar->data_lock);
 		return ret;
 	}
@@ -14011,8 +14030,8 @@ int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
 		return -ETIMEDOUT;
 	}
 
-	ieee80211_queue_delayed_work(hw, &ar->scan.timeout,
-				     msecs_to_jiffies(duration));
+	wiphy_delayed_work_queue(hw->wiphy, &ar->scan.timeout,
+				 msecs_to_jiffies(duration));
 
 	return 0;
 }
@@ -15052,6 +15071,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
 	ar->num_tx_chains = hweight32(pdev->cap.tx_chain_mask);
 	ar->num_rx_chains = hweight32(pdev->cap.rx_chain_mask);
 	ar->scan.arvif = NULL;
+	ar->scan.finish_queued = false;
 	ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID;
 
 	spin_lock_init(&ar->data_lock);
@@ -15077,7 +15097,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
 	ar->thermal.temperature = 0;
 	ar->thermal.hwmon_dev = NULL;
 
-	INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
+	wiphy_delayed_work_init(&ar->scan.timeout, ath12k_scan_timeout_work);
 	wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work);
 	INIT_WORK(&ar->regd_channel_update_work, ath12k_regd_update_chan_list_work);
 	INIT_LIST_HEAD(&ar->regd_channel_update_queue);
-- 
2.34.1

^ permalink raw reply related

* Re: [PATCH] rtlwifi: rtl8192d: remove dead SMPS rate mask code
From: Ping-Ke Shih @ 2026-07-17  2:40 UTC (permalink / raw)
  To: Chelsy Ratnawat, pkshih; +Cc: linux-wireless, linux-kernel, Chelsy Ratnawat
In-Reply-To: <20260709194315.157030-1-chelsyratnawat2001@gmail.com>

Chelsy Ratnawat <chelsyratnawat2001@gmail.com> wrote:

> mimo_ps is initialized to IEEE80211_SMPS_OFF and never modified in
> rtl92d_update_hal_rate_table(). Therefore, the IEEE80211_SMPS_STATIC
> case is unreachable.
> Remove the unused mimo_ps variable and the dead branch.
> 
> Signed-off-by: Chelsy Ratnawat <chelsyratnawat2001@gmail.com>
> Acked-by: Ping-Ke Shih <pkshih@realtek.com>

1 patch(es) applied to rtw-next branch of rtw.git, thanks.

d4157cd3aeab wifi: rtlwifi: rtl8192d: remove dead SMPS rate mask code

---
https://github.com/pkshih/rtw.git


^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath11k: Avoid buffer overread in ath11k_wmi_tlv_op_rx()
From: Baochen Qiang @ 2026-07-17  2:01 UTC (permalink / raw)
  To: Jeff Johnson, Jeff Johnson; +Cc: linux-wireless, ath11k, linux-kernel
In-Reply-To: <20260716-ath11k_wmi_tlv_op_rx-overread-v1-1-0b972b3f1368@oss.qualcomm.com>



On 7/17/2026 4:01 AM, Jeff Johnson wrote:
> Currently, in ath11k_wmi_tlv_op_rx(), the firmware buffer is read
> without first verifying that the buffer has enough data to hold a
> header. This could result in a buffer overread.
> 
> Add an upfront length check before dereferencing skb->data as a
> wmi_cmd_hdr. The check is placed before the trace_ath11k_wmi_event()
> call to preserve the existing trace semantics (tracing the full raw
> WMI event including the header), unlike the analogous ath12k fix which
> could use skb_pull_data() directly.
> 
> Compile tested only.
> 
> Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
> Assisted-by: Claude:claude-sonnet-4-6
> Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>

Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath12k: Set DTIM policy to stick mode for station interface
From: Baochen Qiang @ 2026-07-17  1:59 UTC (permalink / raw)
  To: Daizhuang Bai, jjohnson; +Cc: ath12k, linux-wireless, linux-kernel
In-Reply-To: <20260717014302.284034-1-daizhuang.bai@oss.qualcomm.com>



On 7/17/2026 9:43 AM, Daizhuang Bai wrote:
> Currently, the station always follows the listen interval regardless of
> the DTIM value. The DTIM function does not work as expected. The default
> value of the listen interval is 5 so that the STA wakes up every 500ms
> when power save is on. This can cause a data transmission delay.
> 
> Set the DTIM policy to DTIM stick mode so that the station follows the AP
> DTIM interval rather than the listen interval, which is set in the peer
> assoc command. DTIM stick mode is preferable per the firmware team's request.
> 
> Apply this only for STA vdevs and only when STA power save is supported, to
> avoid affecting unsupported targets and P2P client vdevs.
> 
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
> 
> Signed-off-by: Daizhuang Bai <daizhuang.bai@oss.qualcomm.com>

Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath12k: Avoid buffer overread in ath12k_wmi_op_rx()
From: Baochen Qiang @ 2026-07-17  1:58 UTC (permalink / raw)
  To: Jeff Johnson, Jeff Johnson; +Cc: linux-wireless, ath12k, linux-kernel
In-Reply-To: <20260716-ath12k_wmi_op_rx-overread-v1-1-327a4b1c2372@oss.qualcomm.com>



On 7/17/2026 4:01 AM, Jeff Johnson wrote:
> Currently, in ath12k_wmi_op_rx(), the firmware buffer is read without
> first verifying that the buffer has enough data to hold a header. This
> could result in a buffer overread.
> 
> Update the logic to verify the buffer contains at least enough data to
> hold a wmi_cmd_hdr before reading from the buffer.
> 
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
> 
> Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
> Assisted-by: Claude:claude-sonnet-4-6
> Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>

Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH ath-current 5/8] wifi: ath12k: introduce host_alloc_ml_id hardware parameter
From: Baochen Qiang @ 2026-07-17  1:54 UTC (permalink / raw)
  To: Rameshkumar Sundaram, Jeff Johnson; +Cc: linux-wireless, ath12k
In-Reply-To: <a906aae4-b695-4f5f-9ee7-51d40700f8c8@oss.qualcomm.com>



On 7/16/2026 10:55 PM, Rameshkumar Sundaram wrote:
> On 7/13/2026 1:03 PM, Baochen Qiang wrote:
>> Different ath12k devices diverge on who allocates MLD peer id:
>> WCN7850/QCC2072 have the firmware allocate it and notify the host via
>> HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP event; While others let the host allocate
>> it and pass it down through WMI_PEER_ASSOC_CMDID with
>> ATH12K_WMI_FLAG_MLO_PEER_ID_VALID set.
>>
>> Currently ath12k host allocates this ID and sends it to firmware by
>> default for all devices. This breaks WCN7850/QCC2072, because the host
>> maintained ID may be different from the firmware-allocated one.
>> Consequently data path may fail to find the dp peer and drop some received
>> packets. From user point of view, this results in bugs reported in [1] or
>> the 4-way handshake timeout issue.
>>
>> Add host_alloc_ml_id flag to struct ath12k_hw_params (and a copy on struct
>> ath12k_hw for hot-path access) so subsequent patches can branch on it. Set
>> true for QCN9274/IPQ5332/IPQ5424, false for WCN7850/QCC2072. The flag will
>> be consumed by subsequent patches.
>>
>> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
>>
>> Link: https://bugzilla.kernel.org/show_bug.cgi?id=221039 # 1
>> Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
>> ---
>>   drivers/net/wireless/ath/ath12k/core.h     |  1 +
>>   drivers/net/wireless/ath/ath12k/hw.h       |  2 ++
>>   drivers/net/wireless/ath/ath12k/mac.c      | 17 ++++++++++++++++-
>>   drivers/net/wireless/ath/ath12k/wifi7/hw.c | 12 ++++++++++++
>>   4 files changed, 31 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/
>> core.h
>> index fc5127b5c1a3..1f56474efbea 100644
>> --- a/drivers/net/wireless/ath/ath12k/core.h
>> +++ b/drivers/net/wireless/ath/ath12k/core.h
>> @@ -793,6 +793,7 @@ struct ath12k_hw {
>>       enum ath12k_hw_state state;
>>       bool regd_updated;
>>       bool use_6ghz_regd;
>> +    bool host_alloc_ml_id;
>>         u8 num_radio;
>>   diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h
>> index d135b2936378..8091501cf742 100644
>> --- a/drivers/net/wireless/ath/ath12k/hw.h
>> +++ b/drivers/net/wireless/ath/ath12k/hw.h
>> @@ -232,6 +232,8 @@ struct ath12k_hw_params {
>>           u32 max_client_dbs;
>>           u32 max_client_dbs_sbs;
>>       } client;
>> +
>> +    bool host_alloc_ml_id;
>>   };
>>     struct ath12k_hw_ops {
>> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
>> index 7d0d7d5fbf53..9e5fcbf8c730 100644
>> --- a/drivers/net/wireless/ath/ath12k/mac.c
>> +++ b/drivers/net/wireless/ath/ath12k/mac.c
>> @@ -15382,8 +15382,9 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
>>       int mac_id, device_id, total_radio, num_hw;
>>       struct ath12k_base *ab;
>>       struct ath12k_hw *ah;
>> -    int ret, i, j;
>> +    bool conf = false;
>>       u8 radio_per_hw;
>> +    int ret, i, j;
>>         total_radio = 0;
>>       for (i = 0; i < ag->num_devices; i++) {
>> @@ -15423,6 +15424,19 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
>>               }
>>                 ab = ag->ab[device_id];
>> +
>> +            /*
>> +             * the assumption is all devices within an ah
>> +             * share the same host_alloc_ml_id configuration
>> +             */
>> +            if (j == 0) {
>> +                conf = ab->hw_params->host_alloc_ml_id;
>> +            } else if (conf != ab->hw_params->host_alloc_ml_id) {
>> +                ath12k_warn(ab, "inconsistent ML ID config within ah, device 0 uses %s
>> allocated ID, while device %u doesn't\n",
>> +                        conf ? "host" : "firmware", device_id);
> 
> a garbage ret will be returned in this err path.

Good catch, will fix in next revision.

> 
>> +                goto err;
>> +            }
>> +
>>               pdev_map[j].ab = ab;
>>               pdev_map[j].pdev_idx = mac_id;
>>               mac_id++;
>> @@ -15447,6 +15461,7 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
>>           }
>>             ah->dev = ab->dev;
>> +        ah->host_alloc_ml_id = conf;
>>             ag->ah[i] = ah;
>>           ag->num_hw++;
> 
> -- 
> Ramesh


^ permalink raw reply

* [PATCH ath-next] wifi: ath12k: Set DTIM policy to stick mode for station interface
From: Daizhuang Bai @ 2026-07-17  1:43 UTC (permalink / raw)
  To: jjohnson; +Cc: ath12k, linux-wireless, linux-kernel, daizhuang.bai

Currently, the station always follows the listen interval regardless of
the DTIM value. The DTIM function does not work as expected. The default
value of the listen interval is 5 so that the STA wakes up every 500ms
when power save is on. This can cause a data transmission delay.

Set the DTIM policy to DTIM stick mode so that the station follows the AP
DTIM interval rather than the listen interval, which is set in the peer
assoc command. DTIM stick mode is preferable per the firmware team's request.

Apply this only for STA vdevs and only when STA power save is supported, to
avoid affecting unsupported targets and P2P client vdevs.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3

Signed-off-by: Daizhuang Bai <daizhuang.bai@oss.qualcomm.com>
---
 drivers/net/wireless/ath/ath12k/mac.c | 11 +++++++++++
 drivers/net/wireless/ath/ath12k/wmi.h |  7 +++++++
 2 files changed, 18 insertions(+)

diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 16339469c24c..706104168650 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -3985,6 +3985,17 @@ static void ath12k_bss_assoc(struct ath12k *ar,
 		ath12k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n",
 			    arvif->vdev_id, ret);
 
+	if (ar->ab->hw_params->supports_sta_ps &&
+	    ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
+	    ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) {
+		ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+						    WMI_VDEV_PARAM_DTIM_POLICY,
+						    WMI_DTIM_POLICY_STICK);
+		if (ret)
+			ath12k_warn(ar->ab, "failed to set vdev %d stick DTIM policy: %d\n",
+				    arvif->vdev_id, ret);
+	}
+
 	if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) &&
 	    ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
 	    ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE)
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index 51f3426e1fcd..0145cc477944 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -2330,6 +2330,13 @@ enum wmi_slot_time {
 	WMI_VDEV_SLOT_TIME_SHORT = 2,
 };
 
+enum wmi_dtim_policy {
+	WMI_DTIM_POLICY_IGNORE = 1,
+	WMI_DTIM_POLICY_NORMAL = 2,
+	WMI_DTIM_POLICY_STICK  = 3,
+	WMI_DTIM_POLICY_AUTO   = 4,
+};
+
 enum wmi_preamble {
 	WMI_VDEV_PREAMBLE_LONG = 1,
 	WMI_VDEV_PREAMBLE_SHORT = 2,

base-commit: 913998f903fb1432c0046c33003db38a9e8bedb1
-- 
2.43.0


^ permalink raw reply related

* RE: [PATCH 0/4] wifi: rtw89: advertise WFA-certified EHT capabilities for 8922a
From: Ping-Ke Shih @ 2026-07-17  1:23 UTC (permalink / raw)
  To: Louis Kotze; +Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260709103138.1269594-1-loukot@gmail.com>

Louis Kotze <loukot@gmail.com> wrote:
> Only the EML capabilities patch has an explicit improvement: in A/B
> testing on my RTL8922AU against a TP-Link Deco BE63, ML association
> without it came up with a single link, and with it both the 5 GHz
> and 6 GHz links are set up (debugfs link-1/link-2, active_links
> behaving as expected for EMLSR), stable across 60+ hour soaks. The
> other three were no-regression only, which is part of why v2 drops
> them.

It is also like other features. 

With this declaration, it will handshake two links in protocol point
of view, so you can see two links in debugfs, but actually it operates
on one of the links since driver isn't implemented to configure
hardware and firmware. 

> v2 is therefore that single patch, and follows.

So, I'd defer this patch. Internally, we are still cooking to enable
this feature.

Ping-Ke


^ permalink raw reply

* [PATCH wireless] mac80211: recalculate TIM when a station enters power save
From: Andrew Pope @ 2026-07-17  1:17 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, lachlan.hodges, simon, Andrew Pope

When an AP buffers frames for a station on its per-station TXQs and the
station subsequently enters power save, sta_ps_start() records the
buffered TIDs in txq_buffered_tids but does not update the TIM. The
station's TIM bit is only ever set when a further frame is buffered
while the station is already asleep
(ieee80211_tx_h_unicast_ps_buf() -> sta_info_recalc_tim()).

If no further downlink frame arrives for that station the beacon
TIM never advertises the buffered traffic. A station relying on the
TIM then remains in doze indefinitely on top of a non-empty queue. Its
TXQs were removed from the scheduler's active list at PS entry, nothing
pages it, and the flow deadlocks until an unrelated event wakes the
station.

Recalculate the TIM at the end of sta_ps_start(), so traffic
already buffered at PS entry is advertised immediately.
sta_info_recalc_tim() already consults txq_buffered_tids, which is
updated above, and is safe in this context (it is already called
from equivalent paths such as the tx handlers and
ieee80211_handle_filtered_frame()).

Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation")
Signed-off-by: Andrew Pope <andrew.pope@morsemicro.com>
---
 net/mac80211/rx.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d9ea19be0..5e26be8e2 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1717,6 +1717,8 @@ static void sta_ps_start(struct sta_info *sta)
 		else
 			clear_bit(tid, &sta->txq_buffered_tids);
 	}
+
+	sta_info_recalc_tim(sta);
 }
 
 static void sta_ps_end(struct sta_info *sta)
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH 00/10] carl9170: 802.11n compliance and driver improvements
From: Jeff Johnson @ 2026-07-17  1:09 UTC (permalink / raw)
  To: Masi Osmani, Christian Lamparter; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6D188CBDBAFC0CE13247A9444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>

On 3/12/2026 3:37 AM, Masi Osmani wrote:
> The carl9170 driver for Atheros AR9170-based USB WiFi adapters has been
> effectively unmaintained since 2016.  While the hardware shipped as
> Draft-N certified, several 802.11n capabilities were never advertised
> to mac80211, diagnostic counters were left as TODO stubs, and some
> hardware features were never wired up.
> 
> This series addresses these gaps in 10 independent patches, ordered
> from simple HT capability flags to more involved PHY programming:
> 
> Patches 1-3: HT capability corrections
>   - Enable SGI_20 (was only SGI_40)
>   - Advertise RX STBC (1 spatial stream)
>   - Document the SMPS handler (replacing bare TODO)
> 
> Patches 4-5: Diagnostic counters
>   - Wire up the RX dropped frame counter
>   - Track PHY errors via debugfs
> 
> Patch 6: TX power calibration
>   - Replace hardcoded 18 dBm with per-channel EEPROM values
> 
> Patch 7: Recovery hardening
>   - Add exponential backoff to prevent restart storms
> 
> Patch 8: Antenna diversity
>   - Enable fast antenna diversity for 2-chain devices
> 
> Patch 9: DFS radar detection
>   - Program radar registers, call ieee80211_radar_detected()
> 
> Patch 10: Runtime IQ calibration
>   - Periodic I/Q recalibration via existing stat_work timer
> 
> All patches are individually compile-tested and have been verified
> on real hardware (AVM Fritz!WLAN USB Stick N, AR9001U) running
> kernel 6.18.12.  Each patch applies and compiles independently on
> top of the previous ones.
> 
> The ath9k PCI driver for the same chipset family serves as the
> primary reference for register programming and capability flags.
> 
> Masi Osmani (10):
>   carl9170: mac80211: enable Short Guard Interval for 20 MHz
>   carl9170: mac80211: advertise RX STBC capability
>   carl9170: mac80211: document spatial multiplexing power save handler
>   carl9170: rx: wire up dropped frame counter
>   carl9170: rx: track PHY errors via debugfs
>   carl9170: phy: populate per-channel TX power from EEPROM
>   carl9170: main: add exponential restart backoff
>   carl9170: phy: enable antenna diversity for 2-chain devices
>   carl9170: fw: enable DFS radar detection
>   carl9170: phy: add periodic runtime IQ calibration
> 
>  drivers/net/wireless/ath/carl9170/carl9170.h |   5 +
>  drivers/net/wireless/ath/carl9170/debug.c    |   2 +
>  drivers/net/wireless/ath/carl9170/fw.c       |   3 +
>  drivers/net/wireless/ath/carl9170/main.c     |  45 ++++-
>  drivers/net/wireless/ath/carl9170/phy.c      | 194 +++++++++++++++++++
>  drivers/net/wireless/ath/carl9170/rx.c       |  23 ++-
>  6 files changed, 261 insertions(+), 11 deletions(-)
> 
> --
> 2.51.0
> 
> 

In addition to the 10 patches in this series you then posted an additional set
of 2 patches (labeled 11/12 and 12/12) and then another set of patches
(labeled 13/16..16/16). This disjoint labeling makes it hard to track the
patches in patchwork since they aren't all associated to the same series:
https://patchwork.kernel.org/project/linux-wireless/list/?submitter=220736

In addition, the 3 separated disjoint series aren't threaded together in my
e-mail client.

Likewise, posting an update to one patch is not handled well either:
[PATCH v2 07/16] carl9170: main: add exponential restart backoff

When adding to a series or updating one or more patches in a series it is a
best practice to send a new version of the entire series so that the current
state of all patches in the series is captured.

That said, 3 of 16 the patches were ACKed by Christian so I've picked up those
three patches into my 'pending' branch:
carl9170: mac80211: document spatial multiplexing power save handler
carl9170: rx: track PHY errors via debugfs
carl9170: cmd: downgrade transient register I/O errors to wiphy_dbg

Note that I added a wifi: prefix to all to align with the wireless patch style
as previously reminded by Johannes:
<https://lore.kernel.org/linux-wireless/9d093d8a8df188c99b9b652bb56a5c1faecef6c7.camel@sipsolutions.net/>

See:
https://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git/log/?h=pending

Once you see those have been accepted I suggest you rebase your work on the
ath.git main branch and then post a v3 series containing all of the remaining
patches, incorporating any feedback and experience gained since they were
originally posted.

/jeff

^ permalink raw reply

* RE: Bugreport - rtw89: RTL8852AE dead-link stall | linux-zen 7.1.3
From: Ping-Ke Shih @ 2026-07-17  0:23 UTC (permalink / raw)
  To: Pochimilco, linux-wireless@vger.kernel.org
In-Reply-To: <3a31da03-039c-4ac5-bcc4-f2715a718fb7@ik.me>

Pochimilco <pochimilco@ik.me> wrote:
> Bugreport  (information collected with the help of AI - i'm not an
> expert but a more less experienced linux user ;) )
> 
> I hope it helps...  thanks, Pochimilco
> -----------------------------------------------------------------
> 
> Title: rtw89: RTL8852AE dead-link stall ("connected but no traffic")
> under linux-zen 7.1.3, absent under linux-lts 6.18
> Product: Networking
> Component: Wireless
> Kernel: 7.1.3-zen1-3-zen (Arch/Garuda)
> Driver: rtw89 (in-kernel), module rtw89_8852ae
> Hardware: ThinkPad L13 Gen 2a (21AB000PCK), Realtek RTL8852AE
> Severity: high (intermittent connectivity loss, ~2-10 min, self-recovering)

A patch [1] is going to be merged into 7.1.4 (I suppose). I think you can
try it again next week.

[1] https://lore.kernel.org/stable/20260716133048.437330106@linuxfoundation.org/


^ permalink raw reply

* [PATCH] wifi: mwifiex: validate HT/VHT element length before storing beacon IE pointers
From: Christopher Kleiner @ 2026-07-17  0:00 UTC (permalink / raw)
  To: briannorris, linux-wireless; +Cc: francesco, netdev, linux-kernel

mwifiex_update_bss_desc_with_ie() stores raw pointers into the beacon
buffer for the HT Capability, HT Operation, VHT Capability and VHT
Operation elements without checking that the element is long enough to
hold the corresponding fixed-size structure. The generic IE loop only
guarantees that the declared element length fits inside the beacon
buffer (bytes_left >= total_ie_len); it does not guarantee that
element_len is large enough for the struct that later consumers copy.

beacon_buf is a tight kmemdup() of the over-the-air IEs. When the
association command is built, mwifiex_cmd_append_11n_tlv() /
mwifiex_cmd_append_11ac_tlv() copy a fixed number of bytes from the
stored pointers (sizeof(struct ieee80211_ht_cap) and friends). A
malicious AP that emits a beacon or probe response ending in a
truncated (e.g. zero-length) HT Capability element leaves bcn_ht_cap
pointing near the end of the slab, and the subsequent copy reads out of
bounds. The leaked bytes are placed into the association request
transmitted back to the AP, disclosing adjacent slab memory; on
CONFIG_KASAN / panic_on_oops kernels it is an out-of-bounds oops.

Commit 685c9b7750bf ("mwifiex: Abort at too short BSS descriptor
element") added such length checks for the FH/DS/CF/IBSS parameter sets
and a few other elements, but did not cover the HT/VHT capability and
operation elements. Validate element_len against the size of the
structure that will be consumed, mirroring those existing checks.

Fixes: 5e6e3a92b9a4 ("wireless: mwifiex: initial commit for Marvell mwifiex driver")
Cc: stable@vger.kernel.org
Signed-off-by: Christopher Kleiner <chris@kleiner.pro>
---
 drivers/net/wireless/marvell/mwifiex/scan.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c
index 97c0ec3b822e..0196c2adfeed 100644
--- a/drivers/net/wireless/marvell/mwifiex/scan.c
+++ b/drivers/net/wireless/marvell/mwifiex/scan.c
@@ -1384,6 +1384,8 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
 							bss_entry->beacon_buf);
 			break;
 		case WLAN_EID_HT_CAPABILITY:
+			if (element_len < sizeof(struct ieee80211_ht_cap))
+				return -EINVAL;
 			bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *)
 					(current_ptr +
 					sizeof(struct ieee_types_header));
@@ -1392,6 +1394,8 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
 					bss_entry->beacon_buf);
 			break;
 		case WLAN_EID_HT_OPERATION:
+			if (element_len < sizeof(struct ieee80211_ht_operation))
+				return -EINVAL;
 			bss_entry->bcn_ht_oper =
 				(struct ieee80211_ht_operation *)(current_ptr +
 					sizeof(struct ieee_types_header));
@@ -1400,6 +1404,8 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
 					bss_entry->beacon_buf);
 			break;
 		case WLAN_EID_VHT_CAPABILITY:
+			if (element_len < sizeof(struct ieee80211_vht_cap))
+				return -EINVAL;
 			bss_entry->disable_11ac = false;
 			bss_entry->bcn_vht_cap =
 				(void *)(current_ptr +
@@ -1409,6 +1415,8 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
 					      bss_entry->beacon_buf);
 			break;
 		case WLAN_EID_VHT_OPERATION:
+			if (element_len < sizeof(struct ieee80211_vht_operation))
+				return -EINVAL;
 			bss_entry->bcn_vht_oper =
 				(void *)(current_ptr +
 					 sizeof(struct ieee_types_header));
-- 
2.43.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