From 3f9553f65d0b77b724565bbe42c4daa3fab57d5c Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Mon, 29 Sep 2025 11:57:18 +0800 Subject: [PATCH 01/80] wifi: rtl8xxxu: Add USB ID 2001:3328 for D-Link AN3U rev. A1 Add USB ID 2001:3328 for D-Link AN3U rev. A1 which is a RTL8192FU-based Wi-Fi adapter. Compile tested only. Cc: stable@vger.kernel.org # 6.6.x Signed-off-by: Zenm Chen Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250929035719.6172-1-zenmchen@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 3ded5952729f..be39463bd6c4 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -8136,6 +8136,9 @@ static const struct usb_device_id dev_table[] = { /* TP-Link TL-WN823N V2 */ {USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0135, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8192fu_fops}, +/* D-Link AN3U rev. A1 */ +{USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3328, 0xff, 0xff, 0xff), + .driver_info = (unsigned long)&rtl8192fu_fops}, #ifdef CONFIG_RTL8XXXU_UNTESTED /* Still supported by rtlwifi */ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8176, 0xff, 0xff, 0xff), From b377dcd9a286a6f81922ae442cd1c743bc4a2b35 Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Mon, 29 Sep 2025 11:57:19 +0800 Subject: [PATCH 02/80] wifi: rtw88: Add USB ID 2001:3329 for D-Link AC13U rev. A1 Add USB ID 2001:3329 for D-Link AC13U rev. A1 which is a RTL8812CU-based Wi-Fi adapter. Compile tested only. Cc: stable@vger.kernel.org # 6.6.x Signed-off-by: Zenm Chen Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250929035719.6172-2-zenmchen@gmail.com --- drivers/net/wireless/realtek/rtw88/rtw8822cu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c index 324fd5c8bfd4..755f76840b12 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c @@ -21,6 +21,8 @@ static const struct usb_device_id rtw_8822cu_id_table[] = { .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, { USB_DEVICE_AND_INTERFACE_INFO(0x13b1, 0x0043, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, /* Alpha - Alpha */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3329, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, /* D-Link AC13U rev. A1 */ {}, }; MODULE_DEVICE_TABLE(usb, rtw_8822cu_id_table); From 8b795fe42927c2df83b5dbc528e610ab662b1024 Mon Sep 17 00:00:00 2001 From: Yu-Chun Lin Date: Tue, 30 Sep 2025 08:05:45 +0800 Subject: [PATCH 03/80] wifi: rtw89: Replace hardcoded strings with helper functions Replace hardcoded strings with 'str_on_off()', 'str_enable_disable()', and 'str_read_write()'. The change improves readability. Signed-off-by: Yu-Chun Lin Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250930000545.2192692-1-eleanor15x@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c | 8 ++++---- drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c | 8 ++++---- drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c | 6 +++--- drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c | 10 +++++----- drivers/net/wireless/realtek/rtw89/usb.c | 2 +- drivers/net/wireless/realtek/rtw89/wow.c | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c index 84c46d2f4d85..e574a9950a3b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c @@ -1626,7 +1626,7 @@ static void _iqk_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, iqk_info->iqk_table_idx[path] = idx; rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%d (PHY%d): / DBCC %s/ %s/ CH%d/ %s\n", - path, phy, rtwdev->dbcc_en ? "on" : "off", + path, phy, str_on_off(rtwdev->dbcc_en), iqk_info->iqk_band[path] == 0 ? "2G" : iqk_info->iqk_band[path] == 1 ? "5G" : "6G", iqk_info->iqk_ch[path], @@ -1901,8 +1901,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n", path, dpk->cur_idx[path], phy, - rtwdev->is_tssi_mode[path] ? "on" : "off", - rtwdev->dbcc_en ? "on" : "off", + str_on_off(rtwdev->is_tssi_mode[path]), + str_on_off(rtwdev->dbcc_en), dpk->bp[path][kidx].band == 0 ? "2G" : dpk->bp[path][kidx].band == 1 ? "5G" : "6G", dpk->bp[path][kidx].ch, @@ -2016,7 +2016,7 @@ static void _dpk_txpwr_bb_force(struct rtw89_dev *rtwdev, rtw89_phy_write32_mask(rtwdev, R_TXPWRB_H + (path << 13), B_TXPWRB_RDY, force); rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d txpwr_bb_force %s\n", - path, force ? "on" : "off"); + path, str_on_off(force)); } static void _dpk_kip_pwr_clk_onoff(struct rtw89_dev *rtwdev, bool turn_on) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c index 9db8713ac99b..e74257d19412 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c @@ -1403,7 +1403,7 @@ static void _iqk_get_ch_info(struct rtw89_dev *rtwdev, path, iqk_info->iqk_ch[path]); rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%d (PHY%d): / DBCC %s/ %s/ CH%d/ %s\n", path, phy, - rtwdev->dbcc_en ? "on" : "off", + str_on_off(rtwdev->dbcc_en), iqk_info->iqk_band[path] == 0 ? "2G" : iqk_info->iqk_band[path] == 1 ? "5G" : "6G", iqk_info->iqk_ch[path], @@ -1881,8 +1881,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n", path, dpk->cur_idx[path], phy, - rtwdev->is_tssi_mode[path] ? "on" : "off", - rtwdev->dbcc_en ? "on" : "off", + str_on_off(rtwdev->is_tssi_mode[path]), + str_on_off(rtwdev->dbcc_en), dpk->bp[path][kidx].band == 0 ? "2G" : dpk->bp[path][kidx].band == 1 ? "5G" : "6G", dpk->bp[path][kidx].ch, @@ -2736,7 +2736,7 @@ static void _dpk_onoff(struct rtw89_dev *rtwdev, MASKBYTE3, 0x6 | val); rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s !!!\n", path, - kidx, dpk->is_dpk_enable && !off ? "enable" : "disable"); + kidx, str_enable_disable(dpk->is_dpk_enable && !off)); } static void _dpk_track(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index 4796588c0256..70b1515c00fa 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -1696,7 +1696,7 @@ static void _dpk_onoff(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, bool o MASKBYTE3, _dpk_order_convert(rtwdev) << 1 | val); rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s !!!\n", path, - kidx, dpk->is_dpk_enable && !off ? "enable" : "disable"); + kidx, str_enable_disable(dpk->is_dpk_enable && !off)); } static void _dpk_one_shot(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, @@ -1763,8 +1763,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n", path, dpk->cur_idx[path], phy, - rtwdev->is_tssi_mode[path] ? "on" : "off", - rtwdev->dbcc_en ? "on" : "off", + str_on_off(rtwdev->is_tssi_mode[path]), + str_on_off(rtwdev->dbcc_en), dpk->bp[path][kidx].band == 0 ? "2G" : dpk->bp[path][kidx].band == 1 ? "5G" : "6G", dpk->bp[path][kidx].ch, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c index b92e2ce4f4ad..e7fd028c5e82 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c @@ -1344,7 +1344,7 @@ static void _iqk_get_ch_info(struct rtw89_dev *rtwdev, path, iqk_info->iqk_ch[path]); rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%d (PHY%d): / DBCC %s/ %s/ CH%d/ %s\n", path, phy, - rtwdev->dbcc_en ? "on" : "off", + str_on_off(rtwdev->dbcc_en), iqk_info->iqk_band[path] == 0 ? "2G" : iqk_info->iqk_band[path] == 1 ? "5G" : "6G", iqk_info->iqk_ch[path], @@ -1920,8 +1920,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n", path, dpk->cur_idx[path], phy, - rtwdev->is_tssi_mode[path] ? "on" : "off", - rtwdev->dbcc_en ? "on" : "off", + str_on_off(rtwdev->is_tssi_mode[path]), + str_on_off(rtwdev->dbcc_en), dpk->bp[path][kidx].band == 0 ? "2G" : dpk->bp[path][kidx].band == 1 ? "5G" : "6G", dpk->bp[path][kidx].ch, @@ -2000,7 +2000,7 @@ static void _dpk_txpwr_bb_force(struct rtw89_dev *rtwdev, u8 path, bool force) rtw89_phy_write32_mask(rtwdev, R_TXPWRB_H + (path << 13), B_TXPWRB_RDY, force); rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d txpwr_bb_force %s\n", - path, force ? "on" : "off"); + path, str_on_off(force)); } static void _dpk_kip_restore(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, @@ -2828,7 +2828,7 @@ static void _dpk_onoff(struct rtw89_dev *rtwdev, B_DPD_MEN, val); rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s !!!\n", path, - kidx, dpk->is_dpk_enable && !off ? "enable" : "disable"); + kidx, str_enable_disable(dpk->is_dpk_enable && !off)); } static void _dpk_track(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 6cf89aee252e..afbd1fb79021 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -55,7 +55,7 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, else if (ret < 0) rtw89_warn(rtwdev, "usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n", - reqtype == RTW89_USB_VENQT_READ ? "read" : "write", + str_read_write(reqtype == RTW89_USB_VENQT_READ), len * 8, addr, ret, le32_to_cpup(rtwusb->vendor_req_buf), attempt); diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 5faa51ad896a..b9fce0be3ab7 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -1248,7 +1248,7 @@ static int rtw89_wow_check_fw_status(struct rtw89_dev *rtwdev, bool wow_enable) mac->wow_ctrl.addr, mac->wow_ctrl.mask); if (ret) rtw89_err(rtwdev, "failed to check wow status %s\n", - wow_enable ? "enabled" : "disabled"); + str_enabled_disabled(wow_enable)); return ret; } From 7aeb8c118ef9c7a19ca9c22a2984f1d6653eff84 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 9 Oct 2025 14:52:39 +0300 Subject: [PATCH 04/80] wifi: rtl8xxxu: Report the signal strength only if it's known These chips don't report the signal strength for many (any?) data frames. When the signal strength is not known, set RX_FLAG_NO_SIGNAL_VAL in order to avoid reporting a signal strength of 0. Tested with RTL8192FU and RTL8192CU. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/cdbe1a18-f6f1-4575-bad9-e85db8101247@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index be39463bd6c4..2d88b7fba317 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -6454,6 +6454,8 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) rtl8xxxu_rx_update_rssi(priv, rx_status, hdr); + } else { + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; } rx_status->mactime = rx_desc->tsfl; @@ -6560,6 +6562,8 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb) rtl8xxxu_rx_update_rssi(priv, rx_status, hdr); + } else { + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; } rx_status->mactime = rx_desc->tsfl; From fb5183aa65f6af7bc63869ad7f7d0e6505a80a58 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 9 Oct 2025 14:53:37 +0300 Subject: [PATCH 05/80] wifi: rtl8xxxu: Dump the efuse right after reading it Someone reported a problem with their TP-Link WN722N (RTL8188EUS): usb 1-1: This Realtek USB WiFi dongle (0x2357:0x010c) is untested! usb 1-1: Please report results to Jes.Sorensen@gmail.com usb 1-1: Fatal - failed to parse EFuse rtl8xxxu: probe of 1-1:1.0 failed with error -22 This error can happen when the first two bytes of the efuse don't have the expected magic value of 0x8129. In a situation like this it could be useful to see the contents of the efuse. Dump the efuse right after reading it, before trying to parse it. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/1f544dc4-17f6-4401-995d-5cb4567b82fa@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 2d88b7fba317..cf631df54fa5 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -7910,15 +7910,15 @@ static int rtl8xxxu_probe(struct usb_interface *interface, goto err_set_intfdata; } + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE) + rtl8xxxu_dump_efuse(priv); + ret = priv->fops->parse_efuse(priv); if (ret) { dev_err(&udev->dev, "Fatal - failed to parse EFuse\n"); goto err_set_intfdata; } - if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE) - rtl8xxxu_dump_efuse(priv); - rtl8xxxu_print_chipinfo(priv); ret = priv->fops->load_firmware(priv); From 9b567039e540705c5cf0f39a3344086b6f51b3dd Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 9 Oct 2025 14:55:08 +0300 Subject: [PATCH 06/80] wifi: rtl8xxxu: Use correct power off sequence for RTL8192CU RTL8192CU disappears and reappears when rtl8xxxu is unloaded: usbcore: deregistering interface driver rtl8xxxu wlp3s0f3u2: deauthenticating from ... by local choice (Reason: 3=DEAUTH_LEAVING) usb 1-2: rtl8xxxu_active_to_emu: Disabling MAC timed out usb 1-2: USB disconnect, device number 7 usb 1-2: disconnecting usb 1-2: new high-speed USB device number 8 using xhci_hcd usb 1-2: New USB device found, idVendor=0bda, idProduct=8178, bcdDevice= 2.00 usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-2: Product: 802.11n WLAN Adapter usb 1-2: Manufacturer: Realtek usb 1-2: SerialNumber: 00e04c000001 This is because rtl8xxxu is using the power off sequence for RTL8723AU. Add the correct power off sequence for RTL8192CU. rtl8xxxu_power_off(), rtl8xxxu_active_to_emu(), and rtl8xxxu_emu_to_disabled() are now only used for RTL8723AU, so move them to 8723a.c and rename them to have the "rtl8723au" prefix. Tested only with RTL8192CU. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/b9d3f137-12ce-4bd9-8ada-3b8874bc3615@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/8192c.c | 80 +++++++++++- drivers/net/wireless/realtek/rtl8xxxu/8723a.c | 115 +++++++++++++++- drivers/net/wireless/realtek/rtl8xxxu/core.c | 123 ------------------ drivers/net/wireless/realtek/rtl8xxxu/regs.h | 1 + .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 1 - 5 files changed, 194 insertions(+), 126 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c index 73034e7e41d1..444872131c66 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c @@ -593,6 +593,84 @@ static int rtl8192cu_power_on(struct rtl8xxxu_priv *priv) return 0; } +static void rtl8192cu_power_off(struct rtl8xxxu_priv *priv) +{ + u32 val32; + u16 val16; + u8 val8; + int i; + + /* + * Workaround for 8188RU LNA power leakage problem. + */ + if (priv->rtl_chip == RTL8188R) { + val32 = rtl8xxxu_read32(priv, REG_FPGA0_XCD_RF_PARM); + val32 |= BIT(1); + rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32); + } + + /* _DisableRFAFEAndResetBB */ + rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff); + rtl8xxxu_write_rfreg_mask(priv, RF_A, RF6052_REG_AC, 0xff, 0); + + rtl8xxxu_write8_set(priv, REG_APSD_CTRL, APSD_CTRL_OFF); + rtl8xxxu_write32_set(priv, REG_FPGA0_XCD_RF_PARM, FPGA0_RF_PARM_CLK_GATE); + + rtl8xxxu_write8(priv, REG_SYS_FUNC, + SYS_FUNC_USBA | SYS_FUNC_USBD | SYS_FUNC_BB_GLB_RSTN); + rtl8xxxu_write8(priv, REG_SYS_FUNC, SYS_FUNC_USBA | SYS_FUNC_USBD); + + /* _ResetDigitalProcedure1 */ + if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_DL_READY) { + rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00); + + rtl8xxxu_write8(priv, REG_FWIMR, 0x20); + + rtl8xxxu_write8(priv, REG_HMTFR + 3, 0x20); + + for (i = 0; i < 100; i++) { + val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); + if (!(val16 & SYS_FUNC_CPU_ENABLE)) + break; + + fsleep(50); + } + + if (i == 100) { + rtl8xxxu_write8(priv, REG_SYS_FUNC + 1, + (SYS_FUNC_HWPDN | SYS_FUNC_ELDR) >> 8); + msleep(10); + } + } + + val8 = (SYS_FUNC_HWPDN | SYS_FUNC_ELDR | SYS_FUNC_CPU_ENABLE) >> 8; + rtl8xxxu_write8(priv, REG_SYS_FUNC + 1, val8); + + /* _DisableGPIO */ + rtl8xxxu_write16(priv, REG_GPIO_PIN_CTRL + 2, 0); + val32 = rtl8xxxu_read32(priv, REG_GPIO_PIN_CTRL) & 0xffff00ff; + val32 |= (val32 & 0xff) << 8; + val32 |= 0x00ff0000; + rtl8xxxu_write32(priv, REG_GPIO_PIN_CTRL, val32); + + rtl8xxxu_write8(priv, REG_GPIO_MUXCFG + 3, 0); + val16 = rtl8xxxu_read16(priv, REG_GPIO_MUXCFG + 2) & 0xff0f; + val16 |= (val16 & 0xf) << 4; + val16 |= 0x0780; + rtl8xxxu_write16(priv, REG_GPIO_MUXCFG + 2, val16); + + /* _DisableAnalog */ + val8 = 0x23; + if (priv->vendor_umc && priv->chip_cut == 1) + val8 |= BIT(3); + rtl8xxxu_write8(priv, REG_SPS0_CTRL, val8); + + val16 = APS_FSMCO_HOST | APS_FSMCO_HW_SUSPEND | APS_FSMCO_PFM_ALDN; + rtl8xxxu_write16(priv, REG_APS_FSMCO, val16); + + rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e); +} + static int rtl8192cu_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { @@ -618,7 +696,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .parse_efuse = rtl8192cu_parse_efuse, .load_firmware = rtl8192cu_load_firmware, .power_on = rtl8192cu_power_on, - .power_off = rtl8xxxu_power_off, + .power_off = rtl8192cu_power_off, .read_efuse = rtl8xxxu_read_efuse, .reset_8051 = rtl8xxxu_reset_8051, .llt_init = rtl8xxxu_init_llt_table, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c index ecbc324e4609..4f4493d0bfc2 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c @@ -411,6 +411,119 @@ static int rtl8723au_power_on(struct rtl8xxxu_priv *priv) return ret; } +static int rtl8723au_active_to_emu(struct rtl8xxxu_priv *priv) +{ + u8 val8; + int count, ret = 0; + + /* Start of rtl8723AU_card_enable_flow */ + /* Act to Cardemu sequence*/ + /* Turn off RF */ + rtl8xxxu_write8(priv, REG_RF_CTRL, 0); + + /* 0x004E[7] = 0, switch DPDT_SEL_P output from register 0x0065[2] */ + val8 = rtl8xxxu_read8(priv, REG_LEDCFG2); + val8 &= ~LEDCFG2_DPDT_SELECT; + rtl8xxxu_write8(priv, REG_LEDCFG2, val8); + + /* 0x0005[1] = 1 turn off MAC by HW state machine*/ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 |= BIT(1); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + if ((val8 & BIT(1)) == 0) + break; + udelay(10); + } + + if (!count) { + dev_warn(&priv->udev->dev, "%s: Disabling MAC timed out\n", + __func__); + ret = -EBUSY; + goto exit; + } + + /* 0x0000[5] = 1 analog Ips to digital, 1:isolation */ + val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL); + val8 |= SYS_ISO_ANALOG_IPS; + rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8); + + /* 0x0020[0] = 0 disable LDOA12 MACRO block*/ + val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL); + val8 &= ~LDOA15_ENABLE; + rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8); + +exit: + return ret; +} + +static int rtl8723au_emu_to_disabled(struct rtl8xxxu_priv *priv) +{ + u8 val8; + + /* 0x0007[7:0] = 0x20 SOP option to disable BG/MB */ + rtl8xxxu_write8(priv, REG_APS_FSMCO + 3, 0x20); + + /* 0x04[12:11] = 01 enable WL suspend */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~BIT(4); + val8 |= BIT(3); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 |= BIT(7); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* 0x48[16] = 1 to enable GPIO9 as EXT wakeup */ + val8 = rtl8xxxu_read8(priv, REG_GPIO_INTM + 2); + val8 |= BIT(0); + rtl8xxxu_write8(priv, REG_GPIO_INTM + 2, val8); + + return 0; +} + +static void rtl8723au_power_off(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u16 val16; + + rtl8xxxu_flush_fifo(priv); + + rtl8xxxu_active_to_lps(priv); + + /* Turn off RF */ + rtl8xxxu_write8(priv, REG_RF_CTRL, 0x00); + + /* Reset Firmware if running in RAM */ + if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL) + rtl8xxxu_firmware_self_reset(priv); + + /* Reset MCU */ + val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); + val16 &= ~SYS_FUNC_CPU_ENABLE; + rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); + + /* Reset MCU ready status */ + rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00); + + rtl8723au_active_to_emu(priv); + rtl8723au_emu_to_disabled(priv); + + /* Reset MCU IO Wrapper */ + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1); + val8 &= ~BIT(0); + rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8); + + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1); + val8 |= BIT(0); + rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8); + + /* RSV_CTRL 0x1C[7:0] = 0x0e lock ISO/CLK/Power control register */ + rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e); +} + #define XTAL1 GENMASK(23, 18) #define XTAL0 GENMASK(17, 12) @@ -492,7 +605,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = { .parse_efuse = rtl8723au_parse_efuse, .load_firmware = rtl8723au_load_firmware, .power_on = rtl8723au_power_on, - .power_off = rtl8xxxu_power_off, + .power_off = rtl8723au_power_off, .read_efuse = rtl8xxxu_read_efuse, .reset_8051 = rtl8xxxu_reset_8051, .llt_init = rtl8xxxu_init_llt_table, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index cf631df54fa5..d82cb7714314 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -3637,54 +3637,6 @@ static void rtl8xxxu_set_ampdu_min_space(struct rtl8xxxu_priv *priv, u8 density) rtl8xxxu_write8(priv, REG_AMPDU_MIN_SPACE, val8); } -static int rtl8xxxu_active_to_emu(struct rtl8xxxu_priv *priv) -{ - u8 val8; - int count, ret = 0; - - /* Start of rtl8723AU_card_enable_flow */ - /* Act to Cardemu sequence*/ - /* Turn off RF */ - rtl8xxxu_write8(priv, REG_RF_CTRL, 0); - - /* 0x004E[7] = 0, switch DPDT_SEL_P output from register 0x0065[2] */ - val8 = rtl8xxxu_read8(priv, REG_LEDCFG2); - val8 &= ~LEDCFG2_DPDT_SELECT; - rtl8xxxu_write8(priv, REG_LEDCFG2, val8); - - /* 0x0005[1] = 1 turn off MAC by HW state machine*/ - val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); - val8 |= BIT(1); - rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); - - for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { - val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); - if ((val8 & BIT(1)) == 0) - break; - udelay(10); - } - - if (!count) { - dev_warn(&priv->udev->dev, "%s: Disabling MAC timed out\n", - __func__); - ret = -EBUSY; - goto exit; - } - - /* 0x0000[5] = 1 analog Ips to digital, 1:isolation */ - val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL); - val8 |= SYS_ISO_ANALOG_IPS; - rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8); - - /* 0x0020[0] = 0 disable LDOA12 MACRO block*/ - val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL); - val8 &= ~LDOA15_ENABLE; - rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8); - -exit: - return ret; -} - int rtl8xxxu_active_to_lps(struct rtl8xxxu_priv *priv) { u8 val8; @@ -3761,31 +3713,6 @@ void rtl8xxxu_disabled_to_emu(struct rtl8xxxu_priv *priv) rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); } -static int rtl8xxxu_emu_to_disabled(struct rtl8xxxu_priv *priv) -{ - u8 val8; - - /* 0x0007[7:0] = 0x20 SOP option to disable BG/MB */ - rtl8xxxu_write8(priv, REG_APS_FSMCO + 3, 0x20); - - /* 0x04[12:11] = 01 enable WL suspend */ - val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); - val8 &= ~BIT(4); - val8 |= BIT(3); - rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); - - val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); - val8 |= BIT(7); - rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); - - /* 0x48[16] = 1 to enable GPIO9 as EXT wakeup */ - val8 = rtl8xxxu_read8(priv, REG_GPIO_INTM + 2); - val8 |= BIT(0); - rtl8xxxu_write8(priv, REG_GPIO_INTM + 2, val8); - - return 0; -} - int rtl8xxxu_flush_fifo(struct rtl8xxxu_priv *priv) { struct device *dev = &priv->udev->dev; @@ -3863,56 +3790,6 @@ void rtl8xxxu_gen2_usb_quirks(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32); } -void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv) -{ - u8 val8; - u16 val16; - u32 val32; - - /* - * Workaround for 8188RU LNA power leakage problem. - */ - if (priv->rtl_chip == RTL8188R) { - val32 = rtl8xxxu_read32(priv, REG_FPGA0_XCD_RF_PARM); - val32 |= BIT(1); - rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32); - } - - rtl8xxxu_flush_fifo(priv); - - rtl8xxxu_active_to_lps(priv); - - /* Turn off RF */ - rtl8xxxu_write8(priv, REG_RF_CTRL, 0x00); - - /* Reset Firmware if running in RAM */ - if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL) - rtl8xxxu_firmware_self_reset(priv); - - /* Reset MCU */ - val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); - val16 &= ~SYS_FUNC_CPU_ENABLE; - rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); - - /* Reset MCU ready status */ - rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00); - - rtl8xxxu_active_to_emu(priv); - rtl8xxxu_emu_to_disabled(priv); - - /* Reset MCU IO Wrapper */ - val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1); - val8 &= ~BIT(0); - rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8); - - val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1); - val8 |= BIT(0); - rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8); - - /* RSV_CTRL 0x1C[7:0] = 0x0e lock ISO/CLK/Power control register */ - rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e); -} - void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv, u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5) { diff --git a/drivers/net/wireless/realtek/rtl8xxxu/regs.h b/drivers/net/wireless/realtek/rtl8xxxu/regs.h index 61c0c0ec07b3..0741db8d08bf 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/regs.h @@ -40,6 +40,7 @@ #define APS_FSMCO_SW_LPS BIT(10) #define APS_FSMCO_HW_SUSPEND BIT(11) #define APS_FSMCO_PCIE BIT(12) +#define APS_FSMCO_HOST BIT(14) #define APS_FSMCO_HW_POWERDOWN BIT(15) #define APS_FSMCO_WLON_RESET BIT(16) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index f42463e595cc..9fb2583ffffc 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -2078,7 +2078,6 @@ int rtl8xxxu_init_phy_regs(struct rtl8xxxu_priv *priv, const struct rtl8xxxu_reg32val *array); int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, const char *fw_name); void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv); -void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv); void rtl8xxxu_identify_vendor_1bit(struct rtl8xxxu_priv *priv, u32 vendor); void rtl8xxxu_identify_vendor_2bits(struct rtl8xxxu_priv *priv, u32 vendor); void rtl8xxxu_config_endpoints_sie(struct rtl8xxxu_priv *priv); From b47d748110fd2c2911b59b169b10cc14fd5d3648 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 21 Oct 2025 21:33:55 +0800 Subject: [PATCH 07/80] wifi: rtw89: splice C2H events queue to local to prevent racing RX task enqueues C2H events and fork a C2H work to handle events, but the work uses skb_queue_walk_safe() without a lock causing potential racing. Use skb_queue_splice() and its friends with spin_lock to splice the queue to local, and then still use skb_queue_walk_safe() to iterate all events. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251021133402.15467-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 31 ++++++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ab904a7def1b..9a926bb2cf00 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6891,11 +6891,18 @@ void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work) struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, c2h_work); struct sk_buff *skb, *tmp; + struct sk_buff_head c2hq; + unsigned long flags; lockdep_assert_wiphy(rtwdev->hw->wiphy); - skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) { - skb_unlink(skb, &rtwdev->c2h_queue); + __skb_queue_head_init(&c2hq); + + spin_lock_irqsave(&rtwdev->c2h_queue.lock, flags); + skb_queue_splice_init(&rtwdev->c2h_queue, &c2hq); + spin_unlock_irqrestore(&rtwdev->c2h_queue.lock, flags); + + skb_queue_walk_safe(&c2hq, skb, tmp) { rtw89_fw_c2h_cmd_handle(rtwdev, skb); dev_kfree_skb_any(skb); } @@ -6905,18 +6912,20 @@ void rtw89_fw_c2h_purge_obsoleted_scan_events(struct rtw89_dev *rtwdev) { struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct sk_buff *skb, *tmp; - int limit; + struct sk_buff_head c2hq; + unsigned long flags; lockdep_assert_wiphy(rtwdev->hw->wiphy); - limit = skb_queue_len(&rtwdev->c2h_queue); + __skb_queue_head_init(&c2hq); - skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) { + spin_lock_irqsave(&rtwdev->c2h_queue.lock, flags); + skb_queue_splice_init(&rtwdev->c2h_queue, &c2hq); + spin_unlock_irqrestore(&rtwdev->c2h_queue.lock, flags); + + skb_queue_walk_safe(&c2hq, skb, tmp) { struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb); - if (--limit < 0) - return; - if (!attr->is_scan_event || attr->scan_seq == scan_info->seq) continue; @@ -6924,9 +6933,13 @@ void rtw89_fw_c2h_purge_obsoleted_scan_events(struct rtw89_dev *rtwdev) "purge obsoleted scan event with seq=%d (cur=%d)\n", attr->scan_seq, scan_info->seq); - skb_unlink(skb, &rtwdev->c2h_queue); + __skb_unlink(skb, &c2hq); dev_kfree_skb_any(skb); } + + spin_lock_irqsave(&rtwdev->c2h_queue.lock, flags); + skb_queue_splice(&c2hq, &rtwdev->c2h_queue); + spin_unlock_irqrestore(&rtwdev->c2h_queue.lock, flags); } static int rtw89_fw_write_h2c_reg(struct rtw89_dev *rtwdev, From f44a9b14a78f1e2e2c57dbc1f1e93889ad2f6ac0 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 21 Oct 2025 21:33:56 +0800 Subject: [PATCH 08/80] wifi: rtw89: use skb_dequeue() for queued ROC packets to prevent racing TX task can enqueue ROC skb, but other tasks dequeue the skb. Using skb_queue_walk_safe() without locking will cause potential racing. Use skb_dequeue() with lock instead. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251021133402.15467-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 917b2adede61..8b40cada4149 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3632,12 +3632,10 @@ void rtw89_core_free_sta_pending_roc_tx(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta) { struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); - struct sk_buff *skb, *tmp; + struct sk_buff *skb; - skb_queue_walk_safe(&rtwsta->roc_queue, skb, tmp) { - skb_unlink(skb, &rtwsta->roc_queue); + while ((skb = skb_dequeue(&rtwsta->roc_queue))) dev_kfree_skb_any(skb); - } } static void rtw89_core_stop_tx_ba_session(struct rtw89_dev *rtwdev, @@ -3881,8 +3879,8 @@ static void rtw89_core_sta_pending_tx_iter(void *data, struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); struct rtw89_vif_link *target = data; struct rtw89_vif_link *rtwvif_link; - struct sk_buff *skb, *tmp; unsigned int link_id; + struct sk_buff *skb; int qsel, ret; rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) @@ -3895,9 +3893,7 @@ static void rtw89_core_sta_pending_tx_iter(void *data, if (skb_queue_len(&rtwsta->roc_queue) == 0) return; - skb_queue_walk_safe(&rtwsta->roc_queue, skb, tmp) { - skb_unlink(skb, &rtwsta->roc_queue); - + while ((skb = skb_dequeue(&rtwsta->roc_queue))) { ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel); if (ret) { rtw89_warn(rtwdev, "pending tx failed with %d\n", ret); From e79382ab0347b1d7f9f26d27cb4645e4f053bcc0 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 21 Oct 2025 21:33:57 +0800 Subject: [PATCH 09/80] wifi: rtw89: mlo: handle needed H2C when link switching is requested by stack To switch link, FW needs H2C commands to indicate which link is on or off. Originally, these H2C commands are considered only when the link switching is initiated by driver. But, in some cases, e.g. ml_reconf or TTLM, link switching would be initiated by stack. Hence, plan these H2C commands into ieee80211_ops. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251021133402.15467-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 18 +----- drivers/net/wireless/realtek/rtw89/core.h | 8 +++ drivers/net/wireless/realtek/rtw89/mac80211.c | 64 ++++++++++++++++++- 3 files changed, 74 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 8b40cada4149..a3f156bf708e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6010,7 +6010,7 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); u16 usable_links = ieee80211_vif_usable_links(vif); u16 active_links = vif->active_links; - struct rtw89_vif_link *target, *cur; + struct rtw89_vif_link *target; int ret; lockdep_assert_wiphy(rtwdev->hw->wiphy); @@ -6036,11 +6036,9 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, ieee80211_stop_queues(rtwdev->hw); flush_work(&rtwdev->txq_work); - cur = rtw89_get_designated_link(rtwvif); - - ret = ieee80211_set_active_links(vif, active_links | BIT(link_id)); + ret = ieee80211_set_active_links(vif, BIT(link_id)); if (ret) { - rtw89_err(rtwdev, "%s: failed to activate link id %u\n", + rtw89_err(rtwdev, "%s: failed to work on link id %u\n", __func__, link_id); goto wake_queue; } @@ -6055,16 +6053,6 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, goto wake_queue; } - if (likely(cur)) - rtw89_fw_h2c_mlo_link_cfg(rtwdev, cur, false); - - rtw89_fw_h2c_mlo_link_cfg(rtwdev, target, true); - - ret = ieee80211_set_active_links(vif, BIT(link_id)); - if (ret) - rtw89_err(rtwdev, "%s: failed to inactivate links 0x%x\n", - __func__, active_links); - rtw89_chip_rfk_channel(rtwdev, target); rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 928c8c84c964..7c74ed3cfaf7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5933,6 +5933,7 @@ struct rtw89_mcc_info { enum rtw89_mlo_mode { RTW89_MLO_MODE_MLSR = 0, + RTW89_MLO_MODE_EMLSR = 1, NUM_OF_RTW89_MLO_MODE, }; @@ -6097,6 +6098,12 @@ struct rtw89_link_conf_container { struct ieee80211_bss_conf *link_conf[IEEE80211_MLD_MAX_NUM_LINKS]; }; +struct rtw89_vif_ml_trans { + u16 mediate_links; + u16 links_to_del; + u16 links_to_add; +}; + #define RTW89_VIF_IDLE_LINK_ID 0 struct rtw89_vif { @@ -6119,6 +6126,7 @@ struct rtw89_vif { bool offchan; enum rtw89_mlo_mode mlo_mode; + struct rtw89_vif_ml_trans ml_trans; struct list_head dlink_pool; u8 links_inst_valid_num; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 7b04183a3a5d..a19304ff8306 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -718,6 +718,17 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ARP_FILTER) rtwvif->ip_addr = vif->cfg.arp_addr_list[0]; + + if (changed & BSS_CHANGED_MLD_VALID_LINKS) { + struct rtw89_vif_link *cur = rtw89_get_designated_link(rtwvif); + + rtw89_chip_rfk_channel(rtwdev, cur); + + if (hweight16(vif->active_links) == 1) + rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; + else + rtwvif->mlo_mode = RTW89_MLO_MODE_EMLSR; + } } static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, @@ -1531,10 +1542,29 @@ static bool rtw89_ops_can_activate_links(struct ieee80211_hw *hw, u16 active_links) { struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); + u16 current_links = vif->active_links; + struct rtw89_vif_ml_trans trans = { + .mediate_links = current_links | active_links, + .links_to_del = current_links & ~active_links, + .links_to_add = active_links & ~current_links, + }; lockdep_assert_wiphy(hw->wiphy); - return rtw89_can_work_on_links(rtwdev, vif, active_links); + if (!rtw89_can_work_on_links(rtwdev, vif, active_links)) + return false; + + /* + * Leave LPS at the beginning of ieee80211_set_active_links(). + * Because the entire process takes the same lock as our track + * work, LPS will not enter during ieee80211_set_active_links(). + */ + rtw89_leave_lps(rtwdev); + + rtwvif->ml_trans = trans; + + return true; } static void __rtw89_ops_clr_vif_links(struct rtw89_dev *rtwdev, @@ -1579,6 +1609,36 @@ static int __rtw89_ops_set_vif_links(struct rtw89_dev *rtwdev, return 0; } +static void rtw89_vif_cfg_fw_links(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + unsigned long links, bool en) +{ + struct rtw89_vif_link *rtwvif_link; + unsigned int link_id; + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + rtwvif_link = rtwvif->links[link_id]; + if (unlikely(!rtwvif_link)) + continue; + + rtw89_fw_h2c_mlo_link_cfg(rtwdev, rtwvif_link, en); + } +} + +static void rtw89_vif_update_fw_links(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + u16 current_links) +{ + struct rtw89_vif_ml_trans *trans = &rtwvif->ml_trans; + + /* Do follow-up when all updating links exist. */ + if (current_links != trans->mediate_links) + return; + + rtw89_vif_cfg_fw_links(rtwdev, rtwvif, trans->links_to_del, false); + rtw89_vif_cfg_fw_links(rtwdev, rtwvif, trans->links_to_add, true); +} + static int rtw89_ops_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1620,6 +1680,8 @@ int rtw89_ops_change_vif_links(struct ieee80211_hw *hw, if (rtwdev->scanning) rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif); + rtw89_vif_update_fw_links(rtwdev, rtwvif, old_links); + if (!old_links) __rtw89_ops_clr_vif_links(rtwdev, rtwvif, BIT(RTW89_VIF_IDLE_LINK_ID)); From 438c9178cd8ab629025374ea598b060024a410e7 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 21 Oct 2025 21:33:58 +0800 Subject: [PATCH 10/80] wifi: rtw89: support EHT rate pattern via bitrate mask When setting bitrate mask, e.g. using iw set bitrates, there are some designated patterns to be recognized, called rate pattern. When a rate pattern is matched, treat the setting as asking a fixed rate. Now, add support to recognize EHT rates. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251021133402.15467-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 39 +++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index ba7feadd7582..23892c1359a5 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -231,7 +231,12 @@ static u64 rtw89_phy_ra_mask_cfg(struct rtw89_dev *rtwdev, return -1; } - if (link_sta->he_cap.has_he) { + if (link_sta->eht_cap.has_eht) { + cfg_mask |= u64_encode_bits(mask->control[band].eht_mcs[0], + RA_MASK_EHT_1SS_RATES); + cfg_mask |= u64_encode_bits(mask->control[band].eht_mcs[1], + RA_MASK_EHT_2SS_RATES); + } else if (link_sta->he_cap.has_he) { cfg_mask |= u64_encode_bits(mask->control[band].he_mcs[0], RA_MASK_HE_1SS_RATES); cfg_mask |= u64_encode_bits(mask->control[band].he_mcs[1], @@ -557,6 +562,14 @@ static bool __check_rate_pattern(struct rtw89_phy_rate_pattern *next, return true; } +enum __rtw89_hw_rate_invalid_bases { + /* no EHT rate for ax chip */ + RTW89_HW_RATE_EHT_NSS1_MCS0 = RTW89_HW_RATE_INVAL, + RTW89_HW_RATE_EHT_NSS2_MCS0 = RTW89_HW_RATE_INVAL, + RTW89_HW_RATE_EHT_NSS3_MCS0 = RTW89_HW_RATE_INVAL, + RTW89_HW_RATE_EHT_NSS4_MCS0 = RTW89_HW_RATE_INVAL, +}; + #define RTW89_HW_RATE_BY_CHIP_GEN(rate) \ { \ [RTW89_CHIP_AX] = RTW89_HW_RATE_ ## rate, \ @@ -572,6 +585,12 @@ void __rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev, struct rtw89_phy_rate_pattern next_pattern = {0}; const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); + static const u16 hw_rate_eht[][RTW89_CHIP_GEN_NUM] = { + RTW89_HW_RATE_BY_CHIP_GEN(EHT_NSS1_MCS0), + RTW89_HW_RATE_BY_CHIP_GEN(EHT_NSS2_MCS0), + RTW89_HW_RATE_BY_CHIP_GEN(EHT_NSS3_MCS0), + RTW89_HW_RATE_BY_CHIP_GEN(EHT_NSS4_MCS0), + }; static const u16 hw_rate_he[][RTW89_CHIP_GEN_NUM] = { RTW89_HW_RATE_BY_CHIP_GEN(HE_NSS1_MCS0), RTW89_HW_RATE_BY_CHIP_GEN(HE_NSS2_MCS0), @@ -596,6 +615,17 @@ void __rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev, u8 tx_nss = rtwdev->hal.tx_nss; u8 i; + if (chip_gen == RTW89_CHIP_AX) + goto rs_11ax; + + for (i = 0; i < tx_nss; i++) + if (!__check_rate_pattern(&next_pattern, hw_rate_eht[i][chip_gen], + RA_MASK_EHT_RATES, RTW89_RA_MODE_EHT, + mask->control[nl_band].eht_mcs[i], + 0, true)) + goto out; + +rs_11ax: for (i = 0; i < tx_nss; i++) if (!__check_rate_pattern(&next_pattern, hw_rate_he[i][chip_gen], RA_MASK_HE_RATES, RTW89_RA_MODE_HE, @@ -640,6 +670,13 @@ void __rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev, if (!next_pattern.enable) goto out; + if (unlikely(next_pattern.rate >= RTW89_HW_RATE_INVAL)) { + rtw89_debug(rtwdev, RTW89_DBG_RA, + "pattern invalid target: chip_gen %d, mode 0x%x\n", + chip_gen, next_pattern.ra_mode); + goto out; + } + rtwvif_link->rate_pattern = next_pattern; rtw89_debug(rtwdev, RTW89_DBG_RA, "configure pattern: rate 0x%x, mask 0x%llx, mode 0x%x\n", From 0ac5ead00bf90f3280b8f5c0a0ad12e9800da8ec Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 21 Oct 2025 21:33:59 +0800 Subject: [PATCH 11/80] wifi: rtw89: regd: apply ACPI policy even if country code is programmed There are regulatory related policy according to BIOS/ACPI configuration, e.g. distro decides to disable some bands or some channels. Even if chip has a programmed country code, these policy should still be applied within regulatory notifier. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251021133402.15467-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 ++ drivers/net/wireless/realtek/rtw89/regd.c | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 7c74ed3cfaf7..ae98d6866b30 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5449,6 +5449,8 @@ struct rtw89_regd_ctrl { struct rtw89_regulatory_info { struct rtw89_regd_ctrl ctrl; const struct rtw89_regd *regd; + bool programmed; + enum rtw89_reg_6ghz_power reg_6ghz_power; struct rtw89_reg_6ghz_tpe reg_6ghz_tpe; bool txpwr_uk_follow_etsi; diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 58582f8d2b74..209d84909f88 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -723,6 +723,8 @@ int rtw89_regd_init_hint(struct rtw89_dev *rtwdev) chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code); if (!rtw89_regd_is_ww(chip_regd)) { rtwdev->regulatory.regd = chip_regd; + rtwdev->regulatory.programmed = true; + /* Ignore country ie if there is a country domain programmed in chip */ wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; wiphy->regulatory_flags |= REGULATORY_STRICT_REG; @@ -867,11 +869,6 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; else wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE; - - rtw89_regd_apply_policy_unii4(rtwdev, wiphy); - rtw89_regd_apply_policy_6ghz(rtwdev, wiphy); - rtw89_regd_apply_policy_tas(rtwdev); - rtw89_regd_apply_policy_ant_gain(rtwdev); } static @@ -883,19 +880,22 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request wiphy_lock(wiphy); rtw89_leave_ps_mode(rtwdev); - if (wiphy->regd) { - rtw89_debug(rtwdev, RTW89_DBG_REGD, - "There is a country domain programmed in chip, ignore notifications\n"); - goto exit; - } + if (rtwdev->regulatory.programmed) + goto policy; + rtw89_regd_notifier_apply(rtwdev, wiphy, request); rtw89_debug_regd(rtwdev, rtwdev->regulatory.regd, "get from initiator %d, alpha2", request->initiator); +policy: + rtw89_regd_apply_policy_unii4(rtwdev, wiphy); + rtw89_regd_apply_policy_6ghz(rtwdev, wiphy); + rtw89_regd_apply_policy_tas(rtwdev); + rtw89_regd_apply_policy_ant_gain(rtwdev); + rtw89_core_set_chip_txpwr(rtwdev); -exit: wiphy_unlock(wiphy); } From a48ae54a67f7a3c05d51ca87877832d4d6ea4639 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Tue, 21 Oct 2025 21:34:00 +0800 Subject: [PATCH 12/80] wifi: rtw89: 8852c: fix ADC oscillation in 160MHz affecting RX performance When operating in 160 MHz, the ADC may oscillate and affect AGC, leading to unstable RX quality. This issue can be resolved by ensuring proper RF filter bandwidth switching to avoid ADC oscillation. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251021133402.15467-7-pkshih@realtek.com --- .../net/wireless/realtek/rtw89/rtw8852c_rfk.c | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c index e7fd028c5e82..cbee484dee30 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c @@ -3987,37 +3987,56 @@ static void _ctrl_ch(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, } } +static void _set_rxbb_bw(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, + enum rtw89_bandwidth bw) +{ + u32 val; + + rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x1); + rtw89_write_rf(rtwdev, path, RR_LUTWA, RR_LUTWA_M2, 0xa); + + switch (bw) { + case RTW89_CHANNEL_WIDTH_20: + val = 0x1b; + break; + case RTW89_CHANNEL_WIDTH_40: + val = 0x13; + break; + case RTW89_CHANNEL_WIDTH_80: + val = 0xb; + break; + case RTW89_CHANNEL_WIDTH_160: + default: + val = 0x3; + break; + } + + rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, val); + rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x0); +} + +static void _set_tia_bw(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, + enum rtw89_bandwidth bw) +{ + if (bw == RTW89_CHANNEL_WIDTH_160) + rtw89_write_rf(rtwdev, path, RR_RXBB2, RR_RXBB2_EBW, 0x0); + else + rtw89_write_rf(rtwdev, path, RR_RXBB2, RR_RXBB2_EBW, 0x2); +} + static void _rxbb_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, enum rtw89_bandwidth bw) { u8 kpath; u8 path; - u32 val; kpath = _kpath(rtwdev, phy); for (path = 0; path < 2; path++) { if (!(kpath & BIT(path))) continue; - rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x1); - rtw89_write_rf(rtwdev, path, RR_LUTWA, RR_LUTWA_M2, 0xa); - switch (bw) { - case RTW89_CHANNEL_WIDTH_20: - val = 0x1b; - break; - case RTW89_CHANNEL_WIDTH_40: - val = 0x13; - break; - case RTW89_CHANNEL_WIDTH_80: - val = 0xb; - break; - case RTW89_CHANNEL_WIDTH_160: - default: - val = 0x3; - break; - } - rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, val); - rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x0); + _set_rxbb_bw(rtwdev, path, bw); + _set_tia_bw(rtwdev, path, bw); } } From e139b1c1f01a8874df02894b3aedf0b64f5b839b Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Tue, 21 Oct 2025 21:34:01 +0800 Subject: [PATCH 13/80] wifi: rtw89: restart hardware to recover firmware if power-save becomes abnormal Somehow power-save related functions get failure, such as failed to send null packet, or no response form firmware, and then WiFi will become unstable. Trigger SER function actively to reset firmware/driver to recover from abnormal states, including - firmware failed to ACK for entering PS mode - firmware failed to ACK for leaving PS mode - check PS H2C command received by firmware fail - failed to leave PS state Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251021133402.15467-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 1 + drivers/net/wireless/realtek/rtw89/core.h | 2 ++ drivers/net/wireless/realtek/rtw89/mac.c | 7 +++++-- drivers/net/wireless/realtek/rtw89/ps.c | 23 +++++++++++++++++++++-- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a3f156bf708e..1b5a40e9821c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -5538,6 +5538,7 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) rtw89_fw_h2c_fw_log(rtwdev, rtwdev->fw.log.enable); rtw89_fw_h2c_init_ba_cam(rtwdev); rtw89_tas_fw_timer_enable(rtwdev, true); + rtwdev->ps_hang_cnt = 0; return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ae98d6866b30..f8b443894db9 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -42,6 +42,7 @@ extern const struct ieee80211_ops rtw89_ops; #define RTW89_TRACK_WORK_PERIOD round_jiffies_relative(HZ * 2) #define RTW89_TRACK_PS_WORK_PERIOD msecs_to_jiffies(100) #define RTW89_FORBID_BA_TIMER round_jiffies_relative(HZ * 4) +#define RTW89_PS_HANG_MAX_CNT 3 #define CFO_TRACK_MAX_USER 64 #define MAX_RSSI 110 #define RSSI_FACTOR 1 @@ -6082,6 +6083,7 @@ struct rtw89_dev { struct rtw89_btc btc; enum rtw89_ps_mode ps_mode; bool lps_enabled; + u8 ps_hang_cnt; struct rtw89_wow_param wow; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index fd11b8fb3c89..d837513f4e92 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -12,6 +12,7 @@ #include "phy.h" #include "ps.h" #include "reg.h" +#include "ser.h" #include "util.h" static const u32 rtw89_mac_mem_base_addrs_ax[RTW89_MAC_MEM_NUM] = { @@ -1423,13 +1424,15 @@ void rtw89_mac_power_mode_change(struct rtw89_dev *rtwdev, bool enter) if (!ret) break; - if (i == RPWM_TRY_CNT - 1) + if (i == RPWM_TRY_CNT - 1) { rtw89_err(rtwdev, "firmware failed to ack for %s ps mode\n", enter ? "entering" : "leaving"); - else + rtw89_ser_notify(rtwdev, MAC_AX_ERR_ASSERTION); + } else { rtw89_debug(rtwdev, RTW89_DBG_UNEXP, "%d time firmware failed to ack for %s ps mode\n", i + 1, enter ? "entering" : "leaving"); + } } } diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index cf58121eb541..3f69dd4361c3 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -11,6 +11,7 @@ #include "phy.h" #include "ps.h" #include "reg.h" +#include "ser.h" #include "util.h" static int rtw89_fw_receive_lps_h2c_check(struct rtw89_dev *rtwdev, u8 macid) @@ -26,16 +27,27 @@ static int rtw89_fw_receive_lps_h2c_check(struct rtw89_dev *rtwdev, u8 macid) c2h_info.id = RTW89_FWCMD_C2HREG_FUNC_PS_LEAVE_ACK; ret = rtw89_fw_msg_reg(rtwdev, NULL, &c2h_info); if (ret) - return ret; + goto fw_fail; c2hreg_macid = u32_get_bits(c2h_info.u.c2hreg[0], RTW89_C2HREG_PS_LEAVE_ACK_MACID); c2hreg_ret = u32_get_bits(c2h_info.u.c2hreg[1], RTW89_C2HREG_PS_LEAVE_ACK_RET); - if (macid != c2hreg_macid || c2hreg_ret) + if (macid != c2hreg_macid || c2hreg_ret) { rtw89_warn(rtwdev, "rtw89: check lps h2c received by firmware fail\n"); + ret = -EINVAL; + goto fw_fail; + } + rtwdev->ps_hang_cnt = 0; return 0; + +fw_fail: + rtwdev->ps_hang_cnt++; + if (rtwdev->ps_hang_cnt >= RTW89_PS_HANG_MAX_CNT) + rtw89_ser_notify(rtwdev, MAC_AX_ERR_ASSERTION); + + return ret; } static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid) @@ -51,9 +63,16 @@ static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid) mac->ps_status, chk_msk); if (ret) { rtw89_info(rtwdev, "rtw89: failed to leave lps state\n"); + + rtwdev->ps_hang_cnt++; + if (rtwdev->ps_hang_cnt >= RTW89_PS_HANG_MAX_CNT) + rtw89_ser_notify(rtwdev, MAC_AX_ERR_ASSERTION); + return -EBUSY; } + rtwdev->ps_hang_cnt = 0; + return 0; } From a62b654125068a69b7c459ba59526ebd9882f405 Mon Sep 17 00:00:00 2001 From: Jui-Peng Tsai Date: Tue, 21 Oct 2025 21:34:02 +0800 Subject: [PATCH 14/80] wifi: rtw89: improve scan time on 6 GHz band Reduce scan time for all supported channels from 4.4s to 3.5s. If NL80211_SCAN_FLAG_COLOCATED_6GHZ is set in scan request, only scan PSC channels and the channels from the RNR element found on the 2.4/5 GHz channels. When firmware support parsing RNR element from received beacon or probe response, offload the decision about non-PSC channels to firmware. Driver do not need to fill non-PSC channels to scan list. If NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set, scan all supported channels. Signed-off-by: Jui-Peng Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251021133402.15467-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 9a926bb2cf00..cb431c8a65ac 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -7787,15 +7787,23 @@ int rtw89_hw_scan_prep_chan_list_be(struct rtw89_dev *rtwdev, struct ieee80211_channel *channel; struct list_head chan_list; enum rtw89_chan_type type; + bool chan_by_rnr; bool random_seq; int ret; u32 idx; random_seq = !!(req->flags & NL80211_SCAN_FLAG_RANDOM_SN); + chan_by_rnr = rtwdev->chip->support_rnr && + (req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ); INIT_LIST_HEAD(&chan_list); for (idx = 0; idx < req->n_channels; idx++) { channel = req->channels[idx]; + + if (channel->band == NL80211_BAND_6GHZ && + !cfg80211_channel_is_psc(channel) && chan_by_rnr) + continue; + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); if (!ch_info) { ret = -ENOMEM; From 030b8d5878e87ba9b91c48727135dba6e0f0fc11 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Sat, 1 Nov 2025 21:19:55 +0200 Subject: [PATCH 15/80] wifi: rtw89: 8832cu: turn off TX partial mode The TX partial mode in USB devices will cause timeout to wait for payload, causing SER 0x999 and disconnection. Turn off this mode according to design suggestion. rtw89_8852cu 2-4:1.0: FW status = 0xee001108 rtw89_8852cu 2-4:1.0: FW BADADDR = 0x18605fc8 rtw89_8852cu 2-4:1.0: FW EPC/RA = 0x0 rtw89_8852cu 2-4:1.0: FW MISC = 0x1010000 rtw89_8852cu 2-4:1.0: R_AX_HALT_C2H = 0x999 rtw89_8852cu 2-4:1.0: R_AX_SER_DBG_INFO = 0x71020010 rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55e rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55a rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55a rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55e rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55e rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55e rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55a rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55e rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55a rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55e rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f554 rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f556 rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55a rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55a rtw89_8852cu 2-4:1.0: [ERR]fw PC = 0x2013f55e rtw89_8852cu 2-4:1.0: ---> err=0x999 rtw89_8852cu 2-4:1.0: R_AX_SER_DBG_INFO =0x71020010 rtw89_8852cu 2-4:1.0: R_AX_SER_DBG_INFO =0x71020010 rtw89_8852cu 2-4:1.0: DBG Counter 1 (R_AX_DRV_FW_HSK_4)=0x00000000 rtw89_8852cu 2-4:1.0: DBG Counter 2 (R_AX_DRV_FW_HSK_5)=0x00000000 rtw89_8852cu 2-4:1.0: R_AX_DMAC_ERR_ISR=0x00000000 rtw89_8852cu 2-4:1.0: R_AX_DMAC_ERR_IMR=0x00000000 rtw89_8852cu 2-4:1.0: R_AX_CMAC_ERR_ISR [0]=0x00000000 rtw89_8852cu 2-4:1.0: R_AX_CMAC_FUNC_EN [0]=0xf000803f rtw89_8852cu 2-4:1.0: R_AX_CK_EN [0]=0xffffffff rtw89_8852cu 2-4:1.0: R_AX_CMAC_ERR_IMR [0]=0x00000000 rtw89_8852cu 2-4:1.0: [CMAC] : CMAC1 not enabled Signed-off-by: Ping-Ke Shih Signed-off-by: Bitterblue Smith Link: https://patch.msgid.link/3904dd9f-2178-41e5-95c2-7a9f6268e935@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index d837513f4e92..df040b4e8855 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -2327,7 +2327,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev) if (chip->chip_id == RTL8852C) val |= B_AX_UC_MGNT_DEC; if (chip->chip_id == RTL8852A || chip->chip_id == RTL8852B || - chip->chip_id == RTL8851B) + chip->chip_id == RTL8851B || + (chip->chip_id == RTL8852C && rtwdev->hci.type == RTW89_HCI_TYPE_USB)) val &= ~B_AX_TX_PARTIAL_MODE; rtw89_write32(rtwdev, R_AX_SEC_ENG_CTRL, val); From 89acd6c49322db4603a122e20780904021dc0491 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:20:38 +0200 Subject: [PATCH 16/80] wifi: rtw89: Add rtw89_core_get_ch_dma_v2() RTL8852CU, RTL8852AU, and RTL8922AU need a different mapping of TX queue to DMA channel compared to their PCI versions, so make get_ch_dma in struct rtw89_chip_ops an array and add rtw89_core_get_ch_dma_v2(). Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/6c7b2f01-2c78-47c8-a4c4-98cd3060d7f3@gmail.com --- drivers/net/wireless/realtek/rtw89/core.c | 19 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 5 +++-- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 4 +++- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 4 +++- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 4 +++- .../net/wireless/realtek/rtw89/rtw8852bt.c | 4 +++- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 4 +++- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 4 +++- 8 files changed, 40 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 1b5a40e9821c..1f92be300ea7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -759,6 +759,25 @@ u8 rtw89_core_get_ch_dma_v1(struct rtw89_dev *rtwdev, u8 qsel) } EXPORT_SYMBOL(rtw89_core_get_ch_dma_v1); +u8 rtw89_core_get_ch_dma_v2(struct rtw89_dev *rtwdev, u8 qsel) +{ + switch (qsel) { + default: + rtw89_warn(rtwdev, "Cannot map qsel to dma v2: %d\n", qsel); + fallthrough; + case RTW89_TX_QSEL_BE_0: + case RTW89_TX_QSEL_VO_0: + return RTW89_TXCH_ACH0; + case RTW89_TX_QSEL_BK_0: + case RTW89_TX_QSEL_VI_0: + return RTW89_TXCH_ACH2; + case RTW89_TX_QSEL_B0_MGMT: + case RTW89_TX_QSEL_B0_HI: + return RTW89_TXCH_CH8; + } +} +EXPORT_SYMBOL(rtw89_core_get_ch_dma_v2); + static void rtw89_core_tx_update_mgmt_info(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f8b443894db9..c7b48bd000c7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3764,7 +3764,7 @@ struct rtw89_chip_ops { void (*fill_txdesc_fwcmd)(struct rtw89_dev *rtwdev, struct rtw89_tx_desc_info *desc_info, void *txdesc); - u8 (*get_ch_dma)(struct rtw89_dev *rtwdev, u8 qsel); + u8 (*get_ch_dma[RTW89_HCI_TYPE_NUM])(struct rtw89_dev *rtwdev, u8 qsel); int (*cfg_ctrl_path)(struct rtw89_dev *rtwdev, bool wl); int (*mac_cfg_gnt)(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex_gnt *gnt_cfg); @@ -7253,7 +7253,7 @@ u8 rtw89_chip_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel) { const struct rtw89_chip_info *chip = rtwdev->chip; - return chip->ops->get_ch_dma(rtwdev, qsel); + return chip->ops->get_ch_dma[rtwdev->hci.type](rtwdev, qsel); } static inline @@ -7507,6 +7507,7 @@ void rtw89_core_fill_txdesc_fwcmd_v2(struct rtw89_dev *rtwdev, void *txdesc); u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel); u8 rtw89_core_get_ch_dma_v1(struct rtw89_dev *rtwdev, u8 qsel); +u8 rtw89_core_get_ch_dma_v2(struct rtw89_dev *rtwdev, u8 qsel); void rtw89_core_rx(struct rtw89_dev *rtwdev, struct rtw89_rx_desc_info *desc_info, struct sk_buff *skb); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index edcbda124916..2019f6022cbb 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2537,7 +2537,9 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .query_rxdesc = rtw89_core_query_rxdesc, .fill_txdesc = rtw89_core_fill_txdesc, .fill_txdesc_fwcmd = rtw89_core_fill_txdesc, - .get_ch_dma = rtw89_core_get_ch_dma, + .get_ch_dma = {rtw89_core_get_ch_dma, + rtw89_core_get_ch_dma, + NULL,}, .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path, .mac_cfg_gnt = rtw89_mac_cfg_gnt, .stop_sch_tx = rtw89_mac_stop_sch_tx, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 232f4c1bee1b..fa347dbebf9a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2178,7 +2178,9 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .query_rxdesc = rtw89_core_query_rxdesc, .fill_txdesc = rtw89_core_fill_txdesc, .fill_txdesc_fwcmd = rtw89_core_fill_txdesc, - .get_ch_dma = rtw89_core_get_ch_dma, + .get_ch_dma = {rtw89_core_get_ch_dma, + rtw89_core_get_ch_dma_v2, + NULL,}, .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path, .mac_cfg_gnt = rtw89_mac_cfg_gnt, .stop_sch_tx = rtw89_mac_stop_sch_tx, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 0777e336aaa1..38cd151f8c3f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -842,7 +842,9 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .query_rxdesc = rtw89_core_query_rxdesc, .fill_txdesc = rtw89_core_fill_txdesc, .fill_txdesc_fwcmd = rtw89_core_fill_txdesc, - .get_ch_dma = rtw89_core_get_ch_dma, + .get_ch_dma = {rtw89_core_get_ch_dma, + rtw89_core_get_ch_dma, + NULL,}, .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path, .mac_cfg_gnt = rtw89_mac_cfg_gnt, .stop_sch_tx = rtw89_mac_stop_sch_tx, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index b3a79ebc7e75..15ba780492d6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -708,7 +708,9 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = { .query_rxdesc = rtw89_core_query_rxdesc, .fill_txdesc = rtw89_core_fill_txdesc, .fill_txdesc_fwcmd = rtw89_core_fill_txdesc, - .get_ch_dma = rtw89_core_get_ch_dma, + .get_ch_dma = {rtw89_core_get_ch_dma, + NULL, + NULL,}, .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path, .mac_cfg_gnt = rtw89_mac_cfg_gnt, .stop_sch_tx = rtw89_mac_stop_sch_tx, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 440801d63343..ee1915307376 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2962,7 +2962,9 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .query_rxdesc = rtw89_core_query_rxdesc, .fill_txdesc = rtw89_core_fill_txdesc_v1, .fill_txdesc_fwcmd = rtw89_core_fill_txdesc_fwcmd_v1, - .get_ch_dma = rtw89_core_get_ch_dma, + .get_ch_dma = {rtw89_core_get_ch_dma, + rtw89_core_get_ch_dma_v2, + NULL,}, .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path_v1, .mac_cfg_gnt = rtw89_mac_cfg_gnt_v1, .stop_sch_tx = rtw89_mac_stop_sch_tx_v1, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 6aa19ad259ac..61f3a0a3f440 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2821,7 +2821,9 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .query_rxdesc = rtw89_core_query_rxdesc_v2, .fill_txdesc = rtw89_core_fill_txdesc_v2, .fill_txdesc_fwcmd = rtw89_core_fill_txdesc_fwcmd_v2, - .get_ch_dma = rtw89_core_get_ch_dma, + .get_ch_dma = {rtw89_core_get_ch_dma, + rtw89_core_get_ch_dma_v2, + NULL,}, .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path_v2, .mac_cfg_gnt = rtw89_mac_cfg_gnt_v2, .stop_sch_tx = rtw89_mac_stop_sch_tx_v2, From 994944aa58f84a4798664408a95606bd9f8a40f2 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:22:19 +0200 Subject: [PATCH 17/80] wifi: rtw89: usb: Move bulk out map to new struct rtw89_usb_info RTL8852AU, RTL8852CU, and RTL8922AU will need a different TX channel to bulk out endpoint mapping, so create a new struct rtw89_usb_info and move the mapping there. Initialise it in each chip's driver. Struct rtw89_usb_info will also hold some registers which are located at different offsets in RTL8852CU compared to the other wifi 6 chips. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/de11cfae-1dc0-4514-95b2-02b1bbfa92be@gmail.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 ++ .../net/wireless/realtek/rtw89/rtw8851bu.c | 15 +++++++++++ .../net/wireless/realtek/rtw89/rtw8852bu.c | 15 +++++++++++ drivers/net/wireless/realtek/rtw89/usb.c | 25 +++---------------- drivers/net/wireless/realtek/rtw89/usb.h | 5 ++++ 5 files changed, 40 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c7b48bd000c7..97cb0e5f0898 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -15,6 +15,7 @@ struct rtw89_dev; struct rtw89_pci_info; +struct rtw89_usb_info; struct rtw89_mac_gen_def; struct rtw89_phy_gen_def; struct rtw89_fw_blacklist; @@ -4514,6 +4515,7 @@ struct rtw89_chip_variant { union rtw89_bus_info { const struct rtw89_pci_info *pci; + const struct rtw89_usb_info *usb; }; struct rtw89_driver_info { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 04e1ab13b753..c39acbec12a9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -7,10 +7,25 @@ #include "rtw8851b.h" #include "usb.h" +static const struct rtw89_usb_info rtw8851b_usb_info = { + .bulkout_id = { + [RTW89_DMA_ACH0] = 3, + [RTW89_DMA_ACH1] = 4, + [RTW89_DMA_ACH2] = 5, + [RTW89_DMA_ACH3] = 6, + [RTW89_DMA_B0MG] = 0, + [RTW89_DMA_B0HI] = 1, + [RTW89_DMA_H2C] = 2, + }, +}; + static const struct rtw89_driver_info rtw89_8851bu_info = { .chip = &rtw8851b_chip_info, .variant = NULL, .quirks = NULL, + .bus = { + .usb = &rtw8851b_usb_info, + } }; static const struct usb_device_id rtw_8851bu_id_table[] = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index 0694272f7ffa..28e69473918e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -7,10 +7,25 @@ #include "rtw8852b.h" #include "usb.h" +static const struct rtw89_usb_info rtw8852b_usb_info = { + .bulkout_id = { + [RTW89_DMA_ACH0] = 3, + [RTW89_DMA_ACH1] = 4, + [RTW89_DMA_ACH2] = 5, + [RTW89_DMA_ACH3] = 6, + [RTW89_DMA_B0MG] = 0, + [RTW89_DMA_B0HI] = 1, + [RTW89_DMA_H2C] = 2, + }, +}; + static const struct rtw89_driver_info rtw89_8852bu_info = { .chip = &rtw8852b_chip_info, .variant = NULL, .quirks = NULL, + .bus = { + .usb = &rtw8852b_usb_info, + } }; static const struct usb_device_id rtw_8852bu_id_table[] = { diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index afbd1fb79021..8c0129d54b6c 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -167,27 +167,6 @@ rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, return 42; /* TODO some kind of calculation? */ } -static u8 rtw89_usb_get_bulkout_id(u8 ch_dma) -{ - switch (ch_dma) { - case RTW89_DMA_ACH0: - return 3; - case RTW89_DMA_ACH1: - return 4; - case RTW89_DMA_ACH2: - return 5; - case RTW89_DMA_ACH3: - return 6; - default: - case RTW89_DMA_B0MG: - return 0; - case RTW89_DMA_B0HI: - return 1; - case RTW89_DMA_H2C: - return 2; - } -} - static void rtw89_usb_write_port_complete(struct urb *urb) { struct rtw89_usb_tx_ctrl_block *txcb = urb->context; @@ -249,9 +228,10 @@ static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma, void *data, int len, void *context) { struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + const struct rtw89_usb_info *info = rtwusb->info; struct usb_device *usbd = rtwusb->udev; struct urb *urb; - u8 bulkout_id = rtw89_usb_get_bulkout_id(ch_dma); + u8 bulkout_id = info->bulkout_id[ch_dma]; unsigned int pipe; int ret; @@ -949,6 +929,7 @@ int rtw89_usb_probe(struct usb_interface *intf, rtwusb = rtw89_usb_priv(rtwdev); rtwusb->rtwdev = rtwdev; + rtwusb->info = info->bus.usb; rtwdev->hci.ops = &rtw89_usb_ops; rtwdev->hci.type = RTW89_HCI_TYPE_USB; diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index c1b4bfa20979..a27c698a424c 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -20,6 +20,10 @@ #define RTW89_MAX_ENDPOINT_NUM 9 #define RTW89_MAX_BULKOUT_NUM 7 +struct rtw89_usb_info { + u8 bulkout_id[RTW89_DMA_CH_NUM]; +}; + struct rtw89_usb_rx_ctrl_block { struct rtw89_dev *rtwdev; struct urb *rx_urb; @@ -35,6 +39,7 @@ struct rtw89_usb_tx_ctrl_block { struct rtw89_usb { struct rtw89_dev *rtwdev; struct usb_device *udev; + const struct rtw89_usb_info *info; __le32 *vendor_req_buf; From 7697701d6c0356cecda03be516023cb527ddb007 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:22:56 +0200 Subject: [PATCH 18/80] wifi: rtw89: usb: Prepare rtw89_usb_ops_mac_pre_init() for RTL8852CU The registers used in rtw89_usb_ops_mac_pre_init() are located at different offsets in RTL8852CU, so move them to struct rtw89_usb_info, which is filled in each chip's driver. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/36b823c9-2cb5-4191-8492-2a291f9dc1db@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8851bu.c | 4 ++++ drivers/net/wireless/realtek/rtw89/rtw8852bu.c | 4 ++++ drivers/net/wireless/realtek/rtw89/usb.c | 13 ++++++++----- drivers/net/wireless/realtek/rtw89/usb.h | 3 +++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index c39acbec12a9..9cc682872d24 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -5,9 +5,13 @@ #include #include #include "rtw8851b.h" +#include "reg.h" #include "usb.h" static const struct rtw89_usb_info rtw8851b_usb_info = { + .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2, + .usb_wlan0_1 = R_AX_USB_WLAN0_1, + .hci_func_en = R_AX_HCI_FUNC_EN, .bulkout_id = { [RTW89_DMA_ACH0] = 3, [RTW89_DMA_ACH1] = 4, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index 28e69473918e..b913cae95e3b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -5,9 +5,13 @@ #include #include #include "rtw8852b.h" +#include "reg.h" #include "usb.h" static const struct rtw89_usb_info rtw8852b_usb_info = { + .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2, + .usb_wlan0_1 = R_AX_USB_WLAN0_1, + .hci_func_en = R_AX_HCI_FUNC_EN, .bulkout_id = { [RTW89_DMA_ACH0] = 3, [RTW89_DMA_ACH1] = 4, diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 8c0129d54b6c..ea1d8726ec3f 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -678,20 +678,23 @@ static int rtw89_usb_ops_deinit(struct rtw89_dev *rtwdev) static int rtw89_usb_ops_mac_pre_init(struct rtw89_dev *rtwdev) { + struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + const struct rtw89_usb_info *info = rtwusb->info; u32 val32; - rtw89_write32_set(rtwdev, R_AX_USB_HOST_REQUEST_2, B_AX_R_USBIO_MODE); + rtw89_write32_set(rtwdev, info->usb_host_request_2, + B_AX_R_USBIO_MODE); /* fix USB IO hang suggest by chihhanli@realtek.com */ - rtw89_write32_clr(rtwdev, R_AX_USB_WLAN0_1, + rtw89_write32_clr(rtwdev, info->usb_wlan0_1, B_AX_USBRX_RST | B_AX_USBTX_RST); - val32 = rtw89_read32(rtwdev, R_AX_HCI_FUNC_EN); + val32 = rtw89_read32(rtwdev, info->hci_func_en); val32 &= ~(B_AX_HCI_RXDMA_EN | B_AX_HCI_TXDMA_EN); - rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val32); + rtw89_write32(rtwdev, info->hci_func_en, val32); val32 |= B_AX_HCI_RXDMA_EN | B_AX_HCI_TXDMA_EN; - rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val32); + rtw89_write32(rtwdev, info->hci_func_en, val32); /* fix USB TRX hang suggest by chihhanli@realtek.com */ return 0; diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index a27c698a424c..be3a9fbe42c0 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -21,6 +21,9 @@ #define RTW89_MAX_BULKOUT_NUM 7 struct rtw89_usb_info { + u32 usb_host_request_2; + u32 usb_wlan0_1; + u32 hci_func_en; u8 bulkout_id[RTW89_DMA_CH_NUM]; }; From 199afd3af12c81f663734d8fe290197288618ada Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:23:33 +0200 Subject: [PATCH 19/80] wifi: rtw89: usb: Prepare rtw89_usb_ops_mac_post_init() for RTL8852CU The registers used in rtw89_usb_ops_mac_post_init() are located at different offsets in RTL8852CU, so move them to struct rtw89_usb_info, which is filled in each chip's driver. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/c12b621a-037c-4870-ac12-9795ddde6b53@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8851bu.c | 3 +++ drivers/net/wireless/realtek/rtw89/rtw8852bu.c | 3 +++ drivers/net/wireless/realtek/rtw89/usb.c | 7 ++++--- drivers/net/wireless/realtek/rtw89/usb.h | 3 +++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 9cc682872d24..b8799a37add3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -12,6 +12,9 @@ static const struct rtw89_usb_info rtw8851b_usb_info = { .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2, .usb_wlan0_1 = R_AX_USB_WLAN0_1, .hci_func_en = R_AX_HCI_FUNC_EN, + .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0, + .usb_endpoint_0 = R_AX_USB_ENDPOINT_0, + .usb_endpoint_2 = R_AX_USB_ENDPOINT_2, .bulkout_id = { [RTW89_DMA_ACH0] = 3, [RTW89_DMA_ACH1] = 4, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index b913cae95e3b..c8a644403341 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -12,6 +12,9 @@ static const struct rtw89_usb_info rtw8852b_usb_info = { .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2, .usb_wlan0_1 = R_AX_USB_WLAN0_1, .hci_func_en = R_AX_HCI_FUNC_EN, + .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0, + .usb_endpoint_0 = R_AX_USB_ENDPOINT_0, + .usb_endpoint_2 = R_AX_USB_ENDPOINT_2, .bulkout_id = { [RTW89_DMA_ACH0] = 3, [RTW89_DMA_ACH1] = 4, diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index ea1d8726ec3f..4c47071b30fd 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -708,10 +708,11 @@ static int rtw89_usb_ops_mac_pre_deinit(struct rtw89_dev *rtwdev) static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) { struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + const struct rtw89_usb_info *info = rtwusb->info; enum usb_device_speed speed; u32 ep; - rtw89_write32_clr(rtwdev, R_AX_USB3_MAC_NPI_CONFIG_INTF_0, + rtw89_write32_clr(rtwdev, info->usb3_mac_npi_config_intf_0, B_AX_SSPHY_LFPS_FILTER); speed = rtwusb->udev->speed; @@ -727,9 +728,9 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) if (ep == 8) continue; - rtw89_write8_mask(rtwdev, R_AX_USB_ENDPOINT_0, + rtw89_write8_mask(rtwdev, info->usb_endpoint_0, B_AX_EP_IDX, ep); - rtw89_write8(rtwdev, R_AX_USB_ENDPOINT_2 + 1, NUMP); + rtw89_write8(rtwdev, info->usb_endpoint_2 + 1, NUMP); } return 0; diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index be3a9fbe42c0..32cdbf5128e4 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -24,6 +24,9 @@ struct rtw89_usb_info { u32 usb_host_request_2; u32 usb_wlan0_1; u32 hci_func_en; + u32 usb3_mac_npi_config_intf_0; + u32 usb_endpoint_0; + u32 usb_endpoint_2; u8 bulkout_id[RTW89_DMA_CH_NUM]; }; From 32e0381e86031d0289b98e82a7a872ed1698676d Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:24:05 +0200 Subject: [PATCH 20/80] wifi: rtw89: Fix rtw89_mac_dmac_func_pre_en_ax() for USB/SDIO Set the DMA mode according to the interface type. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/d2250e72-0aa1-422c-8f7f-9aeb283ca376@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index df040b4e8855..b5309ae3fd57 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -3984,8 +3984,15 @@ static void rtw89_mac_dmac_func_pre_en_ax(struct rtw89_dev *rtwdev) val = rtw89_read32(rtwdev, R_AX_HAXI_INIT_CFG1); val &= ~(B_AX_DMA_MODE_MASK | B_AX_STOP_AXI_MST); - val |= FIELD_PREP(B_AX_DMA_MODE_MASK, DMA_MOD_PCIE_1B) | - B_AX_TXHCI_EN_V1 | B_AX_RXHCI_EN_V1; + val |= B_AX_TXHCI_EN_V1 | B_AX_RXHCI_EN_V1; + + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + val |= FIELD_PREP(B_AX_DMA_MODE_MASK, DMA_MOD_PCIE_1B); + else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + val |= FIELD_PREP(B_AX_DMA_MODE_MASK, DMA_MOD_USB); + else + val |= FIELD_PREP(B_AX_DMA_MODE_MASK, DMA_MOD_SDIO); + rtw89_write32(rtwdev, R_AX_HAXI_INIT_CFG1, val); rtw89_write32_clr(rtwdev, R_AX_HAXI_DMA_STOP1, From 97259766b18e2a22f2d5258d4ae151260e9165b0 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:24:39 +0200 Subject: [PATCH 21/80] wifi: rtw89: 8852c: Fix rtw8852c_pwr_{on,off}_func() for USB There are a few differences in the power on/off functions between PCIE and USB. The changes in the power off function in particular are needed for the RTL8832CU to be able to power on again after it's powered off. While the RTL8832CU appears to work without the changes in the power on function, it's probably best to implement them, in case they are needed in some situations. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/598dec66-b5cc-435a-bcf6-fa66577f8cfc@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index ee1915307376..6aedbd03e3d4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -214,7 +214,8 @@ static int rtw8852c_pwr_on_func(struct rtw89_dev *rtwdev) int ret; val32 = rtw89_read32_mask(rtwdev, R_AX_SYS_STATUS1, B_AX_PAD_HCI_SEL_V2_MASK); - if (val32 == MAC_AX_HCI_SEL_PCIE_USB) + if (val32 == MAC_AX_HCI_SEL_PCIE_USB || + rtwdev->hci.type == RTW89_HCI_TYPE_USB) rtw89_write32_set(rtwdev, R_AX_LDO_AON_CTRL0, B_AX_PD_REGU_L); rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_AFSM_WLSUS_EN | @@ -246,7 +247,9 @@ static int rtw8852c_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write8_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); - rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1); + + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1); rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND, B_AX_CMAC1_FEN); rtw89_write32_set(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND, B_AX_R_SYM_ISO_CMAC12PP); @@ -305,9 +308,11 @@ static int rtw8852c_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B14); rtw89_write32_clr(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); - rtw89_write32_set(rtwdev, R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, - B_AX_EECS_PULL_LOW_EN | B_AX_EESK_PULL_LOW_EN | - B_AX_LED1_PULL_LOW_EN); + + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32_set(rtwdev, R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, + B_AX_EECS_PULL_LOW_EN | B_AX_EESK_PULL_LOW_EN | + B_AX_LED1_PULL_LOW_EN); rtw89_write32_set(rtwdev, R_AX_DMAC_FUNC_EN, B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | B_AX_MPDU_PROC_EN | @@ -385,12 +390,24 @@ static int rtw8852c_pwr_off_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION); + else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_SOP_EDSWR); + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_XTAL_OFF_A_DIE); rtw89_write32_set(rtwdev, R_AX_SYS_SWR_CTRL1, B_AX_SYM_CTRL_SPS_PWMFREQ); rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0, B_AX_REG_ZCDC_H_MASK, 0x3); - rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); + + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); + } else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) { + val32 = rtw89_read32(rtwdev, R_AX_SYS_PW_CTRL); + val32 &= ~B_AX_AFSM_PCIE_SUS_EN; + val32 |= B_AX_AFSM_WLSUS_EN; + rtw89_write32(rtwdev, R_AX_SYS_PW_CTRL, val32); + } return 0; } From a8658990846058fc9a76a38b20edc5ddf05fd39d Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:25:41 +0200 Subject: [PATCH 22/80] wifi: rtw89: Add rtw8852c_dle_mem_usb{2,3} Add rtw8852c_dle_mem_usb2 and rtw8852c_dle_mem_usb3 and their various quotas and sizes in struct rtw89_mac_size_set. "dle" could be "Data Link Engine" or "Double Link Engine". These are some parameters needed for RTL8852CU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/40a58644-13ce-48a4-85e2-ba4f3cbb975b@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 20 ++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 10 ++++++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 31 ++++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index b5309ae3fd57..bc39b4f7bc4f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1663,6 +1663,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_size7 = {RTW89_WDE_PG_64, 510, 2,}, /* DLFW */ .wde_size9 = {RTW89_WDE_PG_64, 0, 1024,}, + /* 8852C USB3.0 */ + .wde_size17 = {RTW89_WDE_PG_64, 354, 30,}, /* 8852C DLFW */ .wde_size18 = {RTW89_WDE_PG_64, 0, 2048,}, /* 8852C PCIE SCC */ @@ -1670,6 +1672,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_size23 = {RTW89_WDE_PG_64, 1022, 2,}, /* 8852B USB2.0/USB3.0 SCC */ .wde_size25 = {RTW89_WDE_PG_64, 162, 94,}, + /* 8852C USB2.0 */ + .wde_size31 = {RTW89_WDE_PG_64, 384, 0,}, /* PCIE */ .ple_size0 = {RTW89_PLE_PG_128, 1520, 16,}, .ple_size0_v1 = {RTW89_PLE_PG_128, 2688, 240, 212992,}, @@ -1681,6 +1685,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* DLFW */ .ple_size8 = {RTW89_PLE_PG_128, 64, 960,}, .ple_size9 = {RTW89_PLE_PG_128, 2288, 16,}, + /* 8852C USB */ + .ple_size17 = {RTW89_PLE_PG_128, 3368, 24,}, /* 8852C DLFW */ .ple_size18 = {RTW89_PLE_PG_128, 2544, 16,}, /* 8852C PCIE SCC */ @@ -1689,6 +1695,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_size32 = {RTW89_PLE_PG_128, 620, 20,}, /* 8852B USB3.0 SCC */ .ple_size33 = {RTW89_PLE_PG_128, 632, 8,}, + /* 8852C USB2.0 */ + .ple_size34 = {RTW89_PLE_PG_128, 3374, 18,}, /* PCIE 64 */ .wde_qt0 = {3792, 196, 0, 107,}, .wde_qt0_v1 = {3302, 6, 0, 20,}, @@ -1698,6 +1706,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt6 = {448, 48, 0, 16,}, /* 8852B PCIE SCC */ .wde_qt7 = {446, 48, 0, 16,}, + /* 8852C USB3.0 */ + .wde_qt16 = {344, 2, 0, 8,}, /* 8852C DLFW */ .wde_qt17 = {0, 0, 0, 0,}, /* 8852C PCIE SCC */ @@ -1705,6 +1715,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt23 = {958, 48, 0, 16,}, /* 8852B USB2.0/USB3.0 SCC */ .wde_qt25 = {152, 2, 0, 8,}, + /* 8852C USB2.0 */ + .wde_qt31 = {338, 6, 0, 40,}, .ple_qt0 = {320, 320, 32, 16, 13, 13, 292, 292, 64, 18, 1, 4, 0,}, .ple_qt1 = {320, 320, 32, 16, 1316, 1316, 1595, 1595, 1367, 1321, 1, 1307, 0,}, /* PCIE SCC */ @@ -1716,6 +1728,10 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt13 = {0, 0, 16, 48, 0, 0, 0, 0, 0, 0, 0,}, /* PCIE 64 */ .ple_qt18 = {147, 0, 16, 20, 17, 13, 89, 0, 32, 14, 8, 0,}, + /* USB 52C USB3.0 */ + .ple_qt42 = {1068, 0, 16, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, + /* USB 52C USB3.0 */ + .ple_qt43 = {3068, 0, 32, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, /* DLFW 52C */ .ple_qt44 = {0, 0, 16, 256, 0, 0, 0, 0, 0, 0, 0, 0,}, /* DLFW 52C */ @@ -1735,6 +1751,10 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* USB3.0 52B 92K */ .ple_qt74 = {286, 0, 16, 48, 4, 13, 178, 0, 32, 14, 8, 0, 0,}, .ple_qt75 = {286, 0, 32, 48, 37, 13, 211, 0, 65, 14, 24, 0, 0,}, + /* USB2.0 52C */ + .ple_qt78 = {1560, 0, 16, 48, 13, 13, 390, 0, 32, 38, 8, 16, 0,}, + /* USB2.0 52C */ + .ple_qt79 = {1560, 0, 32, 48, 1253, 13, 1630, 0, 1272, 38, 120, 1256, 0,}, /* 8852A PCIE WOW */ .ple_qt_52a_wow = {264, 0, 32, 20, 64, 13, 1005, 0, 64, 128, 120,}, /* 8852B PCIE WOW */ diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 25fe5e5c8a97..46302f4aa3d9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -923,10 +923,12 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size wde_size6; const struct rtw89_dle_size wde_size7; const struct rtw89_dle_size wde_size9; + const struct rtw89_dle_size wde_size17; const struct rtw89_dle_size wde_size18; const struct rtw89_dle_size wde_size19; const struct rtw89_dle_size wde_size23; const struct rtw89_dle_size wde_size25; + const struct rtw89_dle_size wde_size31; const struct rtw89_dle_size ple_size0; const struct rtw89_dle_size ple_size0_v1; const struct rtw89_dle_size ple_size3_v1; @@ -934,19 +936,23 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size ple_size6; const struct rtw89_dle_size ple_size8; const struct rtw89_dle_size ple_size9; + const struct rtw89_dle_size ple_size17; const struct rtw89_dle_size ple_size18; const struct rtw89_dle_size ple_size19; const struct rtw89_dle_size ple_size32; const struct rtw89_dle_size ple_size33; + const struct rtw89_dle_size ple_size34; const struct rtw89_wde_quota wde_qt0; const struct rtw89_wde_quota wde_qt0_v1; const struct rtw89_wde_quota wde_qt4; const struct rtw89_wde_quota wde_qt6; const struct rtw89_wde_quota wde_qt7; + const struct rtw89_wde_quota wde_qt16; const struct rtw89_wde_quota wde_qt17; const struct rtw89_wde_quota wde_qt18; const struct rtw89_wde_quota wde_qt23; const struct rtw89_wde_quota wde_qt25; + const struct rtw89_wde_quota wde_qt31; const struct rtw89_ple_quota ple_qt0; const struct rtw89_ple_quota ple_qt1; const struct rtw89_ple_quota ple_qt4; @@ -954,6 +960,8 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt9; const struct rtw89_ple_quota ple_qt13; const struct rtw89_ple_quota ple_qt18; + const struct rtw89_ple_quota ple_qt42; + const struct rtw89_ple_quota ple_qt43; const struct rtw89_ple_quota ple_qt44; const struct rtw89_ple_quota ple_qt45; const struct rtw89_ple_quota ple_qt46; @@ -965,6 +973,8 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt73; const struct rtw89_ple_quota ple_qt74; const struct rtw89_ple_quota ple_qt75; + const struct rtw89_ple_quota ple_qt78; + const struct rtw89_ple_quota ple_qt79; const struct rtw89_ple_quota ple_qt_52a_wow; const struct rtw89_ple_quota ple_qt_52b_wow; const struct rtw89_ple_quota ple_qt_52bt_wow; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 6aedbd03e3d4..651ef96d143a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -64,6 +64,32 @@ static const struct rtw89_dle_mem rtw8852c_dle_mem_pcie[] = { NULL}, }; +static const struct rtw89_dle_mem rtw8852c_dle_mem_usb2[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size31, + &rtw89_mac_size.ple_size34, &rtw89_mac_size.wde_qt31, + &rtw89_mac_size.wde_qt31, &rtw89_mac_size.ple_qt78, + &rtw89_mac_size.ple_qt79}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18, + &rtw89_mac_size.ple_size18, &rtw89_mac_size.wde_qt17, + &rtw89_mac_size.wde_qt17, &rtw89_mac_size.ple_qt44, + &rtw89_mac_size.ple_qt45}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_dle_mem rtw8852c_dle_mem_usb3[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size17, + &rtw89_mac_size.ple_size17, &rtw89_mac_size.wde_qt16, + &rtw89_mac_size.wde_qt16, &rtw89_mac_size.ple_qt42, + &rtw89_mac_size.ple_qt43}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18, + &rtw89_mac_size.ple_size18, &rtw89_mac_size.wde_qt17, + &rtw89_mac_size.wde_qt17, &rtw89_mac_size.ple_qt44, + &rtw89_mac_size.ple_qt45}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + static const u32 rtw8852c_h2c_regs[RTW89_H2CREG_MAX] = { R_AX_H2CREG_DATA0_V1, R_AX_H2CREG_DATA1_V1, R_AX_H2CREG_DATA2_V1, R_AX_H2CREG_DATA3_V1 @@ -3026,7 +3052,10 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x6f800, .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, NULL, NULL}, - .dle_mem = {rtw8852c_dle_mem_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8852c_dle_mem_pcie, + rtw8852c_dle_mem_usb2, + rtw8852c_dle_mem_usb3, + NULL}, .wde_qempty_acq_grpnum = 16, .wde_qempty_mgq_grpsel = 16, .rf_base_addr = {0xe000, 0xf000}, From 19faad8c729df195f189763151dceb7ff5851fde Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:26:41 +0200 Subject: [PATCH 23/80] wifi: rtw89: Add rtw8852c_hfc_param_ini_usb "hfc" means "hci fc" which is "Host Control Interface Flow Control". These are some parameters needed for RTL8852CU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/5b90204c-60ad-4579-b241-b7ac2e1fee91@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 651ef96d143a..10e3963b692c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -51,6 +51,48 @@ static const struct rtw89_hfc_param_ini rtw8852c_hfc_param_ini_pcie[] = { [RTW89_QTA_INVALID] = {NULL}, }; +static const struct rtw89_hfc_ch_cfg rtw8852c_hfc_chcfg_usb[] = { + {18, 344, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {18, 344, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {18, 344, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {18, 344, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {18, 344, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {18, 344, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0} /* FWCMDQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8852c_hfc_pubcfg_usb = { + 344, /* Group 0 */ + 0, /* Group 1 */ + 344, /* Public Max */ + 0 /* WP threshold */ +}; + +static const struct rtw89_hfc_prec_cfg rtw8852c_hfc_preccfg_usb = { + 9, /* CH 0-11 pre-cost */ + 32, /* H2C pre-cost */ + 146, /* WP CH 0-7 pre-cost */ + 146, /* WP CH 8-11 pre-cost */ + 1, /* CH 0-11 full condition */ + 1, /* H2C full condition */ + 1, /* WP CH 0-7 full condition */ + 1, /* WP CH 8-11 full condition */ +}; + +static const struct rtw89_hfc_param_ini rtw8852c_hfc_param_ini_usb[] = { + [RTW89_QTA_SCC] = {rtw8852c_hfc_chcfg_usb, &rtw8852c_hfc_pubcfg_usb, + &rtw8852c_hfc_preccfg_usb, RTW89_HCIFC_STF}, + [RTW89_QTA_DLFW] = {NULL, NULL, + &rtw8852c_hfc_preccfg_usb, RTW89_HCIFC_STF}, + [RTW89_QTA_INVALID] = {NULL}, +}; + static const struct rtw89_dle_mem rtw8852c_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size19, &rtw89_mac_size.ple_size19, &rtw89_mac_size.wde_qt18, @@ -3051,7 +3093,9 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .max_amsdu_limit = 8000, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, NULL, NULL}, + .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, + rtw8852c_hfc_param_ini_usb, + NULL}, .dle_mem = {rtw8852c_dle_mem_pcie, rtw8852c_dle_mem_usb2, rtw8852c_dle_mem_usb3, From 6bc2711085a52bd4d8da0173b22b8d4d532c5d21 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:27:09 +0200 Subject: [PATCH 24/80] wifi: rtw89: 8852c: Accept USB devices and load their MAC address Make rtw8852c_read_efuse() accept USB devices and load the MAC address from the correct offset. Also fix the offset of the MAC address because it was wrong. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/78138507-99ea-4a58-a02b-f4d11dbfba3b@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 16 +++++++--------- drivers/net/wireless/realtek/rtw89/rtw8852c.h | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 10e3963b692c..8550ae3bcb64 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -480,14 +480,6 @@ static int rtw8852c_pwr_off_func(struct rtw89_dev *rtwdev) return 0; } -static void rtw8852c_e_efuse_parsing(struct rtw89_efuse *efuse, - struct rtw8852c_efuse *map) -{ - ether_addr_copy(efuse->addr, map->e.mac_addr); - efuse->rfe_type = map->rfe_type; - efuse->xtal_cap = map->xtal_k; -} - static void rtw8852c_efuse_parsing_tssi(struct rtw89_dev *rtwdev, struct rtw8852c_efuse *map) { @@ -596,12 +588,18 @@ static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, switch (rtwdev->hci.type) { case RTW89_HCI_TYPE_PCIE: - rtw8852c_e_efuse_parsing(efuse, map); + ether_addr_copy(efuse->addr, map->e.mac_addr); + break; + case RTW89_HCI_TYPE_USB: + ether_addr_copy(efuse->addr, map->u.mac_addr); break; default: return -ENOTSUPP; } + efuse->rfe_type = map->rfe_type; + efuse->xtal_cap = map->xtal_k; + rtw89_info(rtwdev, "chip rfe_type is %d\n", efuse->rfe_type); return 0; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.h b/drivers/net/wireless/realtek/rtw89/rtw8852c.h index 77b05daedd10..8585921ac6c4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.h @@ -11,7 +11,7 @@ #define BB_PATH_NUM_8852C 2 struct rtw8852c_u_efuse { - u8 rsvd[0x38]; + u8 rsvd[0x88]; u8 mac_addr[ETH_ALEN]; }; From 406849000df417a0618dca96f57f4bd06cf2b7d9 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:27:38 +0200 Subject: [PATCH 25/80] wifi: rtw89: Add rtw8852cu.c This is the entry point for the new rtw89_8852cu module. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/a63a71f0-8003-4390-8bee-5cfd298867a0@gmail.com --- .../net/wireless/realtek/rtw89/rtw8852cu.c | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852cu.c diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c new file mode 100644 index 000000000000..2708b523ca14 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include +#include +#include "rtw8852c.h" +#include "reg.h" +#include "usb.h" + +static const struct rtw89_usb_info rtw8852c_usb_info = { + .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2_V1, + .usb_wlan0_1 = R_AX_USB_WLAN0_1_V1, + .hci_func_en = R_AX_HCI_FUNC_EN_V1, + .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0_V1, + .usb_endpoint_0 = R_AX_USB_ENDPOINT_0_V1, + .usb_endpoint_2 = R_AX_USB_ENDPOINT_2_V1, + .bulkout_id = { + [RTW89_DMA_ACH0] = 3, + [RTW89_DMA_ACH2] = 5, + [RTW89_DMA_ACH4] = 4, + [RTW89_DMA_ACH6] = 6, + [RTW89_DMA_B0MG] = 0, + [RTW89_DMA_B0HI] = 0, + [RTW89_DMA_B1MG] = 1, + [RTW89_DMA_B1HI] = 1, + [RTW89_DMA_H2C] = 2, + }, +}; + +static const struct rtw89_driver_info rtw89_8852cu_info = { + .chip = &rtw8852c_chip_info, + .variant = NULL, + .quirks = NULL, + .bus = { + .usb = &rtw8852c_usb_info, + }, +}; + +static const struct usb_device_id rtw_8852cu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc832, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85d, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x991d, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0101, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0102, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8852cu_id_table); + +static struct usb_driver rtw_8852cu_driver = { + .name = KBUILD_MODNAME, + .id_table = rtw_8852cu_id_table, + .probe = rtw89_usb_probe, + .disconnect = rtw89_usb_disconnect, +}; +module_usb_driver(rtw_8852cu_driver); + +MODULE_AUTHOR("Bitterblue Smith "); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852CU driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 0d971ffdae584ea4a5b413318b4945b3d273a75a Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 1 Nov 2025 21:28:17 +0200 Subject: [PATCH 26/80] wifi: rtw89: Enable the new rtw89_8852cu module Tested mostly in station mode, and a little bit in AP mode. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/859a45ce-2730-4eeb-99d2-37d7ff277bd7@gmail.com --- drivers/net/wireless/realtek/rtw89/Kconfig | 11 +++++++++++ drivers/net/wireless/realtek/rtw89/Makefile | 3 +++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index 4288c30b400a..e89f7481475a 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -121,6 +121,17 @@ config RTW89_8852CE 802.11ax PCIe wireless network (Wi-Fi 6E) adapter +config RTW89_8852CU + tristate "Realtek 8852CU USB wireless network (Wi-Fi 6E) adapter" + depends on USB + select RTW89_CORE + select RTW89_USB + select RTW89_8852C + help + Select this option will enable support for 8852CU chipset + + 802.11ax USB wireless network (Wi-Fi 6E) adapter + config RTW89_8922AE tristate "Realtek 8922AE/8922AE-VS PCI wireless network (Wi-Fi 7) adapter" depends on PCI diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 23e43c444f69..e0d21972e57c 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -75,6 +75,9 @@ rtw89_8852c-objs := rtw8852c.o \ obj-$(CONFIG_RTW89_8852CE) += rtw89_8852ce.o rtw89_8852ce-objs := rtw8852ce.o +obj-$(CONFIG_RTW89_8852CU) += rtw89_8852cu.o +rtw89_8852cu-objs := rtw8852cu.o + obj-$(CONFIG_RTW89_8922A) += rtw89_8922a.o rtw89_8922a-objs := rtw8922a.o \ rtw8922a_rfk.o From 71ee0d565985cbcf9f9c5c6b046e6d4ad52d0b3f Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Mon, 27 Oct 2025 15:02:59 +0800 Subject: [PATCH 27/80] wifi: rtw88: 8822c: use fixed rate and bandwidth to reply CSI packets AP could trigger beamform and send NDPA packet with 6Mbps rate on bandwidth 80MHz, but RTL8822C can't reply CSI packet with the same setting. Therefore, force to use OFDM rate and bandwidth 20MHz instead. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251027070259.18931-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/bf.c | 8 +++++++- drivers/net/wireless/realtek/rtw88/bf.h | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/bf.c b/drivers/net/wireless/realtek/rtw88/bf.c index c827c4a2814b..0d0ccbc7d00c 100644 --- a/drivers/net/wireless/realtek/rtw88/bf.c +++ b/drivers/net/wireless/realtek/rtw88/bf.c @@ -124,8 +124,11 @@ void rtw_bf_init_bfer_entry_mu(struct rtw_dev *rtwdev, void rtw_bf_cfg_sounding(struct rtw_dev *rtwdev, struct rtw_vif *vif, enum rtw_trx_desc_rate rate) { + u8 csi_rsc = CSI_RSC_FOLLOW_RX_PACKET_BW; u32 psf_ctl = 0; - u8 csi_rsc = 0x1; + + if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) + csi_rsc = CSI_RSC_PRIMARY_20M_BW; psf_ctl = rtw_read32(rtwdev, REG_BBPSF_CTRL) | BIT_WMAC_USE_NDPARATE | @@ -387,6 +390,9 @@ void rtw_bf_cfg_csi_rate(struct rtw_dev *rtwdev, u8 rssi, u8 cur_rate, csi_cfg = rtw_read32(rtwdev, REG_BBPSF_CTRL) & ~BIT_MASK_CSI_RATE; cur_rrsr = rtw_read16(rtwdev, REG_RRSR); + if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) + csi_cfg |= BIT_CSI_FORCE_RATE; + if (rssi >= 40) { if (cur_rate != DESC_RATE54M) { cur_rrsr |= BIT(DESC_RATE54M); diff --git a/drivers/net/wireless/realtek/rtw88/bf.h b/drivers/net/wireless/realtek/rtw88/bf.h index 7b40c2c03856..a5d3010e6be6 100644 --- a/drivers/net/wireless/realtek/rtw88/bf.h +++ b/drivers/net/wireless/realtek/rtw88/bf.h @@ -33,6 +33,7 @@ #define BIT_SHIFT_R_MU_RL 12 #define BIT_SHIFT_WMAC_TXMU_ACKPOLICY 4 #define BIT_SHIFT_CSI_RATE 24 +#define BIT_CSI_FORCE_RATE BIT(15) #define BIT_MASK_R_MU_RL (R_MU_RL << BIT_SHIFT_R_MU_RL) #define BIT_MASK_R_MU_TABLE_VALID 0x3f @@ -48,6 +49,12 @@ #define RTW_SND_CTRL_REMOVE 0x98 #define RTW_SND_CTRL_SOUNDING 0x9B +enum csi_rsc { + CSI_RSC_PRIMARY_20M_BW = 0, + CSI_RSC_FOLLOW_RX_PACKET_BW = 1, + CSI_RSC_DUPLICATE_MODE = 2, +}; + enum csi_seg_len { HAL_CSI_SEG_4K = 0, HAL_CSI_SEG_8K = 1, From 8adb609f645b42148c14989066a971bbb5f3587e Mon Sep 17 00:00:00 2001 From: Hiroaki Yamamoto Date: Fri, 31 Oct 2025 20:16:34 +0900 Subject: [PATCH 28/80] wifi: rtw88: Add BUFFALO WI-U3-866DHP to the USB ID list BUFFALO WI-U3-866DHP (0411:03d0) is based on rtl8812bu. I locally tested this patch with a retail sample and it worked fine. Signed-off-by: Hiroaki Yamamoto Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251031111641.33653-1-hrak1529@gmail.com --- drivers/net/wireless/realtek/rtw88/rtw8822bu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c index 44e28e583964..2769b86ce1b2 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c @@ -79,6 +79,8 @@ static const struct usb_device_id rtw_8822bu_id_table[] = { .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* D-Link DWA-T185 rev. A1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03d1, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* BUFFALO WI-U2-866DM */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03d0, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* BUFFALO WI-U3-866DHP */ {}, }; MODULE_DEVICE_TABLE(usb, rtw_8822bu_id_table); From 28a45575289f3292aa9cb7bacae18ba3ee7a6adf Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:08 +0300 Subject: [PATCH 29/80] wifi: rtw89: usb: use common error path for skbs in rtw89_usb_rx_handler() Allow adding rx_skb to rx_free_queue for later reuse on the common error handling path, otherwise free it. Found by Linux Verification Center (linuxtesting.org). Fixes: 2135c28be6a8 ("wifi: rtw89: Add usb.{c,h}") Acked-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-2-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 4c47071b30fd..450631c77840 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -390,8 +390,7 @@ static void rtw89_usb_rx_handler(struct work_struct *work) if (skb_queue_len(&rtwusb->rx_queue) >= RTW89_USB_MAX_RXQ_LEN) { rtw89_warn(rtwdev, "rx_queue overflow\n"); - dev_kfree_skb_any(rx_skb); - continue; + goto free_or_reuse; } memset(&desc_info, 0, sizeof(desc_info)); @@ -402,7 +401,7 @@ static void rtw89_usb_rx_handler(struct work_struct *work) rtw89_debug(rtwdev, RTW89_DBG_HCI, "failed to allocate RX skb of size %u\n", desc_info.pkt_size); - continue; + goto free_or_reuse; } pkt_offset = desc_info.offset + desc_info.rxd_len; @@ -412,6 +411,7 @@ static void rtw89_usb_rx_handler(struct work_struct *work) rtw89_core_rx(rtwdev, &desc_info, skb); +free_or_reuse: if (skb_queue_len(&rtwusb->rx_free_queue) >= RTW89_USB_RX_SKB_NUM) dev_kfree_skb_any(rx_skb); else From 7543818e97d5d54b3b2f75f1c4dedee298d7d914 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:09 +0300 Subject: [PATCH 30/80] wifi: rtw89: usb: fix leak in rtw89_usb_write_port() When there is an attempt to write data and RTW89_FLAG_UNPLUGGED is set, this means device is disconnected and no urb is submitted. Return appropriate error code to the caller to properly free the allocated resources. Found by Linux Verification Center (linuxtesting.org). Fixes: 2135c28be6a8 ("wifi: rtw89: Add usb.{c,h}") Acked-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-3-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/usb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 450631c77840..639b84ebe942 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -236,7 +236,7 @@ static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma, int ret; if (test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags)) - return 0; + return -ENODEV; urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) @@ -285,8 +285,9 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch) ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len, txcb); if (ret) { - rtw89_err(rtwdev, "write port txch %d failed: %d\n", - txch, ret); + if (ret != -ENODEV) + rtw89_err(rtwdev, "write port txch %d failed: %d\n", + txch, ret); skb_dequeue(&txcb->tx_ack_queue); kfree(txcb); From 45a6a88f011fc57ba8518f4d032cc2b4f6543c12 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:10 +0300 Subject: [PATCH 31/80] wifi: rtw89: usb: use ieee80211_free_txskb() where appropriate rtw89_usb_ops_tx_kick_off() may need to release skb if a failure occurs. It operates mainly on skbs coming from the core wireless stack and the ones containing firmware commands. Use ieee80211_free_txskb() for the former case. Suggested-by: Ping-Ke Shih Acked-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-4-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/usb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 639b84ebe942..59cdee54feef 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -258,6 +258,15 @@ static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma, return ret; } +static void rtw89_usb_tx_free_skb(struct rtw89_dev *rtwdev, u8 txch, + struct sk_buff *skb) +{ + if (txch == RTW89_TXCH_CH12) + dev_kfree_skb_any(skb); + else + ieee80211_free_txskb(rtwdev->hw, skb); +} + static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch) { struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); @@ -272,7 +281,7 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch) txcb = kmalloc(sizeof(*txcb), GFP_ATOMIC); if (!txcb) { - dev_kfree_skb_any(skb); + rtw89_usb_tx_free_skb(rtwdev, txch, skb); continue; } @@ -291,7 +300,7 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch) skb_dequeue(&txcb->tx_ack_queue); kfree(txcb); - dev_kfree_skb_any(skb); + rtw89_usb_tx_free_skb(rtwdev, txch, skb); } } } From 8986bafb0945821b7fc160780074d39eae5f7247 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:11 +0300 Subject: [PATCH 32/80] wifi: rtw89: refine rtw89_core_tx_wait_complete() Pass TX status value directly into rtw89_core_tx_wait_complete(). This will make it a bit in sync with further patches and will give flexibility in future work. Also use scope based RCU locking which simplifies the code of the function. Found by Linux Verification Center (linuxtesting.org). Acked-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-5-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/core.h | 20 ++++++++++---------- drivers/net/wireless/realtek/rtw89/pci.c | 2 +- drivers/net/wireless/realtek/rtw89/pci.h | 4 ---- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 97cb0e5f0898..d23ec4d4095a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3509,6 +3509,11 @@ struct rtw89_phy_rate_pattern { bool enable; }; +#define RTW89_TX_DONE 0x0 +#define RTW89_TX_RETRY_LIMIT 0x1 +#define RTW89_TX_LIFE_TIME 0x2 +#define RTW89_TX_MACID_DROP 0x3 + #define RTW89_TX_WAIT_WORK_TIMEOUT msecs_to_jiffies(500) struct rtw89_tx_wait_info { struct rcu_head rcu_head; @@ -7388,25 +7393,20 @@ static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev, static inline bool rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev, struct rtw89_tx_skb_data *skb_data, - bool tx_done) + u8 tx_status) { struct rtw89_tx_wait_info *wait; - bool ret = false; - rcu_read_lock(); + guard(rcu)(); wait = rcu_dereference(skb_data->wait); if (!wait) - goto out; + return false; - ret = true; - wait->tx_done = tx_done; + wait->tx_done = tx_status == RTW89_TX_DONE; /* Don't access skb anymore after completion */ complete_all(&wait->completion); - -out: - rcu_read_unlock(); - return ret; + return true; } static inline bool rtw89_is_mlo_1_1(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 0ee5f8579447..b1985193a18f 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -464,7 +464,7 @@ static void rtw89_pci_tx_status(struct rtw89_dev *rtwdev, struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb); struct ieee80211_tx_info *info; - if (rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status == RTW89_TX_DONE)) + if (rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status)) return; info = IEEE80211_SKB_CB(skb); diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index cb05c83dfd56..16dfb0e79d77 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -1487,10 +1487,6 @@ struct rtw89_pci_tx_addr_info_32_v1 { #define RTW89_PCI_RPP_POLLUTED BIT(31) #define RTW89_PCI_RPP_SEQ GENMASK(30, 16) #define RTW89_PCI_RPP_TX_STATUS GENMASK(15, 13) -#define RTW89_TX_DONE 0x0 -#define RTW89_TX_RETRY_LIMIT 0x1 -#define RTW89_TX_LIFE_TIME 0x2 -#define RTW89_TX_MACID_DROP 0x3 #define RTW89_PCI_RPP_QSEL GENMASK(12, 8) #define RTW89_PCI_RPP_MACID GENMASK(7, 0) From c33c6a1b6f72ae48570530a271c68131c17abdbb Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:12 +0300 Subject: [PATCH 33/80] wifi: rtw89: implement C2H TX report handler rtw89 has several ways of handling TX status report events. The first one is based on RPP feature which is used by PCIe HCI. The other one depends on firmware sending a corresponding C2H message, quite similar to what rtw88 has. Toggle a bit in the TX descriptor to indicate to the firmware that TX report for the frame is expected. This will allow handling TX wait skbs and the ones flagged with IEEE80211_TX_CTL_REQ_TX_STATUS correctly. Do the bulk of the patch according to the vendor driver for RTL8851BU. However, there are slight differences in C2H message format between different types of chips. RTL885xB ones follow format V0. RTL8852C has format V1, and RTL8922AU has format V2. Found by Linux Verification Center (linuxtesting.org). Suggested-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-6-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/core.c | 28 +++++++++++--- drivers/net/wireless/realtek/rtw89/core.h | 4 ++ drivers/net/wireless/realtek/rtw89/fw.h | 41 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 46 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 7 ++++ drivers/net/wireless/realtek/rtw89/txrx.h | 6 ++- 6 files changed, 126 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 1f92be300ea7..84f94c796a48 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1415,7 +1415,10 @@ static __le32 rtw89_build_txwd_info1(struct rtw89_tx_desc_info *desc_info) u32 dword = FIELD_PREP(RTW89_TXWD_INFO1_MAX_AGGNUM, desc_info->ampdu_num) | FIELD_PREP(RTW89_TXWD_INFO1_A_CTRL_BSR, desc_info->a_ctrl_bsr) | FIELD_PREP(RTW89_TXWD_INFO1_DATA_RTY_LOWEST_RATE, - desc_info->data_retry_lowest_rate); + desc_info->data_retry_lowest_rate) | + FIELD_PREP(RTW89_TXWD_INFO1_DATA_TXCNT_LMT_SEL, + desc_info->tx_cnt_lmt_en) | + FIELD_PREP(RTW89_TXWD_INFO1_DATA_TXCNT_LMT, desc_info->tx_cnt_lmt); return cpu_to_le32(dword); } @@ -1439,11 +1442,19 @@ static __le32 rtw89_build_txwd_info2_v1(struct rtw89_tx_desc_info *desc_info) return cpu_to_le32(dword); } +static __le32 rtw89_build_txwd_info3(struct rtw89_tx_desc_info *desc_info) +{ + u32 dword = FIELD_PREP(RTW89_TXWD_INFO3_SPE_RPT, desc_info->report); + + return cpu_to_le32(dword); +} + static __le32 rtw89_build_txwd_info4(struct rtw89_tx_desc_info *desc_info) { bool rts_en = !desc_info->is_bmc; u32 dword = FIELD_PREP(RTW89_TXWD_INFO4_RTS_EN, rts_en) | - FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, 1); + FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, 1) | + FIELD_PREP(RTW89_TXWD_INFO4_SW_DEFINE, desc_info->sn); return cpu_to_le32(dword); } @@ -1466,6 +1477,7 @@ void rtw89_core_fill_txdesc(struct rtw89_dev *rtwdev, txwd_info->dword0 = rtw89_build_txwd_info0(desc_info); txwd_info->dword1 = rtw89_build_txwd_info1(desc_info); txwd_info->dword2 = rtw89_build_txwd_info2(desc_info); + txwd_info->dword3 = rtw89_build_txwd_info3(desc_info); txwd_info->dword4 = rtw89_build_txwd_info4(desc_info); } @@ -1495,6 +1507,7 @@ void rtw89_core_fill_txdesc_v1(struct rtw89_dev *rtwdev, txwd_info->dword0 = rtw89_build_txwd_info0_v1(desc_info); txwd_info->dword1 = rtw89_build_txwd_info1(desc_info); txwd_info->dword2 = rtw89_build_txwd_info2_v1(desc_info); + txwd_info->dword3 = rtw89_build_txwd_info3(desc_info); txwd_info->dword4 = rtw89_build_txwd_info4(desc_info); } EXPORT_SYMBOL(rtw89_core_fill_txdesc_v1); @@ -1580,7 +1593,10 @@ static __le32 rtw89_build_txwd_info0_v2(struct rtw89_tx_desc_info *desc_info) u32 dword = FIELD_PREP(BE_TXD_INFO0_DATA_STBC, desc_info->stbc) | FIELD_PREP(BE_TXD_INFO0_DATA_LDPC, desc_info->ldpc) | FIELD_PREP(BE_TXD_INFO0_DISDATAFB, desc_info->dis_data_fb) | - FIELD_PREP(BE_TXD_INFO0_MULTIPORT_ID, desc_info->port); + FIELD_PREP(BE_TXD_INFO0_MULTIPORT_ID, desc_info->port) | + FIELD_PREP(BE_TXD_INFO0_DATA_TXCNT_LMT_SEL, + desc_info->tx_cnt_lmt_en) | + FIELD_PREP(BE_TXD_INFO0_DATA_TXCNT_LMT, desc_info->tx_cnt_lmt); return cpu_to_le32(dword); } @@ -1590,7 +1606,8 @@ static __le32 rtw89_build_txwd_info1_v2(struct rtw89_tx_desc_info *desc_info) u32 dword = FIELD_PREP(BE_TXD_INFO1_MAX_AGG_NUM, desc_info->ampdu_num) | FIELD_PREP(BE_TXD_INFO1_A_CTRL_BSR, desc_info->a_ctrl_bsr) | FIELD_PREP(BE_TXD_INFO1_DATA_RTY_LOWEST_RATE, - desc_info->data_retry_lowest_rate); + desc_info->data_retry_lowest_rate) | + FIELD_PREP(BE_TXD_INFO1_SW_DEFINE, desc_info->sn); return cpu_to_le32(dword); } @@ -1599,7 +1616,8 @@ static __le32 rtw89_build_txwd_info2_v2(struct rtw89_tx_desc_info *desc_info) { u32 dword = FIELD_PREP(BE_TXD_INFO2_AMPDU_DENSITY, desc_info->ampdu_density) | FIELD_PREP(BE_TXD_INFO2_FORCE_KEY_EN, desc_info->sec_en) | - FIELD_PREP(BE_TXD_INFO2_SEC_CAM_IDX, desc_info->sec_cam_idx); + FIELD_PREP(BE_TXD_INFO2_SEC_CAM_IDX, desc_info->sec_cam_idx) | + FIELD_PREP(BE_TXD_INFO2_SPE_RPT_V1, desc_info->report); return cpu_to_le32(dword); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d23ec4d4095a..2914cc4e97d5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1169,6 +1169,10 @@ struct rtw89_tx_desc_info { u8 ampdu_density; u8 ampdu_num; bool sec_en; + bool report; + bool tx_cnt_lmt_en; + u8 sn: 4; + u8 tx_cnt_lmt: 6; u8 addr_info_nr; u8 sec_keyid; u8 sec_type; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index ddebf7972068..310580206368 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3747,6 +3747,47 @@ struct rtw89_c2h_scanofld { #define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(15, 8)) +struct rtw89_c2h_mac_tx_rpt { + struct rtw89_c2h_hdr hdr; + __le32 w2; + __le32 w3; + __le32 w4; + __le32 w5; + __le32 w6; + __le32 w7; +} __packed; + +#define RTW89_C2H_MAC_TX_RPT_W2_TX_STATE GENMASK(7, 6) +#define RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE GENMASK(11, 8) +#define RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT GENMASK(13, 8) +#define RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT_V1 GENMASK(15, 10) + +struct rtw89_c2h_mac_tx_rpt_v2 { + struct rtw89_c2h_hdr hdr; + __le32 w2; + __le32 w3; + __le32 w4; + __le32 w5; + __le32 w6; + __le32 w7; + __le32 w8; + __le32 w9; + __le32 w10; + __le32 w11; + __le32 w12; + __le32 w13; + __le32 w14; + __le32 w15; + __le32 w16; + __le32 w17; + __le32 w18; + __le32 w19; +} __packed; + +#define RTW89_C2H_MAC_TX_RPT_W12_TX_STATE_V2 GENMASK(9, 8) +#define RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2 GENMASK(15, 12) +#define RTW89_C2H_MAC_TX_RPT_W14_DATA_TX_CNT_V2 GENMASK(15, 10) + struct rtw89_mac_mcc_tsf_rpt { u32 macid_x; u32 macid_y; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index bc39b4f7bc4f..47048d125c01 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5488,6 +5488,40 @@ rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data); } +static void +rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 sw_define, tx_status, txcnt; + + if (rtwdev->chip->chip_id == RTL8922A) { + const struct rtw89_c2h_mac_tx_rpt_v2 *rpt_v2; + + rpt_v2 = (const struct rtw89_c2h_mac_tx_rpt_v2 *)c2h->data; + sw_define = le32_get_bits(rpt_v2->w12, + RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2); + tx_status = le32_get_bits(rpt_v2->w12, + RTW89_C2H_MAC_TX_RPT_W12_TX_STATE_V2); + txcnt = le32_get_bits(rpt_v2->w14, + RTW89_C2H_MAC_TX_RPT_W14_DATA_TX_CNT_V2); + } else { + const struct rtw89_c2h_mac_tx_rpt *rpt; + + rpt = (const struct rtw89_c2h_mac_tx_rpt *)c2h->data; + sw_define = le32_get_bits(rpt->w2, RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE); + tx_status = le32_get_bits(rpt->w2, RTW89_C2H_MAC_TX_RPT_W2_TX_STATE); + if (rtwdev->chip->chip_id == RTL8852C) + txcnt = le32_get_bits(rpt->w5, + RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT_V1); + else + txcnt = le32_get_bits(rpt->w5, + RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT); + } + + rtw89_debug(rtwdev, RTW89_DBG_TXRX, + "C2H TX RPT: sn %d, tx_status %d, txcnt %d\n", + sw_define, tx_status, txcnt); +} + static void rtw89_mac_c2h_mrc_tsf_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { @@ -5722,6 +5756,12 @@ void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev, [RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT] = rtw89_mac_c2h_mcc_status_rpt, }; +static +void (* const rtw89_mac_c2h_misc_handler[])(struct rtw89_dev *rtwdev, + struct sk_buff *c2h, u32 len) = { + [RTW89_MAC_C2H_FUNC_TX_REPORT] = rtw89_mac_c2h_tx_rpt, +}; + static void (* const rtw89_mac_c2h_mlo_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { @@ -5808,6 +5848,8 @@ bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, struct sk_buff *c2h, } case RTW89_MAC_C2H_CLASS_MCC: return true; + case RTW89_MAC_C2H_CLASS_MISC: + return true; case RTW89_MAC_C2H_CLASS_MLO: return true; case RTW89_MAC_C2H_CLASS_MRC: @@ -5843,6 +5885,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC) handler = rtw89_mac_c2h_mcc_handler[func]; break; + case RTW89_MAC_C2H_CLASS_MISC: + if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MISC) + handler = rtw89_mac_c2h_misc_handler[func]; + break; case RTW89_MAC_C2H_CLASS_MLO: if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MLO) handler = rtw89_mac_c2h_mlo_handler[func]; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 46302f4aa3d9..dfa85ade38ce 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -432,6 +432,12 @@ enum rtw89_mac_c2h_mcc_func { NUM_OF_RTW89_MAC_C2H_FUNC_MCC, }; +enum rtw89_mac_c2h_misc_func { + RTW89_MAC_C2H_FUNC_TX_REPORT = 1, + + NUM_OF_RTW89_MAC_C2H_FUNC_MISC, +}; + enum rtw89_mac_c2h_mlo_func { RTW89_MAC_C2H_FUNC_MLO_GET_TBL = 0x0, RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE = 0x1, @@ -470,6 +476,7 @@ enum rtw89_mac_c2h_class { RTW89_MAC_C2H_CLASS_WOW = 0x3, RTW89_MAC_C2H_CLASS_MCC = 0x4, RTW89_MAC_C2H_CLASS_FWDBG = 0x5, + RTW89_MAC_C2H_CLASS_MISC = 0x9, RTW89_MAC_C2H_CLASS_MLO = 0xc, RTW89_MAC_C2H_CLASS_MRC = 0xe, RTW89_MAC_C2H_CLASS_AP = 0x18, diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 984c9fdbb018..b37dbac7b790 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -127,6 +127,8 @@ static inline u8 rtw89_get_data_nss(struct rtw89_dev *rtwdev, u16 hw_rate) #define RTW89_TXWD_INFO0_MULTIPORT_ID GENMASK(6, 4) /* TX WD INFO DWORD 1 */ +#define RTW89_TXWD_INFO1_DATA_TXCNT_LMT_SEL BIT(31) +#define RTW89_TXWD_INFO1_DATA_TXCNT_LMT GENMASK(30, 25) #define RTW89_TXWD_INFO1_DATA_RTY_LOWEST_RATE GENMASK(24, 16) #define RTW89_TXWD_INFO1_A_CTRL_BSR BIT(14) #define RTW89_TXWD_INFO1_MAX_AGGNUM GENMASK(7, 0) @@ -139,10 +141,12 @@ static inline u8 rtw89_get_data_nss(struct rtw89_dev *rtwdev, u16 hw_rate) #define RTW89_TXWD_INFO2_SEC_CAM_IDX GENMASK(7, 0) /* TX WD INFO DWORD 3 */ +#define RTW89_TXWD_INFO3_SPE_RPT BIT(10) /* TX WD INFO DWORD 4 */ -#define RTW89_TXWD_INFO4_RTS_EN BIT(27) #define RTW89_TXWD_INFO4_HW_RTS_EN BIT(31) +#define RTW89_TXWD_INFO4_RTS_EN BIT(27) +#define RTW89_TXWD_INFO4_SW_DEFINE GENMASK(3, 0) /* TX WD INFO DWORD 5 */ From 21b946104087cd48e16ea51903c421cc3962225b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 4 Nov 2025 16:57:13 +0300 Subject: [PATCH 34/80] wifi: rtw89: fill TX descriptor of FWCMD in shortcut TX type FWCMD is used to download firmware and send H2C commands, and it only fill few fields of TX description, such as desc_info->pkt_size. Therefore, early return the TX type FWCMD. Signed-off-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Link: https://patch.msgid.link/20251104135720.321110-7-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/core.c | 26 ++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 84f94c796a48..fd6624d12efe 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1106,32 +1106,35 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (void *)skb->data; struct rtw89_addr_cam_entry *addr_cam; - enum rtw89_core_tx_type tx_type; enum btc_pkt_type pkt_type; bool upd_wlan_hdr = false; bool is_bmc; u16 seq; + desc_info->pkt_size = skb->len; + + if (unlikely(tx_req->tx_type == RTW89_CORE_TX_TYPE_FWCMD)) { + rtw89_core_tx_update_h2c_info(rtwdev, tx_req); + return; + } + + tx_req->tx_type = rtw89_core_get_tx_type(rtwdev, skb); + if (tx_req->sta) desc_info->mlo = tx_req->sta->mlo; else if (tx_req->vif) desc_info->mlo = ieee80211_vif_is_mld(tx_req->vif); seq = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; - if (tx_req->tx_type != RTW89_CORE_TX_TYPE_FWCMD) { - tx_type = rtw89_core_get_tx_type(rtwdev, skb); - tx_req->tx_type = tx_type; + addr_cam = rtw89_get_addr_cam_of(tx_req->rtwvif_link, + tx_req->rtwsta_link); + if (addr_cam->valid && desc_info->mlo) + upd_wlan_hdr = true; - addr_cam = rtw89_get_addr_cam_of(tx_req->rtwvif_link, - tx_req->rtwsta_link); - if (addr_cam->valid && desc_info->mlo) - upd_wlan_hdr = true; - } is_bmc = (is_broadcast_ether_addr(hdr->addr1) || is_multicast_ether_addr(hdr->addr1)); desc_info->seq = seq; - desc_info->pkt_size = skb->len; desc_info->is_bmc = is_bmc; desc_info->wd_page = true; desc_info->hiq = info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM; @@ -1148,8 +1151,7 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, rtw89_core_tx_update_ampdu_info(rtwdev, tx_req, pkt_type); rtw89_core_tx_update_llc_hdr(rtwdev, desc_info, skb); break; - case RTW89_CORE_TX_TYPE_FWCMD: - rtw89_core_tx_update_h2c_info(rtwdev, tx_req); + default: break; } } From 26a42d804aa89f6e62be68126114a4b62a8fe713 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:14 +0300 Subject: [PATCH 35/80] wifi: rtw89: usb: anchor TX URBs During HCI reset all pending TX URBs should be canceled. Use anchor to keep track of them and have an ability to cancel them synchronously. Note however that canceling RX URBs can't be done here in rtw89_usb_ops_reset() as it breaks driver initialization. Found by Linux Verification Center (linuxtesting.org). Acked-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-8-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/usb.c | 24 ++++++++++++++++++++---- drivers/net/wireless/realtek/rtw89/usb.h | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 59cdee54feef..b7b981bac7bf 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -221,7 +221,6 @@ static void rtw89_usb_write_port_complete(struct urb *urb) } kfree(txcb); - usb_free_urb(urb); } static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma, @@ -247,10 +246,17 @@ static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma, usb_fill_bulk_urb(urb, usbd, pipe, data, len, rtw89_usb_write_port_complete, context); urb->transfer_flags |= URB_ZERO_PACKET; - ret = usb_submit_urb(urb, GFP_ATOMIC); + usb_anchor_urb(urb, &rtwusb->tx_submitted); + ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) - usb_free_urb(urb); + usb_unanchor_urb(urb); + + /* release our reference to this URB, USB core will eventually free it + * on its own after the completion callback finishes (or URB is + * immediately freed here if its submission has failed) + */ + usb_free_urb(urb); if (ret == -ENODEV) set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); @@ -557,6 +563,11 @@ static void rtw89_usb_cancel_rx_bufs(struct rtw89_usb *rtwusb) } } +static void rtw89_usb_cancel_tx_bufs(struct rtw89_usb *rtwusb) +{ + usb_kill_anchored_urbs(&rtwusb->tx_submitted); +} + static void rtw89_usb_free_rx_bufs(struct rtw89_usb *rtwusb) { struct rtw89_usb_rx_ctrl_block *rxcb; @@ -658,7 +669,9 @@ static void rtw89_usb_deinit_tx(struct rtw89_dev *rtwdev) static void rtw89_usb_ops_reset(struct rtw89_dev *rtwdev) { - /* TODO: anything to do here? */ + struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + + rtw89_usb_cancel_tx_bufs(rtwusb); } static int rtw89_usb_ops_start(struct rtw89_dev *rtwdev) @@ -895,6 +908,8 @@ static int rtw89_usb_intf_init(struct rtw89_dev *rtwdev, struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); int ret; + init_usb_anchor(&rtwusb->tx_submitted); + ret = rtw89_usb_parse(rtwdev, intf); if (ret) return ret; @@ -1021,6 +1036,7 @@ void rtw89_usb_disconnect(struct usb_interface *intf) rtwusb = rtw89_usb_priv(rtwdev); rtw89_usb_cancel_rx_bufs(rtwusb); + rtw89_usb_cancel_tx_bufs(rtwusb); rtw89_core_unregister(rtwdev); rtw89_core_deinit(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index 32cdbf5128e4..203ec8e993e9 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -60,6 +60,7 @@ struct rtw89_usb { struct sk_buff_head rx_free_queue; struct work_struct rx_work; struct work_struct rx_urb_work; + struct usb_anchor tx_submitted; struct sk_buff_head tx_queue[RTW89_TXCH_NUM]; }; From 816e849ef83aaf989035545d4a927b50a3d03525 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:15 +0300 Subject: [PATCH 36/80] wifi: rtw89: handle IEEE80211_TX_CTL_REQ_TX_STATUS frames for USB Frames flagged with IEEE80211_TX_CTL_REQ_TX_STATUS mean the driver has to report to mac80211 stack whether AP sent ACK for the null frame/probe request or not. It's not implemented in USB part of the driver yet. PCIe HCI has its own way of getting TX status incorporated into RPP feature, and it's always enabled there. Other HCIs need a different scheme based on processing C2H messages. Thus define a .tx_rpt_enabled flag indicating which HCIs need to enable a TX report feature. Currently it is USB only. Toggle a bit in the TX descriptor and place flagged skbs in a fix-sized queue to wait for a message from the firmware. Firmware maintains a 4-bit sequence number for required frames hence the queue can contain just 16 elements simultaneously. That's enough for normal driver / firmware communication. If the firmware crashes for any reason and doesn't provide TX reports in time, driver will handle this and report the obsolete frames as dropped. rtw89 also has a new feature providing a TX report for each transmission attempt. Ignore a failed TX status reported by the firmware until retry limit is reached or successful status appears. When there is no success and the retry limit is reached, report the frame up to the wireless stack as failed eventually. HCI reset should stop all pending TX activity so forcefully flush the queue there. Found by Linux Verification Center (linuxtesting.org). Signed-off-by: Fedor Pchelkin Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-9-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/core.c | 4 ++ drivers/net/wireless/realtek/rtw89/core.h | 14 ++++ drivers/net/wireless/realtek/rtw89/mac.c | 32 +++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 81 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/usb.c | 18 +++++ 5 files changed, 149 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index fd6624d12efe..6059e30a0590 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1131,6 +1131,9 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, if (addr_cam->valid && desc_info->mlo) upd_wlan_hdr = true; + if (rtw89_is_tx_rpt_skb(tx_req->skb)) + rtw89_tx_rpt_init(rtwdev, tx_req); + is_bmc = (is_broadcast_ether_addr(hdr->addr1) || is_multicast_ether_addr(hdr->addr1)); @@ -5865,6 +5868,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) wiphy_work_init(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work); INIT_WORK(&rtwdev->load_firmware_work, rtw89_load_firmware_work); + spin_lock_init(&rtwdev->tx_rpt.skb_lock); skb_queue_head_init(&rtwdev->c2h_queue); rtw89_core_ppdu_sts_init(rtwdev); rtw89_traffic_stats_init(rtwdev, &rtwdev->stats); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 2914cc4e97d5..cb71bd87b193 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3518,6 +3518,15 @@ struct rtw89_phy_rate_pattern { #define RTW89_TX_LIFE_TIME 0x2 #define RTW89_TX_MACID_DROP 0x3 +#define RTW89_MAX_TX_RPTS 16 +#define RTW89_MAX_TX_RPTS_MASK (RTW89_MAX_TX_RPTS - 1) +struct rtw89_tx_rpt { + struct sk_buff *skbs[RTW89_MAX_TX_RPTS]; + /* protect skbs array access/modification */ + spinlock_t skb_lock; + atomic_t sn; +}; + #define RTW89_TX_WAIT_WORK_TIMEOUT msecs_to_jiffies(500) struct rtw89_tx_wait_info { struct rcu_head rcu_head; @@ -3529,6 +3538,8 @@ struct rtw89_tx_wait_info { struct rtw89_tx_skb_data { struct rtw89_tx_wait_info __rcu *wait; + u8 tx_rpt_sn; + u8 tx_pkt_cnt_lmt; u8 hci_priv[]; }; @@ -3698,6 +3709,7 @@ struct rtw89_hci_info { u32 rpwm_addr; u32 cpwm_addr; bool paused; + bool tx_rpt_enabled; }; struct rtw89_chip_ops { @@ -6021,6 +6033,8 @@ struct rtw89_dev { struct list_head tx_waits; struct wiphy_delayed_work tx_wait_work; + struct rtw89_tx_rpt tx_rpt; + struct rtw89_cam_info cam_info; struct sk_buff_head c2h_queue; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 47048d125c01..0a8474002cb7 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5491,7 +5491,10 @@ rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 static void rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { + struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt; + struct rtw89_tx_skb_data *skb_data; u8 sw_define, tx_status, txcnt; + struct sk_buff *skb; if (rtwdev->chip->chip_id == RTL8922A) { const struct rtw89_c2h_mac_tx_rpt_v2 *rpt_v2; @@ -5520,6 +5523,35 @@ rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) rtw89_debug(rtwdev, RTW89_DBG_TXRX, "C2H TX RPT: sn %d, tx_status %d, txcnt %d\n", sw_define, tx_status, txcnt); + + /* claim sw_define is not over size of tx_rpt->skbs[] */ + static_assert(hweight32(RTW89_MAX_TX_RPTS_MASK) == + hweight32(RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2) && + hweight32(RTW89_MAX_TX_RPTS_MASK) == + hweight32(RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE)); + + scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) { + skb = tx_rpt->skbs[sw_define]; + + /* skip if no skb (normally shouldn't happen) */ + if (!skb) { + rtw89_debug(rtwdev, RTW89_DBG_TXRX, + "C2H TX RPT: no skb found in queue\n"); + return; + } + + skb_data = RTW89_TX_SKB_CB(skb); + + /* skip if TX attempt has failed and retry limit has not been + * reached yet + */ + if (tx_status != RTW89_TX_DONE && + txcnt != skb_data->tx_pkt_cnt_lmt) + return; + + tx_rpt->skbs[sw_define] = NULL; + rtw89_tx_rpt_tx_status(rtwdev, skb, tx_status); + } } static void diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index dfa85ade38ce..56751dd6e99b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1626,4 +1626,85 @@ int rtw89_mac_scan_offload(struct rtw89_dev *rtwdev, return ret; } + +static inline +void rtw89_tx_rpt_init(struct rtw89_dev *rtwdev, + struct rtw89_core_tx_request *tx_req) +{ + struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt; + + if (!rtwdev->hci.tx_rpt_enabled) + return; + + tx_req->desc_info.report = true; + /* firmware maintains a 4-bit sequence number */ + tx_req->desc_info.sn = atomic_inc_return(&tx_rpt->sn) & + RTW89_MAX_TX_RPTS_MASK; + tx_req->desc_info.tx_cnt_lmt_en = true; + tx_req->desc_info.tx_cnt_lmt = 8; +} + +static inline +bool rtw89_is_tx_rpt_skb(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + return info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS; +} + +static inline +void rtw89_tx_rpt_tx_status(struct rtw89_dev *rtwdev, struct sk_buff *skb, + u8 tx_status) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + ieee80211_tx_info_clear_status(info); + + if (tx_status == RTW89_TX_DONE) + info->flags |= IEEE80211_TX_STAT_ACK; + else + info->flags &= ~IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(rtwdev->hw, skb); +} + +static inline +void rtw89_tx_rpt_skb_add(struct rtw89_dev *rtwdev, struct sk_buff *skb) +{ + struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt; + struct rtw89_tx_skb_data *skb_data; + u8 idx; + + skb_data = RTW89_TX_SKB_CB(skb); + idx = skb_data->tx_rpt_sn; + + scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) { + /* if skb having the similar seq number is still in the queue, + * this means the queue is overflowed - it isn't normal and + * should indicate firmware doesn't provide TX reports in time; + * report the old skb as dropped, we can't do much more here + */ + if (tx_rpt->skbs[idx]) + rtw89_tx_rpt_tx_status(rtwdev, tx_rpt->skbs[idx], + RTW89_TX_MACID_DROP); + tx_rpt->skbs[idx] = skb; + } +} + +static inline +void rtw89_tx_rpt_skbs_purge(struct rtw89_dev *rtwdev) +{ + struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt; + struct sk_buff *skbs[RTW89_MAX_TX_RPTS]; + + scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) { + memcpy(skbs, tx_rpt->skbs, sizeof(tx_rpt->skbs)); + memset(tx_rpt->skbs, 0, sizeof(tx_rpt->skbs)); + } + + for (int i = 0; i < ARRAY_SIZE(skbs); i++) + if (skbs[i]) + rtw89_tx_rpt_tx_status(rtwdev, skbs[i], + RTW89_TX_MACID_DROP); +} #endif diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index b7b981bac7bf..f54e00c3033e 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -194,6 +194,15 @@ static void rtw89_usb_write_port_complete(struct urb *urb) skb_pull(skb, txdesc_size); + if (rtw89_is_tx_rpt_skb(skb)) { + if (urb->status == 0) + rtw89_tx_rpt_skb_add(rtwdev, skb); + else + rtw89_tx_rpt_tx_status(rtwdev, skb, + RTW89_TX_MACID_DROP); + continue; + } + info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); @@ -358,6 +367,7 @@ static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev, { struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + struct rtw89_tx_skb_data *skb_data; struct sk_buff *skb = tx_req->skb; struct rtw89_txwd_body *txdesc; u32 txdesc_size; @@ -384,6 +394,12 @@ static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev, le32p_replace_bits(&txdesc->dword0, 1, RTW89_TXWD_BODY0_STF_MODE); + skb_data = RTW89_TX_SKB_CB(skb); + if (tx_req->desc_info.sn) + skb_data->tx_rpt_sn = tx_req->desc_info.sn; + if (tx_req->desc_info.tx_cnt_lmt) + skb_data->tx_pkt_cnt_lmt = tx_req->desc_info.tx_cnt_lmt; + skb_queue_tail(&rtwusb->tx_queue[desc_info->ch_dma], skb); return 0; @@ -672,6 +688,7 @@ static void rtw89_usb_ops_reset(struct rtw89_dev *rtwdev) struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); rtw89_usb_cancel_tx_bufs(rtwusb); + rtw89_tx_rpt_skbs_purge(rtwdev); } static int rtw89_usb_ops_start(struct rtw89_dev *rtwdev) @@ -962,6 +979,7 @@ int rtw89_usb_probe(struct usb_interface *intf, rtwdev->hci.ops = &rtw89_usb_ops; rtwdev->hci.type = RTW89_HCI_TYPE_USB; + rtwdev->hci.tx_rpt_enabled = true; ret = rtw89_usb_intf_init(rtwdev, intf); if (ret) { From cc7070e41786a51953f71e39a1e6e803a1ed2d50 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:16 +0300 Subject: [PATCH 37/80] wifi: rtw89: provide TX reports for management frames In order to provide TX reports for the management queue rtw89 should configure the firmware. Do this with SET_CMC_TBL_MGQ_RPT_EN() for the WiFi6 chips and with CCTLINFO_G7_W0_MGQ_RPT_EN flag for the WiFi7 ones. Suggested-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-10-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/fw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index cb431c8a65ac..96f0463e66d6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3165,6 +3165,7 @@ int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, SET_CMC_TBL_ANTSEL_C(skb->data, 0); SET_CMC_TBL_ANTSEL_D(skb->data, 0); } + SET_CMC_TBL_MGQ_RPT_EN(skb->data, rtwdev->hci.tx_rpt_enabled); SET_CMC_TBL_DOPPLER_CTRL(skb->data, 0); SET_CMC_TBL_TXPWR_TOLERENCE(skb->data, 0); if (rtwvif_link->net_type == RTW89_NET_TYPE_AP_MODE) @@ -3210,7 +3211,8 @@ int rtw89_fw_h2c_default_cmac_tbl_g7(struct rtw89_dev *rtwdev, h2c->c0 = le32_encode_bits(mac_id, CCTLINFO_G7_C0_MACID) | le32_encode_bits(1, CCTLINFO_G7_C0_OP); - h2c->w0 = le32_encode_bits(4, CCTLINFO_G7_W0_DATARATE); + h2c->w0 = le32_encode_bits(4, CCTLINFO_G7_W0_DATARATE) | + le32_encode_bits(rtwdev->hci.tx_rpt_enabled, CCTLINFO_G7_W0_MGQ_RPT_EN); h2c->m0 = cpu_to_le32(CCTLINFO_G7_W0_ALL); h2c->w1 = le32_encode_bits(4, CCTLINFO_G7_W1_DATA_RTY_LOWEST_RATE) | From d5da3d9fb05f6e2b2685c58b37067749b342b518 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Tue, 4 Nov 2025 16:57:17 +0300 Subject: [PATCH 38/80] wifi: rtw89: process TX wait skbs for USB via C2H handler TX wait skbs need to be completed when they are done. PCIe part does this inside rtw89_pci_tx_status() during RPP processing. Other HCIs use a mechanism based on C2H firmware messages. Store TX wait skbs inside TX report queue so that it'll be possible to identify completed items inside the C2H handler. Try to do this as similar to PCIe path as possible. When the corresponding TX wait skb is found inside TX report queue, unlink it from there and call rtw89_core_tx_wait_complete() to mark the completion. If the callee waiting for the completion has already timed out, the TX wait skb is placed into TX wait list (like PCIe part does). It's important that during HCI reset all pending TX wait frames should be completed inside hci.ops->reset method before calling rtw89_tx_wait_list_clear(). Found by Linux Verification Center (linuxtesting.org). Acked-by: Ping-Ke Shih Signed-off-by: Fedor Pchelkin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251104135720.321110-11-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw89/core.c | 5 ++--- drivers/net/wireless/realtek/rtw89/core.h | 7 +++++++ drivers/net/wireless/realtek/rtw89/mac.h | 13 ++++++++++--- drivers/net/wireless/realtek/rtw89/usb.c | 2 +- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 6059e30a0590..1d30ff27bce1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1131,7 +1131,7 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, if (addr_cam->valid && desc_info->mlo) upd_wlan_hdr = true; - if (rtw89_is_tx_rpt_skb(tx_req->skb)) + if (rtw89_is_tx_rpt_skb(rtwdev, tx_req->skb)) rtw89_tx_rpt_init(rtwdev, tx_req); is_bmc = (is_broadcast_ether_addr(hdr->addr1) || @@ -1263,14 +1263,13 @@ static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev, tx_req.rtwvif_link = rtwvif_link; tx_req.rtwsta_link = rtwsta_link; tx_req.desc_info.sw_mld = sw_mld; + rcu_assign_pointer(skb_data->wait, wait); rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, true, true); rtw89_wow_parse_akm(rtwdev, skb); rtw89_core_tx_update_desc_info(rtwdev, &tx_req); rtw89_core_tx_wake(rtwdev, &tx_req); - rcu_assign_pointer(skb_data->wait, wait); - ret = rtw89_hci_tx_write(rtwdev, &tx_req); if (ret) { rtw89_err(rtwdev, "failed to transmit skb to HCI\n"); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index cb71bd87b193..71eb7078a0dc 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6328,6 +6328,7 @@ static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev, static inline void rtw89_hci_reset(struct rtw89_dev *rtwdev) { rtwdev->hci.ops->reset(rtwdev); + /* hci.ops->reset must complete all pending TX wait SKBs */ rtw89_tx_wait_list_clear(rtwdev); } @@ -7409,6 +7410,12 @@ static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev, return dev_alloc_skb(length); } +static inline bool rtw89_core_is_tx_wait(struct rtw89_dev *rtwdev, + struct rtw89_tx_skb_data *skb_data) +{ + return rcu_access_pointer(skb_data->wait); +} + static inline bool rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev, struct rtw89_tx_skb_data *skb_data, u8 tx_status) diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 56751dd6e99b..ce3474a7ba03 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1645,19 +1645,26 @@ void rtw89_tx_rpt_init(struct rtw89_dev *rtwdev, } static inline -bool rtw89_is_tx_rpt_skb(struct sk_buff *skb) +bool rtw89_is_tx_rpt_skb(struct rtw89_dev *rtwdev, struct sk_buff *skb) { + struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - return info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS; + return rtw89_core_is_tx_wait(rtwdev, skb_data) || + (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS); } static inline void rtw89_tx_rpt_tx_status(struct rtw89_dev *rtwdev, struct sk_buff *skb, u8 tx_status) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb); + struct ieee80211_tx_info *info; + if (rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status)) + return; + + info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); if (tx_status == RTW89_TX_DONE) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index f54e00c3033e..d7d968207a39 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -194,7 +194,7 @@ static void rtw89_usb_write_port_complete(struct urb *urb) skb_pull(skb, txdesc_size); - if (rtw89_is_tx_rpt_skb(skb)) { + if (rtw89_is_tx_rpt_skb(rtwdev, skb)) { if (urb->status == 0) rtw89_tx_rpt_skb_add(rtwdev, skb); else From 233542f5b4a8ea1fe3535f8021d37e7e92ffc4e5 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Fri, 7 Nov 2025 18:06:27 +0200 Subject: [PATCH 39/80] wifi: rtw89: Use the correct power sequences for USB/SDIO Make rtw89_mac_pwr_seq() select the right parts of the power sequences based on the interface type. This is only relevant for RTL8852A. The other chips don't use power sequences. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/dec13310-06eb-429e-acb8-4c5b62656836@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 0a8474002cb7..e22b5d8c8171 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1295,11 +1295,26 @@ static int rtw89_mac_sub_pwr_seq(struct rtw89_dev *rtwdev, u8 cv_msk, static int rtw89_mac_pwr_seq(struct rtw89_dev *rtwdev, const struct rtw89_pwr_cfg * const *cfg_seq) { + u8 intf_msk; int ret; + switch (rtwdev->hci.type) { + case RTW89_HCI_TYPE_PCIE: + intf_msk = PWR_INTF_MSK_PCIE; + break; + case RTW89_HCI_TYPE_USB: + intf_msk = PWR_INTF_MSK_USB; + break; + case RTW89_HCI_TYPE_SDIO: + intf_msk = PWR_INTF_MSK_SDIO; + break; + default: + return -EOPNOTSUPP; + } + for (; *cfg_seq; cfg_seq++) { ret = rtw89_mac_sub_pwr_seq(rtwdev, BIT(rtwdev->hal.cv), - PWR_INTF_MSK_PCIE, *cfg_seq); + intf_msk, *cfg_seq); if (ret) return -EBUSY; } From 0eea5e0f03db84d3d1a1a8926fca12295bda1f04 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Fri, 7 Nov 2025 18:07:06 +0200 Subject: [PATCH 40/80] wifi: rtw89: Add rtw8852a_dle_mem_usb Add rtw8852a_dle_mem_usb and its various quotas and sizes in struct rtw89_mac_size_set. "dle" could be "Data Link Engine" or "Double Link Engine". These are some parameters needed for RTL8852AU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/d0a09039-97a8-4501-b023-510c126d8c61@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 9 +++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 5 +++++ drivers/net/wireless/realtek/rtw89/rtw8852a.c | 18 +++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index e22b5d8c8171..dab0a76556c9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1669,6 +1669,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* PCIE 64 */ .wde_size0 = {RTW89_WDE_PG_64, 4095, 1,}, .wde_size0_v1 = {RTW89_WDE_PG_64, 3328, 0, 0,}, + /* 8852A USB */ + .wde_size1 = {RTW89_WDE_PG_64, 768, 0,}, /* DLFW */ .wde_size4 = {RTW89_WDE_PG_64, 0, 4096,}, .wde_size4_v1 = {RTW89_WDE_PG_64, 0, 3328, 0,}, @@ -1692,6 +1694,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* PCIE */ .ple_size0 = {RTW89_PLE_PG_128, 1520, 16,}, .ple_size0_v1 = {RTW89_PLE_PG_128, 2688, 240, 212992,}, + /* 8852A USB */ + .ple_size1 = {RTW89_PLE_PG_128, 3184, 16,}, .ple_size3_v1 = {RTW89_PLE_PG_128, 2928, 0, 212992,}, /* DLFW */ .ple_size4 = {RTW89_PLE_PG_128, 64, 1472,}, @@ -1715,6 +1719,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* PCIE 64 */ .wde_qt0 = {3792, 196, 0, 107,}, .wde_qt0_v1 = {3302, 6, 0, 20,}, + /* 8852A USB */ + .wde_qt1 = {512, 196, 0, 60,}, /* DLFW */ .wde_qt4 = {0, 0, 0, 0,}, /* PCIE 64 */ @@ -1743,6 +1749,9 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt13 = {0, 0, 16, 48, 0, 0, 0, 0, 0, 0, 0,}, /* PCIE 64 */ .ple_qt18 = {147, 0, 16, 20, 17, 13, 89, 0, 32, 14, 8, 0,}, + /* 8852A USB SCC */ + .ple_qt25 = {1536, 0, 16, 48, 13, 13, 360, 0, 32, 40, 8, 0,}, + .ple_qt26 = {2654, 0, 1134, 48, 64, 13, 1478, 0, 64, 128, 120, 0,}, /* USB 52C USB3.0 */ .ple_qt42 = {1068, 0, 16, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, /* USB 52C USB3.0 */ diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index ce3474a7ba03..868413335f6c 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -924,6 +924,7 @@ struct rtw89_mac_size_set { const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c0; const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c2; const struct rtw89_dle_size wde_size0; + const struct rtw89_dle_size wde_size1; const struct rtw89_dle_size wde_size0_v1; const struct rtw89_dle_size wde_size4; const struct rtw89_dle_size wde_size4_v1; @@ -937,6 +938,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size wde_size25; const struct rtw89_dle_size wde_size31; const struct rtw89_dle_size ple_size0; + const struct rtw89_dle_size ple_size1; const struct rtw89_dle_size ple_size0_v1; const struct rtw89_dle_size ple_size3_v1; const struct rtw89_dle_size ple_size4; @@ -950,6 +952,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size ple_size33; const struct rtw89_dle_size ple_size34; const struct rtw89_wde_quota wde_qt0; + const struct rtw89_wde_quota wde_qt1; const struct rtw89_wde_quota wde_qt0_v1; const struct rtw89_wde_quota wde_qt4; const struct rtw89_wde_quota wde_qt6; @@ -967,6 +970,8 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt9; const struct rtw89_ple_quota ple_qt13; const struct rtw89_ple_quota ple_qt18; + const struct rtw89_ple_quota ple_qt25; + const struct rtw89_ple_quota ple_qt26; const struct rtw89_ple_quota ple_qt42; const struct rtw89_ple_quota ple_qt43; const struct rtw89_ple_quota ple_qt44; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index fa347dbebf9a..7855fd55b43e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -65,6 +65,19 @@ static const struct rtw89_dle_mem rtw8852a_dle_mem_pcie[] = { NULL}, }; +static const struct rtw89_dle_mem rtw8852a_dle_mem_usb[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size1, + &rtw89_mac_size.ple_size1, &rtw89_mac_size.wde_qt1, + &rtw89_mac_size.wde_qt1, &rtw89_mac_size.ple_qt25, + &rtw89_mac_size.ple_qt26}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size4, + &rtw89_mac_size.ple_size4, &rtw89_mac_size.wde_qt4, + &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, + &rtw89_mac_size.ple_qt13}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + static const struct rtw89_reg2_def rtw8852a_pmac_ht20_mcs7_tbl[] = { {0x44AC, 0x00000000}, {0x44B0, 0x00000000}, @@ -2225,7 +2238,10 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, NULL, NULL}, - .dle_mem = {rtw8852a_dle_mem_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8852a_dle_mem_pcie, + rtw8852a_dle_mem_usb, + rtw8852a_dle_mem_usb, + NULL}, .wde_qempty_acq_grpnum = 16, .wde_qempty_mgq_grpsel = 16, .rf_base_addr = {0xc000, 0xd000}, From c19b106609f398b3b1b14a89dc68ed0244420d08 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Fri, 7 Nov 2025 18:08:14 +0200 Subject: [PATCH 41/80] wifi: rtw89: Add rtw8852a_hfc_param_ini_usb "hfc" means "hci fc" which is "Host Control Interface Flow Control". These are some parameters needed for RTL8852AU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/d1032888-2a53-4c52-a8b9-6e00cd6758dc@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 7855fd55b43e..76b64fd5a639 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -48,6 +48,48 @@ static const struct rtw89_hfc_param_ini rtw8852a_hfc_param_ini_pcie[] = { [RTW89_QTA_INVALID] = {NULL}, }; +static const struct rtw89_hfc_ch_cfg rtw8852a_hfc_chcfg_usb[] = { + {22, 402, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {22, 402, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {22, 402, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {22, 402, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {22, 402, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {22, 402, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0} /* FWCMDQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8852a_hfc_pubcfg_usb = { + 512, /* Group 0 */ + 0, /* Group 1 */ + 512, /* Public Max */ + 104 /* WP threshold */ +}; + +static const struct rtw89_hfc_prec_cfg rtw8852a_hfc_preccfg_usb = { + 11, /* CH 0-11 pre-cost */ + 32, /* H2C pre-cost */ + 76, /* WP CH 0-7 pre-cost */ + 25, /* WP CH 8-11 pre-cost */ + 1, /* CH 0-11 full condition */ + 1, /* H2C full condition */ + 1, /* WP CH 0-7 full condition */ + 1, /* WP CH 8-11 full condition */ +}; + +static const struct rtw89_hfc_param_ini rtw8852a_hfc_param_ini_usb[] = { + [RTW89_QTA_SCC] = {rtw8852a_hfc_chcfg_usb, &rtw8852a_hfc_pubcfg_usb, + &rtw8852a_hfc_preccfg_usb, RTW89_HCIFC_STF}, + [RTW89_QTA_DLFW] = {NULL, NULL, + &rtw8852a_hfc_preccfg_usb, RTW89_HCIFC_STF}, + [RTW89_QTA_INVALID] = {NULL}, +}; + static const struct rtw89_dle_mem rtw8852a_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size0, &rtw89_mac_size.ple_size0, &rtw89_mac_size.wde_qt0, @@ -2237,7 +2279,9 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .max_amsdu_limit = 3500, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, NULL, NULL}, + .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, + rtw8852a_hfc_param_ini_usb, + NULL}, .dle_mem = {rtw8852a_dle_mem_pcie, rtw8852a_dle_mem_usb, rtw8852a_dle_mem_usb, From 1dfd11e70022a0117bdf3fd02d2b541c9c732845 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Fri, 7 Nov 2025 18:08:46 +0200 Subject: [PATCH 42/80] wifi: rtw89: 8852a: Accept USB devices and load their MAC address Make rtw8852a_read_efuse() accept USB devices and load the MAC address from the correct offset. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/97412e2c-ac98-403c-9056-9d9fe1ed9f28@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 76b64fd5a639..48205aa4a980 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -621,14 +621,6 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; -static void rtw8852ae_efuse_parsing(struct rtw89_efuse *efuse, - struct rtw8852a_efuse *map) -{ - ether_addr_copy(efuse->addr, map->e.mac_addr); - efuse->rfe_type = map->rfe_type; - efuse->xtal_cap = map->xtal_k; -} - static void rtw8852a_efuse_parsing_tssi(struct rtw89_dev *rtwdev, struct rtw8852a_efuse *map) { @@ -674,12 +666,18 @@ static int rtw8852a_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, switch (rtwdev->hci.type) { case RTW89_HCI_TYPE_PCIE: - rtw8852ae_efuse_parsing(efuse, map); + ether_addr_copy(efuse->addr, map->e.mac_addr); + break; + case RTW89_HCI_TYPE_USB: + ether_addr_copy(efuse->addr, map->u.mac_addr); break; default: return -ENOTSUPP; } + efuse->rfe_type = map->rfe_type; + efuse->xtal_cap = map->xtal_k; + rtw89_info(rtwdev, "chip rfe_type is %d\n", efuse->rfe_type); return 0; From 0029ccab53accee4919971547f4e38036ef62b2f Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Fri, 7 Nov 2025 18:09:22 +0200 Subject: [PATCH 43/80] wifi: rtw89: Add rtw8852au.c This is the entry point of the new rtw89_8852au module. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/9580e5be-2bd1-45f9-ab75-616b86e25694@gmail.com --- .../net/wireless/realtek/rtw89/rtw8852au.c | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852au.c diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852au.c b/drivers/net/wireless/realtek/rtw89/rtw8852au.c new file mode 100644 index 000000000000..ca782469c455 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8852au.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include +#include +#include "rtw8852a.h" +#include "reg.h" +#include "usb.h" + +static const struct rtw89_usb_info rtw8852a_usb_info = { + .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2, + .usb_wlan0_1 = R_AX_USB_WLAN0_1, + .hci_func_en = R_AX_HCI_FUNC_EN, + .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0, + .usb_endpoint_0 = R_AX_USB_ENDPOINT_0, + .usb_endpoint_2 = R_AX_USB_ENDPOINT_2, + .bulkout_id = { + [RTW89_DMA_ACH0] = 3, + [RTW89_DMA_ACH2] = 5, + [RTW89_DMA_ACH4] = 4, + [RTW89_DMA_ACH6] = 6, + [RTW89_DMA_B0MG] = 0, + [RTW89_DMA_B0HI] = 0, + [RTW89_DMA_B1MG] = 1, + [RTW89_DMA_B1HI] = 1, + [RTW89_DMA_H2C] = 2, + }, +}; + +static const struct rtw89_driver_info rtw89_8852au_info = { + .chip = &rtw8852a_chip_info, + .variant = NULL, + .quirks = NULL, + .bus = { + .usb = &rtw8852a_usb_info, + } +}; + +static const struct usb_device_id rtw_8852au_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x0312, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4020, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1997, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x8832, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x885a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x885c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3321, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x332c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x013f, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0140, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0141, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010f, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852au_info }, + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8852au_id_table); + +static struct usb_driver rtw_8852au_driver = { + .name = KBUILD_MODNAME, + .id_table = rtw_8852au_id_table, + .probe = rtw89_usb_probe, + .disconnect = rtw89_usb_disconnect, +}; +module_usb_driver(rtw_8852au_driver); + +MODULE_AUTHOR("Bitterblue Smith "); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852AU driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 623c177323ec16c2c72b4ea0ad68bda223928488 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Fri, 7 Nov 2025 18:10:19 +0200 Subject: [PATCH 44/80] wifi: rtw89: Enable the new rtw89_8852au module Tested in station mode and a little in AP mode. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/180a5e77-9297-4ffc-80d5-191dfef47661@gmail.com --- drivers/net/wireless/realtek/rtw89/Kconfig | 11 +++++++++++ drivers/net/wireless/realtek/rtw89/Makefile | 3 +++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index e89f7481475a..44d8a7f32bf2 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -74,6 +74,17 @@ config RTW89_8852AE 802.11ax PCIe wireless network (Wi-Fi 6) adapter +config RTW89_8852AU + tristate "Realtek 8852AU USB wireless network (Wi-Fi 6) adapter" + depends on USB + select RTW89_CORE + select RTW89_USB + select RTW89_8852A + help + Select this option will enable support for 8852AU chipset + + 802.11ax USB wireless network (Wi-Fi 6) adapter + config RTW89_8852BE tristate "Realtek 8852BE PCI wireless network (Wi-Fi 6) adapter" depends on PCI diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index e0d21972e57c..1be81f254fca 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -43,6 +43,9 @@ rtw89_8852a-objs := rtw8852a.o \ obj-$(CONFIG_RTW89_8852AE) += rtw89_8852ae.o rtw89_8852ae-objs := rtw8852ae.o +obj-$(CONFIG_RTW89_8852AU) += rtw89_8852au.o +rtw89_8852au-objs := rtw8852au.o + obj-$(CONFIG_RTW89_8852B_COMMON) += rtw89_8852b_common.o rtw89_8852b_common-objs := rtw8852b_common.o From 12e6bea1aac85cc98a55031ebf10db969eeafa34 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 6 Nov 2025 08:49:32 +0800 Subject: [PATCH 45/80] wifi: rtlwifi: rtl8188ee: correct allstasleep in P2P PS H2C command The "->allstasleep" variable is a 1 bit bitfield. It can only be 0 or 1. This "= -1" assignement should be "= 0" as other chips and vendor driver. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-wireless/5c992936-4e7b-4c0a-abfc-0ec0fb9ef9fd@suswa.mountain/T/#t Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/1762390172-21091-1-git-send-email-pkshih@realtek.com --- drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c index 7252bc621211..9a9f9e14f472 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c @@ -694,7 +694,7 @@ void rtl88e_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) { p2p_ps_offload->role = 1; - p2p_ps_offload->allstasleep = -1; + p2p_ps_offload->allstasleep = 0; } else { p2p_ps_offload->role = 0; } From dae8d7d63b740d8f5972b8438b139a6488e0f9fa Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 11 Nov 2025 10:24:45 +0800 Subject: [PATCH 46/80] wifi: rtw89: pci: add to read PCI configuration space from common code Normally only access PCI device in pci.c. However for debug purpose, a set of registers predefined in firmware element including PCI configuration space should be read for diagnosis. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251111022452.28093-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 13 +++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 2 -- drivers/net/wireless/realtek/rtw89/pci.c | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 71eb7078a0dc..e6584462f6c0 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -39,6 +39,8 @@ extern const struct ieee80211_ops rtw89_ops; #define RFREG_MASK 0xfffff #define INV_RF_DATA 0xffffffff #define BYPASS_CR_DATA 0xbabecafe +#define RTW89_R32_EA 0xEAEAEAEA +#define RTW89_R32_DEAD 0xDEADBEEF #define RTW89_TRACK_WORK_PERIOD round_jiffies_relative(HZ * 2) #define RTW89_TRACK_PS_WORK_PERIOD msecs_to_jiffies(100) @@ -3674,6 +3676,8 @@ struct rtw89_hci_ops { void (*write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data); void (*write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data); + u32 (*read32_pci_cfg)(struct rtw89_dev *rtwdev, u32 addr); + int (*mac_pre_init)(struct rtw89_dev *rtwdev); int (*mac_pre_deinit)(struct rtw89_dev *rtwdev); int (*mac_post_init)(struct rtw89_dev *rtwdev); @@ -6658,6 +6662,15 @@ rtw89_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, mutex_unlock(&rtwdev->rf_mutex); } +static inline u32 rtw89_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr) +{ + if (rtwdev->hci.type != RTW89_HCI_TYPE_PCIE || + !rtwdev->hci.ops->read32_pci_cfg) + return RTW89_R32_EA; + + return rtwdev->hci.ops->read32_pci_cfg(rtwdev, addr); +} + static inline struct ieee80211_txq *rtw89_txq_to_txq(struct rtw89_txq *rtwtxq) { void *p = rtwtxq; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 868413335f6c..3cc97fd0c0ec 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -581,8 +581,6 @@ enum rtw89_mac_bf_rrsc_rate { RTW89_MAC_BF_RRSC_MAX = 32 }; -#define RTW89_R32_EA 0xEAEAEAEA -#define RTW89_R32_DEAD 0xDEADBEEF #define MAC_REG_POOL_COUNT 10 #define ACCESS_CMAC(_addr) \ ({typeof(_addr) __addr = (_addr); \ diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index b1985193a18f..a66fcdb0293b 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -2064,6 +2064,20 @@ static void rtw89_pci_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) writel(data, rtwpci->mmap + addr); } +static u32 rtw89_pci_ops_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr) +{ + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + struct pci_dev *pdev = rtwpci->pdev; + u32 value; + int ret; + + ret = pci_read_config_dword(pdev, addr, &value); + if (ret) + return RTW89_R32_EA; + + return value; +} + static void rtw89_pci_ctrl_dma_trx(struct rtw89_dev *rtwdev, bool enable) { const struct rtw89_pci_info *info = rtwdev->pci_info; @@ -4683,6 +4697,8 @@ static const struct rtw89_hci_ops rtw89_pci_ops = { .write16 = rtw89_pci_ops_write16, .write32 = rtw89_pci_ops_write32, + .read32_pci_cfg = rtw89_pci_ops_read32_pci_cfg, + .mac_pre_init = rtw89_pci_ops_mac_pre_init, .mac_pre_deinit = rtw89_pci_ops_mac_pre_deinit, .mac_post_init = rtw89_pci_ops_mac_post_init, From de19cc7def5a9c646264a1e7a2a183a3baad112f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 11 Nov 2025 10:24:46 +0800 Subject: [PATCH 47/80] wifi: rtw89: fw: parse firmware element of DIAG_MAC The firmware element ID 28 is a set of rules to diagnose if MAC get abnormal. The latter patch will use these rules via debugfs to know the status. The element contains rules with their textual messages shown as below: +------------------------------------+ | | | +-----------+ | | | rule_size |-------|----------+ +----------------+-----------+-------+ -- | | rule[0] | \ | | rule[1] | | <---+ | : | / +------------------------------------+ -- | msg[0] msg[1] | each msg has variable length | msg[2] msg[3] ... | (with address align 2) | ... | +------------------------------------+ Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251111022452.28093-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/fw.c | 15 +++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index e6584462f6c0..34c135269627 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4728,6 +4728,7 @@ struct rtw89_fw_elm_info { struct rtw89_phy_rfk_log_fmt *rfk_log_fmt; const struct rtw89_regd_data *regd; const struct rtw89_fw_element_hdr *afe; + const struct rtw89_fw_element_hdr *diag_mac; }; enum rtw89_fw_mss_dev_type { diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 96f0463e66d6..515e9f098380 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1298,6 +1298,18 @@ int rtw89_build_afe_pwr_seq_from_elm(struct rtw89_dev *rtwdev, return 0; } +static +int rtw89_recognize_diag_mac_from_elm(struct rtw89_dev *rtwdev, + const struct rtw89_fw_element_hdr *elm, + const union rtw89_fw_element_arg arg) +{ + struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + + elm_info->diag_mac = elm; + + return 0; +} + static const struct rtw89_fw_element_handler __fw_element_handlers[] = { [RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm, { .fw_type = RTW89_FW_BBMCU0 }, NULL}, @@ -1386,6 +1398,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = { [RTW89_FW_ELEMENT_ID_AFE_PWR_SEQ] = { rtw89_build_afe_pwr_seq_from_elm, {}, "AFE", }, + [RTW89_FW_ELEMENT_ID_DIAG_MAC] = { + rtw89_recognize_diag_mac_from_elm, {}, NULL, + }, }; int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 310580206368..6620256e3f22 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4026,6 +4026,7 @@ enum rtw89_fw_element_id { RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ = 25, RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ = 26, RTW89_FW_ELEMENT_ID_AFE_PWR_SEQ = 27, + RTW89_FW_ELEMENT_ID_DIAG_MAC = 28, RTW89_FW_ELEMENT_ID_NUM, }; @@ -4203,6 +4204,11 @@ struct rtw89_fw_element_hdr { __le32 val; } __packed infos[]; } __packed afe; + struct { + __le32 rule_size; + u8 rsvd[4]; + u8 rules_and_msgs[]; + } __packed diag_mac; struct __rtw89_fw_txpwr_element txpwr; struct __rtw89_fw_regd_element regd; } __packed u; From 7bf433c6767ffe2ad5b7ac8680c6e93e7d0be3e4 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 11 Nov 2025 10:24:47 +0800 Subject: [PATCH 48/80] wifi: rtw89: debug: add parser to diagnose along DIAG_MAC fw element The rules to diagnose MAC have a common header, and a cmd field is used to know the exact command and its format. The rules with the same tuple of fields {sheet, seq} can be seen as a set of compound rules, which treat it as positive rule if just one of the rules is positive. Take EQUALV rules as example, if value of {addr, mask} is equal to predefined value as field val, a rule is positive. Fields addr_name_offset and msg_offset are offsets related to textual messages for human readable. Format of common rule header (8 bytes) +-------+-----+--------+-----------+-----+---------+ | sheet | cmd | seq[2] | io / band | len | rsvd[2] | +-------+-----+--------+-----------+-----+---------+ Format of rule command is EQUALV (equal value) (24 bytes): +------+------------------+------+-----+------------+---------+ | addr | addr_name_offset | mask | val | msg_offset | rsvd[4] | +------+------------------+------+-----+------------+---------+ Format of message: +-----+----------+ | len | string[] | +-----+----------+ An example of output: Plain(Ignore)/Rules/Positive: 115(4)/86/81 Where, Plain is total rules written in firmware element. Ignore is the ignored rules, such as USB IO, but current is PCIE. Rules is number of set of compound rules. Positive is number of positive Rules. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251111022452.28093-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 299 +++++++++++++++++++++ 1 file changed, 299 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 3dc7981c510f..1264c2f82600 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -87,6 +87,7 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv disable_dm; struct rtw89_debugfs_priv mlo_mode; struct rtw89_debugfs_priv beacon_info; + struct rtw89_debugfs_priv diag_mac; }; struct rtw89_debugfs_iter_data { @@ -4361,6 +4362,302 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev, return count; } +enum __diag_mac_cmd { + __CMD_EQUALV, + __CMD_EQUALO, + __CMD_NEQUALV, + __CMD_NEQUALO, + __CMD_SETEQUALV, + __CMD_SETEQUALO, + __CMD_CMPWCR, + __CMD_CMPWWD, + __CMD_NEQ_CMPWCR, + __CMD_NEQ_CMPWWD, + __CMD_INCREMENT, + __CMD_MESSAGE, +}; + +enum __diag_mac_io { + __IO_NORMAL, + __IO_NORMAL_PCIE, + __IO_NORMAL_USB, + __IO_NORMAL_SDIO, + __IO_PCIE_CFG, + __IO_SDIO_CCCR, +}; + +struct __diag_mac_rule_header { + u8 sheet; + u8 cmd; + u8 seq_major; + u8 seq_minor; + u8 io_band; + #define __DIAG_MAC_IO GENMASK(3, 0) + #define __DIAG_MAC_N_BAND BIT(4) + #define __DIAG_MAC_HAS_BAND BIT(5) + u8 len; /* include header. Unit: 4 bytes */ + u8 rsvd[2]; +} __packed; + +struct __diag_mac_rule_equal { + struct __diag_mac_rule_header header; + __le32 addr; + __le32 addr_name_offset; + __le32 mask; + __le32 val; + __le32 msg_offset; + u8 rsvd[4]; +} __packed; + +struct __diag_mac_rule_increment { + struct __diag_mac_rule_header header; + __le32 addr; + __le32 addr_name_offset; + __le32 mask; + __le16 sel; + __le16 delay; + __le32 msg_offset; + u8 rsvd[4]; +} __packed; + +struct __diag_mac_msg_buf { + __le16 len; + char string[]; +} __packed; + +static ssize_t rtw89_mac_diag_do_equalv(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + const struct __diag_mac_rule_equal *r, + const void *msg_start, + u64 *positive_bmp) +{ + const struct __diag_mac_msg_buf *name = msg_start + + le32_to_cpu(r->addr_name_offset); + const struct __diag_mac_msg_buf *msg = msg_start + + le32_to_cpu(r->msg_offset); + bool want_eq = r->header.cmd == __CMD_EQUALV; + char *p = buf, *end = buf + bufsz; + bool equal = false; + u32 val; + + *positive_bmp <<= 1; + + if (u8_get_bits(r->header.io_band, __DIAG_MAC_IO) == __IO_PCIE_CFG) + val = rtw89_read32_pci_cfg(rtwdev, le32_to_cpu(r->addr)); + else + val = rtw89_read32(rtwdev, le32_to_cpu(r->addr)); + + if ((val & le32_to_cpu(r->mask)) == le32_to_cpu(r->val)) + equal = true; + + if (want_eq == equal) { + *positive_bmp |= BIT(0); + return p - buf; + } + + p += scnprintf(p, end - p, "sheet: %d, cmd: %d, Reg: %.*s => %x, %.*s\n", + r->header.sheet, r->header.cmd, le16_to_cpu(name->len), + name->string, val, le16_to_cpu(msg->len), msg->string); + + return p - buf; +} + +static ssize_t rtw89_mac_diag_do_increment(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + const struct __diag_mac_rule_increment *r, + const void *msg_start, + u64 *positive_bmp) +{ + const struct __diag_mac_msg_buf *name = msg_start + + le32_to_cpu(r->addr_name_offset); + const struct __diag_mac_msg_buf *msg = msg_start + + le32_to_cpu(r->msg_offset); + char *p = buf, *end = buf + bufsz; + u32 addr = le32_to_cpu(r->addr); + u32 mask = le32_to_cpu(r->mask); + u16 sel = le16_to_cpu(r->sel); + u32 val1, val2; + + *positive_bmp <<= 1; + + rtw89_write32(rtwdev, addr, sel); + + if (u8_get_bits(r->header.io_band, __DIAG_MAC_IO) == __IO_PCIE_CFG) + val1 = rtw89_read32_pci_cfg(rtwdev, addr); + else + val1 = rtw89_read32(rtwdev, addr); + + mdelay(le16_to_cpu(r->delay)); + + if (u8_get_bits(r->header.io_band, __DIAG_MAC_IO) == __IO_PCIE_CFG) + val2 = rtw89_read32_pci_cfg(rtwdev, addr); + else + val2 = rtw89_read32(rtwdev, addr); + + if ((val2 & mask) > (val1 & mask)) { + *positive_bmp |= BIT(0); + return p - buf; + } + + p += scnprintf(p, end - p, "sheet: %d, cmd: %d, Reg: %.*s [%d]=> %x, %.*s\n", + r->header.sheet, r->header.cmd, le16_to_cpu(name->len), + name->string, le16_to_cpu(r->sel), val1, + le16_to_cpu(msg->len), msg->string); + + return p - buf; +} + +static bool rtw89_mac_diag_match_hci(struct rtw89_dev *rtwdev, + const struct __diag_mac_rule_header *rh) +{ + switch (u8_get_bits(rh->io_band, __DIAG_MAC_IO)) { + case __IO_NORMAL: + default: + return true; + case __IO_NORMAL_PCIE: + case __IO_PCIE_CFG: + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + return true; + break; + case __IO_NORMAL_USB: + if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + return true; + break; + case __IO_NORMAL_SDIO: + case __IO_SDIO_CCCR: + if (rtwdev->hci.type == RTW89_HCI_TYPE_SDIO) + return true; + break; + } + + return false; +} + +static bool rtw89_mac_diag_match_band(struct rtw89_dev *rtwdev, + const struct __diag_mac_rule_header *rh) +{ + u8 active_bands; + bool has_band; + u8 band; + + has_band = u8_get_bits(rh->io_band, __DIAG_MAC_HAS_BAND); + if (!has_band) + return true; + + band = u8_get_bits(rh->io_band, __DIAG_MAC_N_BAND); + active_bands = rtw89_get_active_phy_bitmap(rtwdev); + + if (active_bands & BIT(band)) + return true; + + return false; +} + +static ssize_t rtw89_mac_diag_iter_all(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz) +{ + const struct rtw89_fw_element_hdr *elm = rtwdev->fw.elm_info.diag_mac; + u32 n_plains = 0, n_rules = 0, n_positive = 0, n_ignore = 0; + char *p = buf, *end = buf + bufsz, *p_rewind; + const void *rule, *rule_end; + u32 elm_size, rule_size; + const void *msg_start; + u64 positive_bmp = 0; + u8 prev_sheet = 0; + u8 prev_seq = 0; + int limit; + + if (!elm) { + p += scnprintf(p, end - p, "No diag_mac entry\n"); + goto out; + } + + rule_size = le32_to_cpu(elm->u.diag_mac.rule_size); + elm_size = le32_to_cpu(elm->size); + + if (ALIGN(rule_size, 16) > elm_size) { + p += scnprintf(p, end - p, "rule size (%u) exceed elm_size (%u)\n", + ALIGN(rule_size, 16), elm_size); + goto out; + } + + rule = &elm->u.diag_mac.rules_and_msgs[0]; + rule_end = &elm->u.diag_mac.rules_and_msgs[rule_size]; + msg_start = &elm->u.diag_mac.rules_and_msgs[ALIGN(rule_size, 16)]; + + for (limit = 0; limit < 5000 && rule < rule_end; limit++) { + const struct __diag_mac_rule_header *rh = rule; + u8 sheet = rh->sheet; + u8 seq = rh->seq_major; + + if (!rtw89_mac_diag_match_hci(rtwdev, rh) || + !rtw89_mac_diag_match_band(rtwdev, rh)) { + n_ignore++; + goto next; + } + + if (!seq || prev_sheet != sheet || prev_seq != seq) { + if (positive_bmp) { + n_positive++; + /* + * discard output for negative results if one in + * a sequence set is positive. + */ + if (p_rewind) + p = p_rewind; + } + p_rewind = seq ? p : NULL; + positive_bmp = 0; + n_rules++; + } + + switch (rh->cmd) { + case __CMD_EQUALV: + case __CMD_NEQUALV: + p += rtw89_mac_diag_do_equalv(rtwdev, p, end - p, rule, + msg_start, &positive_bmp); + break; + case __CMD_INCREMENT: + p += rtw89_mac_diag_do_increment(rtwdev, p, end - p, rule, + msg_start, &positive_bmp); + break; + default: + p += scnprintf(p, end - p, "unknown rule cmd %u\n", rh->cmd); + break; + } + +next: + n_plains++; + rule += rh->len * 4; + prev_seq = seq; + prev_sheet = sheet; + } + + if (positive_bmp) { + n_positive++; + if (p_rewind) + p = p_rewind; + } + + p += scnprintf(p, end - p, "\nPlain(Ignore)/Rules/Positive: %u(%u)/%u/%u\n", + n_plains, n_ignore, n_rules, n_positive); + +out: + return p - buf; +} + +static ssize_t +rtw89_debug_priv_diag_mac_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + rtw89_leave_lps(rtwdev); + + return rtw89_mac_diag_iter_all(rtwdev, buf, bufsz); +} + static ssize_t rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, struct rtw89_debugfs_priv *debugfs_priv, @@ -4478,6 +4775,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK), .mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK), .beacon_info = rtw89_debug_priv_get(beacon_info), + .diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK), }; #define rtw89_debugfs_add(name, mode, fopname, parent) \ @@ -4524,6 +4822,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_rw(disable_dm); rtw89_debugfs_add_rw(mlo_mode); rtw89_debugfs_add_r(beacon_info); + rtw89_debugfs_add_r(diag_mac); } void rtw89_debugfs_init(struct rtw89_dev *rtwdev) From b37fb77d4530f711a08b6dd860a8a4839897aba2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 11 Nov 2025 10:24:48 +0800 Subject: [PATCH 49/80] wifi: rtw89: 8852c: add compensation of thermal value from efuse calibration The 3th bit of thermal value programmed in efuse means 8 (2 ^ 3) grams. Check the bit and signed bit, and add compensation to final thermal. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251111022452.28093-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 8550ae3bcb64..6be56c2fa0c7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -670,12 +670,16 @@ static void rtw8852c_phycap_parsing_thermal_trim(struct rtw89_dev *rtwdev, } } +#define __THM_MASK_SIGN BIT(0) +#define __THM_MASK_3BITS GENMASK(3, 1) +#define __THM_MASK_VAL8 BIT(4) + static void rtw8852c_thermal_trim(struct rtw89_dev *rtwdev) { -#define __thm_setting(raw) \ -({ \ - u8 __v = (raw); \ - ((__v & 0x1) << 3) | ((__v & 0x1f) >> 1); \ +#define __thm_setting(raw) \ +({ \ + u8 __v = (raw); \ + ((__v & __THM_MASK_SIGN) << 3) | ((__v & __THM_MASK_3BITS) >> 1); \ }) struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; u8 i, val; @@ -2516,13 +2520,26 @@ static void rtw8852c_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) static u8 rtw8852c_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path) { + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + s8 comp = 0; + u8 val; + rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x1); rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x0); rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x1); fsleep(200); - return rtw89_read_rf(rtwdev, rf_path, RR_TM, RR_TM_VAL); + val = rtw89_read_rf(rtwdev, rf_path, RR_TM, RR_TM_VAL); + + if (info->pg_thermal_trim) { + u8 trim = info->thermal_trim[rf_path]; + + if (trim & __THM_MASK_VAL8) + comp = 8 * (trim & __THM_MASK_SIGN ? -1 : 1); + } + + return val + comp; } static void rtw8852c_btc_set_rfe(struct rtw89_dev *rtwdev) From 1dd7e743dd37361c8c1ac89355a8da738e2b81fb Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Tue, 11 Nov 2025 10:24:49 +0800 Subject: [PATCH 50/80] wifi: rtw89: phy: fix out-of-bounds access in rtw89_phy_read_txpwr_limit() Coverity reported a potential out-of-bounds access when 'bw' exceeds the valid range for the specified band. Add a helper `rtw89_bw_is_valid()` to check bandwidth validity for each band before accessing limit tables. Addresses-Coverity-ID: 1598844 ("Out-of-bounds access") Addresses-Coverity-ID: 1598896 ("Out-of-bounds access") Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251111022452.28093-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 23892c1359a5..28e2b15240a7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2376,6 +2376,21 @@ static u8 rtw89_channel_to_idx(struct rtw89_dev *rtwdev, u8 band, u8 channel) } } +static bool rtw89_phy_validate_txpwr_limit_bw(struct rtw89_dev *rtwdev, + u8 band, u8 bw) +{ + switch (band) { + case RTW89_BAND_2G: + return bw < RTW89_2G_BW_NUM; + case RTW89_BAND_5G: + return bw < RTW89_5G_BW_NUM; + case RTW89_BAND_6G: + return bw < RTW89_6G_BW_NUM; + default: + return false; + } +} + s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, u8 bw, u8 ntx, u8 rs, u8 bf, u8 ch) { @@ -2400,6 +2415,11 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, }; s8 cstr; + if (!rtw89_phy_validate_txpwr_limit_bw(rtwdev, band, bw)) { + rtw89_warn(rtwdev, "invalid band %u bandwidth %u\n", band, bw); + return 0; + } + switch (band) { case RTW89_BAND_2G: if (has_ant_gain) From e96abe4fd7cd6f48434d8846aa7588bf7bdb3f0e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 11 Nov 2025 10:24:50 +0800 Subject: [PATCH 51/80] wifi: rtw89: consider data rate/bandwidth/GI for injected packets To send injected packets with configurable rate/bandwidth/GI, fill TXWD fields according to SKB's info->control.rates[0] annotated by mac80211. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251111022452.28093-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 67 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 2 + 2 files changed, 69 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 1d30ff27bce1..a5c451eed5a7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -321,6 +321,26 @@ static const struct ieee80211_supported_band rtw89_sband_6ghz = { .n_bitrates = ARRAY_SIZE(rtw89_bitrates) - 4, }; +static const struct rtw89_hw_rate_def { + enum rtw89_hw_rate ht; + enum rtw89_hw_rate vht[RTW89_NSS_NUM]; +} rtw89_hw_rate[RTW89_CHIP_GEN_NUM] = { + [RTW89_CHIP_AX] = { + .ht = RTW89_HW_RATE_MCS0, + .vht = {RTW89_HW_RATE_VHT_NSS1_MCS0, + RTW89_HW_RATE_VHT_NSS2_MCS0, + RTW89_HW_RATE_VHT_NSS3_MCS0, + RTW89_HW_RATE_VHT_NSS4_MCS0}, + }, + [RTW89_CHIP_BE] = { + .ht = RTW89_HW_RATE_V1_MCS0, + .vht = {RTW89_HW_RATE_V1_VHT_NSS1_MCS0, + RTW89_HW_RATE_V1_VHT_NSS2_MCS0, + RTW89_HW_RATE_V1_VHT_NSS3_MCS0, + RTW89_HW_RATE_V1_VHT_NSS4_MCS0}, + }, +}; + static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats, struct sk_buff *skb, bool tx) { @@ -1097,6 +1117,44 @@ rtw89_core_tx_wake(struct rtw89_dev *rtwdev, rtw89_mac_notify_wake(rtwdev); } +static void rtw89_core_tx_update_injection(struct rtw89_dev *rtwdev, + struct rtw89_core_tx_request *tx_req, + struct ieee80211_tx_info *info) +{ + const struct rtw89_hw_rate_def *hw_rate = &rtw89_hw_rate[rtwdev->chip->chip_gen]; + enum mac80211_rate_control_flags flags = info->control.rates[0].flags; + struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; + const struct rtw89_chan *chan; + u8 idx = info->control.rates[0].idx; + u8 nss, mcs; + + desc_info->use_rate = true; + desc_info->dis_data_fb = true; + + if (flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + desc_info->data_bw = 3; + else if (flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + desc_info->data_bw = 2; + else if (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + desc_info->data_bw = 1; + + if (flags & IEEE80211_TX_RC_SHORT_GI) + desc_info->gi_ltf = 1; + + if (flags & IEEE80211_TX_RC_VHT_MCS) { + nss = umin(idx >> 4, ARRAY_SIZE(hw_rate->vht) - 1); + mcs = idx & 0xf; + desc_info->data_rate = hw_rate->vht[nss] + mcs; + } else if (flags & IEEE80211_TX_RC_MCS) { + desc_info->data_rate = hw_rate->ht + idx; + } else { + chan = rtw89_chan_get(rtwdev, tx_req->rtwvif_link->chanctx_idx); + + desc_info->data_rate = idx + (chan->band_type == RTW89_BAND_2G ? + RTW89_HW_RATE_CCK1 : RTW89_HW_RATE_OFDM6); + } +} + static void rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) @@ -1157,6 +1215,9 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, default: break; } + + if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) + rtw89_core_tx_update_injection(rtwdev, tx_req, info); } static void rtw89_tx_wait_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -1385,6 +1446,8 @@ static __le32 rtw89_build_txwd_body5(struct rtw89_tx_desc_info *desc_info) static __le32 rtw89_build_txwd_body7_v1(struct rtw89_tx_desc_info *desc_info) { u32 dword = FIELD_PREP(RTW89_TXWD_BODY7_USE_RATE_V1, desc_info->use_rate) | + FIELD_PREP(RTW89_TXWD_BODY7_DATA_BW, desc_info->data_bw) | + FIELD_PREP(RTW89_TXWD_BODY7_GI_LTF, desc_info->gi_ltf) | FIELD_PREP(RTW89_TXWD_BODY7_DATA_RATE, desc_info->data_rate); return cpu_to_le32(dword); @@ -1393,6 +1456,8 @@ static __le32 rtw89_build_txwd_body7_v1(struct rtw89_tx_desc_info *desc_info) static __le32 rtw89_build_txwd_info0(struct rtw89_tx_desc_info *desc_info) { u32 dword = FIELD_PREP(RTW89_TXWD_INFO0_USE_RATE, desc_info->use_rate) | + FIELD_PREP(RTW89_TXWD_INFO0_DATA_BW, desc_info->data_bw) | + FIELD_PREP(RTW89_TXWD_INFO0_GI_LTF, desc_info->gi_ltf) | FIELD_PREP(RTW89_TXWD_INFO0_DATA_RATE, desc_info->data_rate) | FIELD_PREP(RTW89_TXWD_INFO0_DATA_STBC, desc_info->stbc) | FIELD_PREP(RTW89_TXWD_INFO0_DATA_LDPC, desc_info->ldpc) | @@ -1585,6 +1650,8 @@ static __le32 rtw89_build_txwd_body6_v2(struct rtw89_tx_desc_info *desc_info) static __le32 rtw89_build_txwd_body7_v2(struct rtw89_tx_desc_info *desc_info) { u32 dword = FIELD_PREP(BE_TXD_BODY7_USERATE_SEL, desc_info->use_rate) | + FIELD_PREP(BE_TXD_BODY7_DATA_BW, desc_info->data_bw) | + FIELD_PREP(BE_TXD_BODY7_GI_LTF, desc_info->gi_ltf) | FIELD_PREP(BE_TXD_BODY7_DATA_ER, desc_info->er_cap) | FIELD_PREP(BE_TXD_BODY7_DATA_BW_ER, 0) | FIELD_PREP(BE_TXD_BODY7_DATARATE, desc_info->data_rate); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 34c135269627..4da73153513a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1182,6 +1182,8 @@ struct rtw89_tx_desc_info { u8 sec_seq[6]; u16 data_rate; u16 data_retry_lowest_rate; + u8 data_bw; + u8 gi_ltf; bool fw_dl; u16 seq; bool a_ctrl_bsr; From d9204e99b69623997cba0d7472fa7df644df7cb5 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 11 Nov 2025 10:24:51 +0800 Subject: [PATCH 52/80] wifi: rtw89: do RF calibration once setting channel when running pure monitor mode To be able to capture and inject packets in monitor mode, do RF calibration once setting certain channel. Since calibration costs time, do not change behavior of normal usage, which do calibration only when starting as AP or going to connect AP. Since driver declares IEEE80211_HW_WANT_MONITOR_VIF, the pure monitor vif must be only one when adding interface with type NL80211_IFTYPE_MONITOR. Otherwise, monitor vif must be NULL. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251111022452.28093-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 18 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 4 ++++ 3 files changed, 23 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a5c451eed5a7..a15a911484bb 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -470,6 +470,22 @@ void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev) __rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_1); } +static void rtw89_chip_rfk_channel_for_pure_mon_vif(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_vif *rtwvif = rtwdev->pure_monitor_mode_vif; + struct rtw89_vif_link *rtwvif_link; + + if (!rtwvif) + return; + + rtwvif_link = rtw89_vif_get_link_inst(rtwvif, phy_idx); + if (!rtwvif_link) + return; + + rtw89_chip_rfk_channel(rtwdev, rtwvif_link); +} + static void __rtw89_set_channel(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_mac_idx mac_idx, @@ -498,6 +514,8 @@ static void __rtw89_set_channel(struct rtw89_dev *rtwdev, } rtw89_set_entity_state(rtwdev, phy_idx, true); + + rtw89_chip_rfk_channel_for_pure_mon_vif(rtwdev, phy_idx); } int rtw89_set_channel(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4da73153513a..f63857b389d7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6125,6 +6125,7 @@ struct rtw89_dev { int napi_budget_countdown; struct rtw89_debugfs *debugfs; + struct rtw89_vif *pure_monitor_mode_vif; /* HCI related data, keep last */ u8 priv[] __aligned(sizeof(void *)); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index a19304ff8306..14c8864bd201 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -220,6 +220,8 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, if (ret) goto unset_link; + rtwdev->pure_monitor_mode_vif = vif->type == NL80211_IFTYPE_MONITOR ? + rtwvif : NULL; rtw89_recalc_lps(rtwdev); return 0; @@ -267,6 +269,8 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw, rtw89_core_release_bit_map(rtwdev->hw_port, port); rtw89_release_mac_id(rtwdev, macid); + rtwdev->pure_monitor_mode_vif = NULL; + rtw89_recalc_lps(rtwdev); rtw89_enter_ips_by_hwflags(rtwdev); } From 7465c0a91278b1829f7a3e5e2459e3329edb4ae4 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 11 Nov 2025 10:24:52 +0800 Subject: [PATCH 53/80] wifi: rtw89: configure RX antenna if chips can support If chip->ops->cfg_txrx_path is implemented, a chip can support to configure RX antenna, so accept setting via iw tool. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251111022452.28093-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac80211.c | 5 +++++ .../net/wireless/realtek/rtw89/rtw8852b_common.c | 6 +++++- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 14 ++++++++++++-- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 12 +++++++++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 14c8864bd201..6f7571c7c274 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -1147,12 +1147,17 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant, u3 { struct rtw89_dev *rtwdev = hw->priv; struct rtw89_hal *hal = &rtwdev->hal; + const struct rtw89_chip_info *chip; lockdep_assert_wiphy(hw->wiphy); + chip = rtwdev->chip; + if (hal->ant_diversity) { if (tx_ant != rx_ant || hweight32(tx_ant) != 1) return -EINVAL; + } else if (chip->ops->cfg_txrx_path) { + /* With cfg_txrx_path ops, chips can configure rx_ant */ } else if (rx_ant != hw->wiphy->available_antennas_rx && rx_ant != hal->antenna_rx) { return -EINVAL; } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c index 3fb2972ae6f6..4e72f4961837 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c @@ -1747,11 +1747,15 @@ static void __rtw8852bx_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) struct rtw89_hal *hal = &rtwdev->hal; const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); enum rtw89_rf_path_bit rx_path = hal->antenna_rx ? hal->antenna_rx : RF_AB; + u8 rx_nss = rtwdev->hal.rx_nss; + + if (rx_path != RF_AB) + rx_nss = 1; rtw8852bx_bb_ctrl_rx_path(rtwdev, rx_path, chan); rtw8852bx_bb_ctrl_rf_mode_rx_path(rtwdev, rx_path); - if (rtwdev->hal.rx_nss == 1) { + if (rx_nss == 1) { rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 0); rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 0); rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 0); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 6be56c2fa0c7..ee81a6792eee 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2502,10 +2502,20 @@ static void rtw8852c_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, static void rtw8852c_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) { struct rtw89_hal *hal = &rtwdev->hal; + u8 nrx_path = RF_PATH_AB; + u8 rx_nss = hal->rx_nss; - rtw8852c_bb_cfg_rx_path(rtwdev, RF_PATH_AB); + if (hal->antenna_rx == RF_A) + nrx_path = RF_PATH_A; + else if (hal->antenna_rx == RF_B) + nrx_path = RF_PATH_B; - if (hal->rx_nss == 1) { + if (nrx_path != RF_PATH_AB) + rx_nss = 1; + + rtw8852c_bb_cfg_rx_path(rtwdev, nrx_path); + + if (rx_nss == 1) { rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 0); rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 0); rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 0); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 61f3a0a3f440..11e8b50a3291 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2347,19 +2347,29 @@ static void rtw8922a_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) enum rtw89_band band = chan->band_type; struct rtw89_hal *hal = &rtwdev->hal; u8 ntx_path = RF_PATH_AB; + u8 nrx_path = RF_PATH_AB; u32 tx_en0, tx_en1; + u8 rx_nss = 2; if (hal->antenna_tx == RF_A) ntx_path = RF_PATH_A; else if (hal->antenna_tx == RF_B) ntx_path = RF_PATH_B; + if (hal->antenna_rx == RF_A) + nrx_path = RF_PATH_A; + else if (hal->antenna_rx == RF_B) + nrx_path = RF_PATH_B; + + if (nrx_path != RF_PATH_AB) + rx_nss = 1; + rtw8922a_hal_reset(rtwdev, RTW89_PHY_0, RTW89_MAC_0, band, &tx_en0, true); if (rtwdev->dbcc_en) rtw8922a_hal_reset(rtwdev, RTW89_PHY_1, RTW89_MAC_1, band, &tx_en1, true); - rtw8922a_ctrl_trx_path(rtwdev, ntx_path, 2, RF_PATH_AB, 2); + rtw8922a_ctrl_trx_path(rtwdev, ntx_path, 2, nrx_path, rx_nss); rtw8922a_hal_reset(rtwdev, RTW89_PHY_0, RTW89_MAC_0, band, &tx_en0, false); if (rtwdev->dbcc_en) From ef42cf705ca32c07a58133817567ac06ef74dc19 Mon Sep 17 00:00:00 2001 From: Dan Hamik Date: Wed, 12 Nov 2025 07:04:51 +0000 Subject: [PATCH 54/80] wifi: rtw89: rtw8852bu: Added dev id for ASUS AX57 NANO USB Wifi dongle Add the USB device ID 0x0b05:0x1cb6 to the rtw8852bu driver to support the ASUS AX57 Nano WiFi 6 USB adapter.This device uses the same Realtek RTL8852BU chipset as other supported models. Tested on: Linux Mint 22 with kernel 6.8.0-87-generic. The adapter initializes successfully and connects to networks. Signed-off-by: Dan Hamik Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/010f019a76e1a84b-0a6f5e9f-2a43-4a9d-9c30-de4ae6363011-000000@us-east-2.amazonses.com --- drivers/net/wireless/realtek/rtw89/rtw8852bu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index c8a644403341..980d17ef68d0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -50,6 +50,8 @@ static const struct usb_device_id rtw_8852bu_id_table[] = { .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1a62, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1cb6, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x6931, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3327, 0xff, 0xff, 0xff), From 3a12581fc292997f5ca6dafe3238afd39b77e798 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Fri, 14 Nov 2025 14:01:15 +0800 Subject: [PATCH 55/80] wifi: rtw89: flush TX queue before deleting key In the wpa_supplicant rekey flow, it sends an EAPOL packet 4/4 through nl80211_tx_control_port() and triggers wake_tx_queue() in the driver. Then, it sends nl80211_new_key() to configure a new key in mac80211. However, in wake_tx_queue(), a workqueue is used to process the TX packet, which might cause the driver to process the EAPOL packet later than nl80211_new_key(). This results in the EAPOL packet 4/4 being transmitted with the new key and IV, causing it to be dropped by the AP. Therefore, needs to flush TX queue before deleting the old key to ensure that the EAPOL 4/4 packet is transmitted using the old key. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 6f7571c7c274..4a7364432428 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -969,6 +969,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } break; case DISABLE_KEY: + flush_work(&rtwdev->txq_work); rtw89_hci_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, false); rtw89_mac_flush_txq(rtwdev, BIT(rtwdev->hw->queues) - 1, false); From 92db331de6de948f4308ab4c80ddbe7ae27f39bb Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:16 +0800 Subject: [PATCH 56/80] wifi: rtw89: fw: part size to download firmware by header info The part size is the unit to download firmware piece by piece. Old chips use 2020 bytes as a piece, but in new chips the part size is defined in firmware header. Change to use the value dynamically. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 31 +++++++++++++++---------- drivers/net/wireless/realtek/rtw89/fw.h | 1 + 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 515e9f098380..b234df109634 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -161,6 +161,11 @@ static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 le info->dynamic_hdr_en = le32_get_bits(fw_hdr->w7, FW_HDR_W7_DYN_HDR); info->idmem_share_mode = le32_get_bits(fw_hdr->w7, FW_HDR_W7_IDMEM_SHARE_MODE); + if (chip->chip_gen == RTW89_CHIP_AX) + info->part_size = FWDL_SECTION_PER_PKT_LEN; + else + info->part_size = le32_get_bits(fw_hdr->w7, FW_HDR_W7_PART_SIZE); + if (info->dynamic_hdr_en) { info->hdr_len = le32_get_bits(fw_hdr->w3, FW_HDR_W3_LEN); info->dynamic_hdr_len = info->hdr_len - base_hdr_len; @@ -439,6 +444,7 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le struct rtw89_fw_bin_info *info) { const struct rtw89_fw_hdr_v1 *fw_hdr = (const struct rtw89_fw_hdr_v1 *)fw; + const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_fw_hdr_section_info *section_info; const struct rtw89_fw_dynhdr_hdr *fwdynhdr; const struct rtw89_fw_hdr_section_v1 *section; @@ -455,6 +461,11 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le info->dynamic_hdr_en = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_DYN_HDR); info->idmem_share_mode = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_IDMEM_SHARE_MODE); + if (chip->chip_gen == RTW89_CHIP_AX) + info->part_size = FWDL_SECTION_PER_PKT_LEN; + else + info->part_size = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_PART_SIZE); + if (info->dynamic_hdr_en) { info->hdr_len = le32_get_bits(fw_hdr->w5, FW_HDR_V1_W5_HDR_SIZE); info->dynamic_hdr_len = info->hdr_len - base_hdr_len; @@ -1516,8 +1527,7 @@ static u32 __rtw89_fw_download_tweak_hdr_v0(struct rtw89_dev *rtwdev, struct rtw89_fw_hdr_section *section; int i; - le32p_replace_bits(&fw_hdr->w7, FWDL_SECTION_PER_PKT_LEN, - FW_HDR_W7_PART_SIZE); + le32p_replace_bits(&fw_hdr->w7, info->part_size, FW_HDR_W7_PART_SIZE); for (i = 0; i < info->section_num; i++) { section_info = &info->section_info[i]; @@ -1542,8 +1552,7 @@ static u32 __rtw89_fw_download_tweak_hdr_v1(struct rtw89_dev *rtwdev, u8 dst_sec_idx = 0; u8 sec_idx; - le32p_replace_bits(&fw_hdr->w7, FWDL_SECTION_PER_PKT_LEN, - FW_HDR_V1_W7_PART_SIZE); + le32p_replace_bits(&fw_hdr->w7, info->part_size, FW_HDR_V1_W7_PART_SIZE); for (sec_idx = 0; sec_idx < info->section_num; sec_idx++) { section_info = &info->section_info[sec_idx]; @@ -1645,7 +1654,8 @@ static int rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, } static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev, - struct rtw89_fw_hdr_section_info *info) + struct rtw89_fw_hdr_section_info *info, + u32 part_size) { struct sk_buff *skb; const u8 *section = info->addr; @@ -1666,20 +1676,17 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev, } if (info->key_addr && info->key_len) { - if (residue_len > FWDL_SECTION_PER_PKT_LEN || info->len < info->key_len) + if (residue_len > part_size || info->len < info->key_len) rtw89_warn(rtwdev, "ignore to copy key data because of len %d, %d, %d, %d\n", - info->len, FWDL_SECTION_PER_PKT_LEN, + info->len, part_size, info->key_len, residue_len); else copy_key = true; } while (residue_len) { - if (residue_len >= FWDL_SECTION_PER_PKT_LEN) - pkt_len = FWDL_SECTION_PER_PKT_LEN; - else - pkt_len = residue_len; + pkt_len = min(residue_len, part_size); skb = rtw89_fw_h2c_alloc_skb_no_hdr(rtwdev, pkt_len); if (!skb) { @@ -1734,7 +1741,7 @@ static int rtw89_fw_download_main(struct rtw89_dev *rtwdev, int ret; while (section_num--) { - ret = __rtw89_fw_download_main(rtwdev, section_info); + ret = __rtw89_fw_download_main(rtwdev, section_info, info->part_size); if (ret) return ret; section_info++; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 6620256e3f22..87c55a1a9d94 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -297,6 +297,7 @@ struct rtw89_fw_hdr_section_info { struct rtw89_fw_bin_info { u8 section_num; + u32 part_size; u32 hdr_len; bool dynamic_hdr_en; u32 dynamic_hdr_len; From a2a64fe2346374940c8e8953b42c73983a84559b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:17 +0800 Subject: [PATCH 57/80] wifi: rtw89: mac: separate pre-init code before downloading firmware Driver needs to initialize registers before downloading firmware, so move pre-init part (power on) from original rtw89_mac_init(). The consequence patches will add more pre-init codes before downloading firmware. Since rtw89_phy_init_bb_afe() is used by coming RTL8922D, don't change logic at all for existing chips. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 8 ++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 15 +++++++++++---- drivers/net/wireless/realtek/rtw89/mac.h | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a15a911484bb..0cabea7dd92b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -5617,8 +5617,16 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) { int ret; + ret = rtw89_mac_preinit(rtwdev); + if (ret) { + rtw89_err(rtwdev, "mac preinit fail, ret: %d\n", ret); + return ret; + } + rtw89_phy_init_bb_afe(rtwdev); + /* above do preinit before downloading firmware */ + ret = rtw89_mac_init(rtwdev); if (ret) { rtw89_err(rtwdev, "mac init fail, ret:%d\n", ret); diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index dab0a76556c9..ff160d25a7c9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4126,6 +4126,17 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb) return 0; } +int rtw89_mac_preinit(struct rtw89_dev *rtwdev) +{ + int ret; + + ret = rtw89_mac_pwr_on(rtwdev); + if (ret) + return ret; + + return 0; +} + int rtw89_mac_init(struct rtw89_dev *rtwdev) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; @@ -4133,10 +4144,6 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev) bool include_bb = !!chip->bbmcu_nr; int ret; - ret = rtw89_mac_pwr_on(rtwdev); - if (ret) - return ret; - ret = rtw89_mac_partial_init(rtwdev, include_bb); if (ret) goto fail; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 3cc97fd0c0ec..d203db444a34 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1201,6 +1201,7 @@ rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev); void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev); int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb); +int rtw89_mac_preinit(struct rtw89_dev *rtwdev); int rtw89_mac_init(struct rtw89_dev *rtwdev); int rtw89_mac_dle_init(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode, enum rtw89_qta_mode ext_mode); From 12e84effcb6d6d2a58e03cb237c1100c817dd479 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:18 +0800 Subject: [PATCH 58/80] wifi: rtw89: phy: calling BB pre-init by chips with/without BB MCU The existing flow is doing BB pre-init before downloading BB MCU firmware, because existing chip RTL8922A has BB MCU. However, the coming chips don't have this, and BB pre-init configuring registers can affect downloading WiFi-CPU firmware. Therefore, calling BB pre-init afterward for new coming chips without BB MCU. For existing WiFi 6 chips, no BB pre-init. For RTL8922A, don't change the logic. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 4 ++++ drivers/net/wireless/realtek/rtw89/core.h | 11 ++++++++--- drivers/net/wireless/realtek/rtw89/mac.c | 9 ++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 0cabea7dd92b..ea22925b8772 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -5615,6 +5615,7 @@ EXPORT_SYMBOL(rtw89_check_quirks); int rtw89_core_start(struct rtw89_dev *rtwdev) { + bool no_bbmcu = !rtwdev->chip->bbmcu_nr; int ret; ret = rtw89_mac_preinit(rtwdev); @@ -5623,6 +5624,9 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) return ret; } + if (no_bbmcu) + rtw89_chip_bb_preinit(rtwdev); + rtw89_phy_init_bb_afe(rtwdev); /* above do preinit before downloading firmware */ diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f63857b389d7..8943166de52c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -7039,12 +7039,17 @@ static inline void rtw89_chip_rfk_hw_init(struct rtw89_dev *rtwdev) } static inline -void rtw89_chip_bb_preinit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +void rtw89_chip_bb_preinit(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; - if (chip->ops->bb_preinit) - chip->ops->bb_preinit(rtwdev, phy_idx); + if (!chip->ops->bb_preinit) + return; + + chip->ops->bb_preinit(rtwdev, RTW89_PHY_0); + + if (rtwdev->dbcc_en) + chip->ops->bb_preinit(rtwdev, RTW89_PHY_1); } static inline diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index ff160d25a7c9..21f5023c4f69 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4104,9 +4104,12 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb) rtw89_mac_ctrl_hci_dma_trx(rtwdev, true); if (include_bb) { - rtw89_chip_bb_preinit(rtwdev, RTW89_PHY_0); - if (rtwdev->dbcc_en) - rtw89_chip_bb_preinit(rtwdev, RTW89_PHY_1); + /* Only call BB preinit including configuration of BB MCU for + * the chips which need to download BB MCU firmware. Otherwise, + * calling preinit later to prevent touching registers affecting + * download firmware. + */ + rtw89_chip_bb_preinit(rtwdev); } ret = rtw89_mac_dmac_pre_init(rtwdev); From d375434cbceae79f841ecb0739ae6f5174b968e2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:19 +0800 Subject: [PATCH 59/80] wifi: rtw89: mac: remove undefined bit B_BE_PPDU_MAC_INFO The bit is defined during test chip development, but formal chips don't have this. Remove it. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 2 +- drivers/net/wireless/realtek/rtw89/reg.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index ef69672b6862..c3620db8aff6 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2020,7 +2020,7 @@ int rtw89_mac_cfg_ppdu_status_be(struct rtw89_dev *rtwdev, u8 mac_idx, bool enab } rtw89_write32_mask(rtwdev, R_BE_HW_PPDU_STATUS, B_BE_FWD_PPDU_STAT_MASK, 3); - rtw89_write32(rtwdev, reg, B_BE_PPDU_STAT_RPT_EN | B_BE_PPDU_MAC_INFO | + rtw89_write32(rtwdev, reg, B_BE_PPDU_STAT_RPT_EN | B_BE_APP_RX_CNT_RPT | B_BE_APP_PLCP_HDR_RPT | B_BE_PPDU_STAT_RPT_CRC32 | B_BE_PPDU_STAT_RPT_DMA); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index ed1d958bc49e..152fd8366dd7 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -7476,7 +7476,6 @@ #define B_BE_PPDU_STAT_RPT_ADDR BIT(4) #define B_BE_APP_PLCP_HDR_RPT BIT(3) #define B_BE_APP_RX_CNT_RPT BIT(2) -#define B_BE_PPDU_MAC_INFO BIT(1) #define B_BE_PPDU_STAT_RPT_EN BIT(0) #define R_BE_RX_SR_CTRL 0x1144A From 7fefea11001d5f5cf948a069f5f2cecc8fa5633f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:20 +0800 Subject: [PATCH 60/80] wifi: rtw89: mac: update wcpu_on to download firmware for RTL8922D The RTL8922D does more settings before downloading firmware, so add them accordingly. Also, update the missed settings for RTL8922A. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 7 +++++++ drivers/net/wireless/realtek/rtw89/reg.h | 23 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index c3620db8aff6..556e5f98e8d4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -458,6 +458,7 @@ static void set_cpu_en(struct rtw89_dev *rtwdev, bool include_bb) static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw) { + const struct rtw89_chip_info *chip = rtwdev->chip; u32 val32; int ret; @@ -479,6 +480,7 @@ static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw) rtw89_write32(rtwdev, R_BE_UDM1, 0); rtw89_write32(rtwdev, R_BE_UDM2, 0); + rtw89_write32(rtwdev, R_BE_BOOT_DBG, 0x0); rtw89_write32(rtwdev, R_BE_HALT_H2C, 0); rtw89_write32(rtwdev, R_BE_HALT_C2H, 0); rtw89_write32(rtwdev, R_BE_HALT_H2C_CTRL, 0); @@ -493,6 +495,11 @@ static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw) B_BE_WDT_WAKE_PCIE_EN | B_BE_WDT_WAKE_USB_EN); rtw89_write32_clr(rtwdev, R_BE_WCPU_FW_CTRL, B_BE_WDT_PLT_RST_EN | B_BE_WCPU_ROM_CUT_GET); + rtw89_write32(rtwdev, R_BE_SECURE_BOOT_MALLOC_INFO, 0); + rtw89_write32_clr(rtwdev, R_BE_GPIO_MUXCFG, B_BE_BOOT_MODE); + + if (chip->chip_id != RTL8922A) + rtw89_write32_set(rtwdev, R_BE_WCPU_FW_CTRL, B_BE_HOST_EXIST); rtw89_write16_mask(rtwdev, R_BE_BOOT_REASON, B_BE_BOOT_REASON_MASK, boot_reason); rtw89_write32_clr(rtwdev, R_BE_PLATFORM_ENABLE, B_BE_WCPU_EN); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 152fd8366dd7..5b4a459cf29c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -3963,6 +3963,24 @@ #define R_BE_EFUSE_CTRL_1_V1 0x0034 #define B_BE_EF_DATA_MASK GENMASK(31, 0) +#define R_BE_GPIO_MUXCFG 0x0040 +#define B_BE_WCPU_AUTO_EN BIT(26) +#define B_BE_WCPU_JTAG_EN BIT(24) +#define B_BE_WCPU_DBG_EN BIT(23) +#define B_BE_JTAG_CHAIN_EN BIT(20) +#define B_BE_BOOT_MODE BIT(19) +#define B_BE_WL_EECS_EXT_32K_SEL BIT(18) +#define B_BE_WL_SEC_BONDING_OPT_STS BIT(17) +#define B_BE_SECSIC_SEL BIT(16) +#define B_BE_ENHTP BIT(14) +#define B_BE_ENSIC BIT(12) +#define B_BE_SIC_SWRST BIT(11) +#define B_BE_PINMUX_PTA_EN BIT(10) +#define B_BE_WL_BT_PTA_SEC BIT(9) +#define B_BE_ENUARTTX BIT(8) +#define B_BE_DBG_GNT_BT_S1_POLARITY BIT(4) +#define B_BE_ENUARTRX BIT(2) + #define R_BE_GPIO_EXT_CTRL 0x0060 #define B_BE_GPIO_MOD_15_TO_8_MASK GENMASK(31, 24) #define B_BE_GPIO_MOD_9 BIT(25) @@ -4323,6 +4341,7 @@ #define B_BE_RUN_ENV_MASK GENMASK(31, 30) #define B_BE_WCPU_FWDL_STATUS_MASK GENMASK(29, 26) #define B_BE_WDT_PLT_RST_EN BIT(17) +#define B_BE_HOST_EXIST BIT(16) #define B_BE_FW_SEC_AUTH_DONE BIT(14) #define B_BE_FW_CPU_UTIL_STS_EN BIT(13) #define B_BE_BBMCU1_FWDL_EN BIT(12) @@ -4599,6 +4618,10 @@ #define B_BE_HCI_RXDMA_EN BIT(1) #define B_BE_HCI_TXDMA_EN BIT(0) +#define R_BE_BOOT_DBG 0x78F0 +#define B_BE_BOOT_STATUS_MASK GENMASK(31, 16) +#define B_BE_SECUREBOOT_STATUS_MASK GENMASK(15, 0) + #define R_BE_DBG_WOW_READY 0x815E #define B_BE_DBG_WOW_READY GENMASK(7, 0) From 9df55e8d41aa6943846d526487a674316c06e307 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:21 +0800 Subject: [PATCH 61/80] wifi: rtw89: phy: consider type 15 in BB gain table BB gain table is a table to configure gain for certain channels. Newly added type 15 is considered to write registers accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy_be.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 3316a38a62d0..bd17714f13d1 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -266,6 +266,10 @@ static void rtw89_phy_config_bb_gain_be(struct rtw89_dev *rtwdev, case 3: rtw89_phy_cfg_bb_gain_op1db_be(rtwdev, arg, reg->data); break; + case 15: + rtw89_phy_write32_idx(rtwdev, reg->addr & 0xFFFFF, MASKHWORD, + reg->data, RTW89_PHY_0); + break; case 4: /* This cfg_type is only used by rfe_type >= 50 with eFEM */ if (efuse->rfe_type < 50) From 5607a141bf283960326f44780b65ab607467a14c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:22 +0800 Subject: [PATCH 62/80] wifi: rtw89: phy: ignore DCFO if not defined in chip_info For WiFi 7 chips, DCFO (digital carrier frequency offset) feature isn't supported, so the corresponding registers aren't defined in chip_info. Check and ignore this feature accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 28e2b15240a7..08eb3fa6b347 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -4608,7 +4608,7 @@ static void rtw89_dcfo_comp(struct rtw89_dev *rtwdev, s32 curr_cfo) s32 dcfo_comp_val; int sign; - if (rtwdev->chip->chip_id == RTL8922A) + if (!dcfo_comp) return; if (!is_linked) { From 1c2ada09628b6c3de539d45015c62f10f1f00a59 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:23 +0800 Subject: [PATCH 63/80] wifi: rtw89: fw: print band and port where beacon update on The C2H event of BCN_UPD_DONE is to notify driver that firmware changes beacon content on certain band/port asked by driver. Print the notification for debug purpose for now. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.h | 9 +++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 13 ++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 87c55a1a9d94..bb3e7bbb41de 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3648,6 +3648,15 @@ struct rtw89_fw_c2h_log_fmt { #define RTW89_C2H_FW_LOG_SIGNATURE 0xA5A5 #define RTW89_C2H_FW_LOG_STR_BUF_SIZE 512 +struct rtw89_c2h_bcn_upd_done { + struct rtw89_c2h_hdr hdr; + __le32 w2; +} __packed; + +#define RTW89_C2H_BCN_UPD_DONE_W2_PORT GENMASK(2, 0) +#define RTW89_C2H_BCN_UPD_DONE_W2_MBSSID GENMASK(6, 3) +#define RTW89_C2H_BCN_UPD_DONE_W2_BAND_IDX BIT(7) + struct rtw89_c2h_mac_bcnfltr_rpt { __le32 w0; __le32 w1; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 21f5023c4f69..386cc982d7c9 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5309,8 +5309,19 @@ rtw89_mac_c2h_bcn_cnt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) } static void -rtw89_mac_c2h_bcn_upd_done(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +rtw89_mac_c2h_bcn_upd_done(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len) { + const struct rtw89_c2h_bcn_upd_done *c2h = + (const struct rtw89_c2h_bcn_upd_done *)skb_c2h->data; + u8 band, port, mbssid; + + port = le32_get_bits(c2h->w2, RTW89_C2H_BCN_UPD_DONE_W2_PORT); + mbssid = le32_get_bits(c2h->w2, RTW89_C2H_BCN_UPD_DONE_W2_MBSSID); + band = le32_get_bits(c2h->w2, RTW89_C2H_BCN_UPD_DONE_W2_BAND_IDX); + + rtw89_debug(rtwdev, RTW89_DBG_FW, + "BCN update done on port:%d mbssid:%d band:%d\n", + port, mbssid, band); } static void From aa8d1a8805bb892f01283e168afbb58462f4a94c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:24 +0800 Subject: [PATCH 64/80] wifi: rtw89: align RA H2C format v1 for RTL8922A For RTL8922A, the bits [16:31] of word 3 of v1 format isn't as WiFi 6 chips. Instead, it only needs to fill additional band type and partial bandwidth ER. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 9 ++++++- drivers/net/wireless/realtek/rtw89/fw.c | 33 ++++++++++++++--------- drivers/net/wireless/realtek/rtw89/fw.h | 7 +++++ drivers/net/wireless/realtek/rtw89/phy.c | 4 +++ 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 8943166de52c..1f99d5f264d2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3384,11 +3384,18 @@ struct rtw89_ra_info { u8 cr_tbl_sel:1; u8 fix_giltf_en:1; u8 fix_giltf:3; - u8 rsvd2:1; + u8 partial_bw_er:1; u8 csi_mcs_ss_idx; u8 csi_mode:2; u8 csi_gi_ltf:3; u8 csi_bw:3; + /* after v1 */ + u8 is_noisy:1; + u8 psra_en:1; + u8 rsvd0:1; + u8 macid_msb:2; + u8 band:2; /* enum rtw89_band */ + u8 is_new_dbgreg:1; }; #define RTW89_PPDU_MAC_INFO_USR_SIZE 4 diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index b234df109634..0dc5aa5ed218 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -4739,13 +4739,16 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi struct rtw89_h2c_ra_v1 *h2c_v1; struct rtw89_h2c_ra *h2c; u32 len = sizeof(*h2c); - bool format_v1 = false; struct sk_buff *skb; + u8 ver = U8_MAX; int ret; - if (chip->chip_gen == RTW89_CHIP_BE) { + if (chip->chip_gen == RTW89_CHIP_AX) { + len = sizeof(*h2c); + ver = 0; + } else { len = sizeof(*h2c_v1); - format_v1 = true; + ver = 1; } skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); @@ -4777,16 +4780,8 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi h2c->w3 = le32_encode_bits(ra->fix_giltf_en, RTW89_H2C_RA_W3_FIX_GILTF_EN) | le32_encode_bits(ra->fix_giltf, RTW89_H2C_RA_W3_FIX_GILTF); - if (!format_v1) - goto csi; - - h2c_v1 = (struct rtw89_h2c_ra_v1 *)h2c; - h2c_v1->w4 = le32_encode_bits(ra->mode_ctrl, RTW89_H2C_RA_V1_W4_MODE_EHT) | - le32_encode_bits(ra->bw_cap, RTW89_H2C_RA_V1_W4_BW_EHT); - -csi: - if (!csi) - goto done; + if (!csi || ver >= 1) + goto next_v1; h2c->w2 |= le32_encode_bits(1, RTW89_H2C_RA_W2_BFEE_CSI_CTL); h2c->w3 |= le32_encode_bits(ra->band_num, RTW89_H2C_RA_W3_BAND_NUM) | @@ -4798,6 +4793,18 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi le32_encode_bits(ra->csi_gi_ltf, RTW89_H2C_RA_W3_FIXED_CSI_GI_LTF) | le32_encode_bits(ra->csi_bw, RTW89_H2C_RA_W3_FIXED_CSI_BW); +next_v1: + if (ver < 1) + goto done; + + h2c->w3 |= le32_encode_bits(ra->partial_bw_er, + RTW89_H2C_RA_V1_W3_PARTIAL_BW_SU_ER) | + le32_encode_bits(ra->band, RTW89_H2C_RA_V1_W3_BAND); + + h2c_v1 = (struct rtw89_h2c_ra_v1 *)h2c; + h2c_v1->w4 = le32_encode_bits(ra->mode_ctrl, RTW89_H2C_RA_V1_W4_MODE_EHT) | + le32_encode_bits(ra->bw_cap, RTW89_H2C_RA_V1_W4_BW_EHT); + done: rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index bb3e7bbb41de..e62b61f584fb 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -447,6 +447,13 @@ struct rtw89_h2c_ra { #define RTW89_H2C_RA_W3_FIXED_CSI_MODE GENMASK(25, 24) #define RTW89_H2C_RA_W3_FIXED_CSI_GI_LTF GENMASK(28, 26) #define RTW89_H2C_RA_W3_FIXED_CSI_BW GENMASK(31, 29) +#define RTW89_H2C_RA_V1_W3_PARTIAL_BW_SU_ER BIT(15) +#define RTW89_H2C_RA_V1_W3_FIXED_CSI_RATE_L GENMASK(23, 16) +#define RTW89_H2C_RA_V1_W3_IS_NOISY BIT(24) +#define RTW89_H2C_RA_V1_W3_PSRA_EN BIT(25) +#define RTW89_H2C_RA_V1_W3_MACID_MSB GENMASK(28, 27) +#define RTW89_H2C_RA_V1_W3_BAND GENMASK(30, 29) +#define RTW89_H2C_RA_V1_W3_NEW_DBGREG BIT(31) struct rtw89_h2c_ra_v1 { struct rtw89_h2c_ra v0; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 08eb3fa6b347..9f418b1fb7ed 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -476,6 +476,10 @@ static void rtw89_phy_ra_sta_update(struct rtw89_dev *rtwdev, ra->ra_mask = ra_mask; ra->fix_giltf_en = fix_giltf_en; ra->fix_giltf = fix_giltf; + ra->partial_bw_er = link_sta->he_cap.has_he ? + !!(link_sta->he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE) : 0; + ra->band = chan->band_type; if (!csi) return; From 239dd70d776cf94f39000740b307c91e4f72d615 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:25 +0800 Subject: [PATCH 65/80] wifi: rtw89: fill addr cam H2C command by struct The addr cam is used to tell firmware the MAC address and BSSID associated to connected stations. Use struct instead of macros with pointer arithmetic to fill the data. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/cam.c | 146 ++++---- drivers/net/wireless/realtek/rtw89/cam.h | 433 +++++------------------ drivers/net/wireless/realtek/rtw89/fw.c | 17 +- 3 files changed, 172 insertions(+), 424 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c index 385a238fe5cc..8233d91024e8 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.c +++ b/drivers/net/wireless/realtek/rtw89/cam.c @@ -760,7 +760,8 @@ int rtw89_cam_init(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, - struct rtw89_sta_link *rtwsta_link, u8 *cmd) + struct rtw89_sta_link *rtwsta_link, + struct rtw89_h2c_addr_cam *h2c) { struct rtw89_bssid_cam_entry *bssid_cam = rtw89_get_bssid_cam_of(rtwvif_link, rtwsta_link); @@ -780,20 +781,19 @@ int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev, rcu_read_unlock(); - FWCMD_SET_ADDR_BSSID_IDX(cmd, bssid_cam->bssid_cam_idx); - FWCMD_SET_ADDR_BSSID_OFFSET(cmd, bssid_cam->offset); - FWCMD_SET_ADDR_BSSID_LEN(cmd, bssid_cam->len); - FWCMD_SET_ADDR_BSSID_VALID(cmd, bssid_cam->valid); - FWCMD_SET_ADDR_BSSID_MASK(cmd, bss_mask); - FWCMD_SET_ADDR_BSSID_BB_SEL(cmd, bssid_cam->phy_idx); - FWCMD_SET_ADDR_BSSID_BSS_COLOR(cmd, bss_color); - - FWCMD_SET_ADDR_BSSID_BSSID0(cmd, bssid_cam->bssid[0]); - FWCMD_SET_ADDR_BSSID_BSSID1(cmd, bssid_cam->bssid[1]); - FWCMD_SET_ADDR_BSSID_BSSID2(cmd, bssid_cam->bssid[2]); - FWCMD_SET_ADDR_BSSID_BSSID3(cmd, bssid_cam->bssid[3]); - FWCMD_SET_ADDR_BSSID_BSSID4(cmd, bssid_cam->bssid[4]); - FWCMD_SET_ADDR_BSSID_BSSID5(cmd, bssid_cam->bssid[5]); + h2c->w12 = le32_encode_bits(bssid_cam->bssid_cam_idx, ADDR_CAM_W12_BSSID_IDX) | + le32_encode_bits(bssid_cam->offset, ADDR_CAM_W12_BSSID_OFFSET) | + le32_encode_bits(bssid_cam->len, ADDR_CAM_W12_BSSID_LEN); + h2c->w13 = le32_encode_bits(bssid_cam->valid, ADDR_CAM_W13_BSSID_VALID) | + le32_encode_bits(bss_mask, ADDR_CAM_W13_BSSID_MASK) | + le32_encode_bits(bssid_cam->phy_idx, ADDR_CAM_W13_BSSID_BB_SEL) | + le32_encode_bits(bss_color, ADDR_CAM_W13_BSSID_BSS_COLOR) | + le32_encode_bits(bssid_cam->bssid[0], ADDR_CAM_W13_BSSID_BSSID0) | + le32_encode_bits(bssid_cam->bssid[1], ADDR_CAM_W13_BSSID_BSSID1); + h2c->w14 = le32_encode_bits(bssid_cam->bssid[2], ADDR_CAM_W14_BSSID_BSSID2) | + le32_encode_bits(bssid_cam->bssid[3], ADDR_CAM_W14_BSSID_BSSID3) | + le32_encode_bits(bssid_cam->bssid[4], ADDR_CAM_W14_BSSID_BSSID4) | + le32_encode_bits(bssid_cam->bssid[5], ADDR_CAM_W14_BSSID_BSSID5); return 0; } @@ -813,7 +813,7 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr, - u8 *cmd) + struct rtw89_h2c_addr_cam *h2c) { struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); struct rtw89_addr_cam_entry *addr_cam = @@ -845,69 +845,65 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, sma_hash = rtw89_cam_addr_hash(sma_start, sma); tma_hash = rtw89_cam_addr_hash(tma_start, tma); - FWCMD_SET_ADDR_IDX(cmd, addr_cam->addr_cam_idx); - FWCMD_SET_ADDR_OFFSET(cmd, addr_cam->offset); - FWCMD_SET_ADDR_LEN(cmd, addr_cam->len); + h2c->w1 = le32_encode_bits(addr_cam->addr_cam_idx, ADDR_CAM_W1_IDX) | + le32_encode_bits(addr_cam->offset, ADDR_CAM_W1_OFFSET) | + le32_encode_bits(addr_cam->len, ADDR_CAM_W1_LEN); + h2c->w2 = le32_encode_bits(addr_cam->valid, ADDR_CAM_W2_VALID) | + le32_encode_bits(rtwvif_link->net_type, ADDR_CAM_W2_NET_TYPE) | + le32_encode_bits(rtwvif_link->bcn_hit_cond, ADDR_CAM_W2_BCN_HIT_COND) | + le32_encode_bits(rtwvif_link->hit_rule, ADDR_CAM_W2_HIT_RULE) | + le32_encode_bits(rtwvif_link->phy_idx, ADDR_CAM_W2_BB_SEL) | + le32_encode_bits(addr_cam->addr_mask, ADDR_CAM_W2_ADDR_MASK) | + le32_encode_bits(addr_cam->mask_sel, ADDR_CAM_W2_MASK_SEL) | + le32_encode_bits(sma_hash, ADDR_CAM_W2_SMA_HASH) | + le32_encode_bits(tma_hash, ADDR_CAM_W2_TMA_HASH); + h2c->w3 = le32_encode_bits(addr_cam->bssid_cam_idx, ADDR_CAM_W3_BSSID_CAM_IDX); + h2c->w4 = le32_encode_bits(sma[0], ADDR_CAM_W4_SMA0) | + le32_encode_bits(sma[1], ADDR_CAM_W4_SMA1) | + le32_encode_bits(sma[2], ADDR_CAM_W4_SMA2) | + le32_encode_bits(sma[3], ADDR_CAM_W4_SMA3); + h2c->w5 = le32_encode_bits(sma[4], ADDR_CAM_W5_SMA4) | + le32_encode_bits(sma[5], ADDR_CAM_W5_SMA5) | + le32_encode_bits(tma[0], ADDR_CAM_W5_TMA0) | + le32_encode_bits(tma[1], ADDR_CAM_W5_TMA1); + h2c->w6 = le32_encode_bits(tma[2], ADDR_CAM_W6_TMA2) | + le32_encode_bits(tma[3], ADDR_CAM_W6_TMA3) | + le32_encode_bits(tma[4], ADDR_CAM_W6_TMA4) | + le32_encode_bits(tma[5], ADDR_CAM_W6_TMA5); + h2c->w8 = le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_PORT_INT) | + le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_TSF_SYNC) | + le32_encode_bits(rtwvif_link->trigger, ADDR_CAM_W8_TF_TRS) | + le32_encode_bits(rtwvif_link->lsig_txop, ADDR_CAM_W8_LSIG_TXOP) | + le32_encode_bits(rtwvif_link->tgt_ind, ADDR_CAM_W8_TGT_IND) | + le32_encode_bits(rtwvif_link->frm_tgt_ind, ADDR_CAM_W8_FRM_TGT_IND) | + le32_encode_bits(rtwsta_link ? rtwsta_link->mac_id : + rtwvif_link->mac_id, ADDR_CAM_W8_MACID); - FWCMD_SET_ADDR_VALID(cmd, addr_cam->valid); - FWCMD_SET_ADDR_NET_TYPE(cmd, rtwvif_link->net_type); - FWCMD_SET_ADDR_BCN_HIT_COND(cmd, rtwvif_link->bcn_hit_cond); - FWCMD_SET_ADDR_HIT_RULE(cmd, rtwvif_link->hit_rule); - FWCMD_SET_ADDR_BB_SEL(cmd, rtwvif_link->phy_idx); - FWCMD_SET_ADDR_ADDR_MASK(cmd, addr_cam->addr_mask); - FWCMD_SET_ADDR_MASK_SEL(cmd, addr_cam->mask_sel); - FWCMD_SET_ADDR_SMA_HASH(cmd, sma_hash); - FWCMD_SET_ADDR_TMA_HASH(cmd, tma_hash); - - FWCMD_SET_ADDR_BSSID_CAM_IDX(cmd, addr_cam->bssid_cam_idx); - - FWCMD_SET_ADDR_SMA0(cmd, sma[0]); - FWCMD_SET_ADDR_SMA1(cmd, sma[1]); - FWCMD_SET_ADDR_SMA2(cmd, sma[2]); - FWCMD_SET_ADDR_SMA3(cmd, sma[3]); - FWCMD_SET_ADDR_SMA4(cmd, sma[4]); - FWCMD_SET_ADDR_SMA5(cmd, sma[5]); - - FWCMD_SET_ADDR_TMA0(cmd, tma[0]); - FWCMD_SET_ADDR_TMA1(cmd, tma[1]); - FWCMD_SET_ADDR_TMA2(cmd, tma[2]); - FWCMD_SET_ADDR_TMA3(cmd, tma[3]); - FWCMD_SET_ADDR_TMA4(cmd, tma[4]); - FWCMD_SET_ADDR_TMA5(cmd, tma[5]); - - FWCMD_SET_ADDR_PORT_INT(cmd, rtwvif_link->port); - FWCMD_SET_ADDR_TSF_SYNC(cmd, rtwvif_link->port); - FWCMD_SET_ADDR_TF_TRS(cmd, rtwvif_link->trigger); - FWCMD_SET_ADDR_LSIG_TXOP(cmd, rtwvif_link->lsig_txop); - FWCMD_SET_ADDR_TGT_IND(cmd, rtwvif_link->tgt_ind); - FWCMD_SET_ADDR_FRM_TGT_IND(cmd, rtwvif_link->frm_tgt_ind); - FWCMD_SET_ADDR_MACID(cmd, rtwsta_link ? rtwsta_link->mac_id : - rtwvif_link->mac_id); if (rtwvif_link->net_type == RTW89_NET_TYPE_INFRA) - FWCMD_SET_ADDR_AID12(cmd, vif->cfg.aid & 0xfff); + h2c->w9 = le32_encode_bits(vif->cfg.aid & 0xfff, ADDR_CAM_W9_AID12); else if (rtwvif_link->net_type == RTW89_NET_TYPE_AP_MODE) - FWCMD_SET_ADDR_AID12(cmd, sta ? sta->aid & 0xfff : 0); - FWCMD_SET_ADDR_WOL_PATTERN(cmd, rtwvif_link->wowlan_pattern); - FWCMD_SET_ADDR_WOL_UC(cmd, rtwvif_link->wowlan_uc); - FWCMD_SET_ADDR_WOL_MAGIC(cmd, rtwvif_link->wowlan_magic); - FWCMD_SET_ADDR_WAPI(cmd, addr_cam->wapi); - FWCMD_SET_ADDR_SEC_ENT_MODE(cmd, addr_cam->sec_ent_mode); - FWCMD_SET_ADDR_SEC_ENT0_KEYID(cmd, addr_cam->sec_ent_keyid[0]); - FWCMD_SET_ADDR_SEC_ENT1_KEYID(cmd, addr_cam->sec_ent_keyid[1]); - FWCMD_SET_ADDR_SEC_ENT2_KEYID(cmd, addr_cam->sec_ent_keyid[2]); - FWCMD_SET_ADDR_SEC_ENT3_KEYID(cmd, addr_cam->sec_ent_keyid[3]); - FWCMD_SET_ADDR_SEC_ENT4_KEYID(cmd, addr_cam->sec_ent_keyid[4]); - FWCMD_SET_ADDR_SEC_ENT5_KEYID(cmd, addr_cam->sec_ent_keyid[5]); - FWCMD_SET_ADDR_SEC_ENT6_KEYID(cmd, addr_cam->sec_ent_keyid[6]); + h2c->w9 = le32_encode_bits(sta ? sta->aid & 0xfff : 0, ADDR_CAM_W9_AID12); - FWCMD_SET_ADDR_SEC_ENT_VALID(cmd, addr_cam->sec_cam_map[0] & 0xff); - FWCMD_SET_ADDR_SEC_ENT0(cmd, addr_cam->sec_ent[0]); - FWCMD_SET_ADDR_SEC_ENT1(cmd, addr_cam->sec_ent[1]); - FWCMD_SET_ADDR_SEC_ENT2(cmd, addr_cam->sec_ent[2]); - FWCMD_SET_ADDR_SEC_ENT3(cmd, addr_cam->sec_ent[3]); - FWCMD_SET_ADDR_SEC_ENT4(cmd, addr_cam->sec_ent[4]); - FWCMD_SET_ADDR_SEC_ENT5(cmd, addr_cam->sec_ent[5]); - FWCMD_SET_ADDR_SEC_ENT6(cmd, addr_cam->sec_ent[6]); + h2c->w9 |= le32_encode_bits(rtwvif_link->wowlan_pattern, ADDR_CAM_W9_WOL_PATTERN) | + le32_encode_bits(rtwvif_link->wowlan_uc, ADDR_CAM_W9_WOL_UC) | + le32_encode_bits(rtwvif_link->wowlan_magic, ADDR_CAM_W9_WOL_MAGIC) | + le32_encode_bits(addr_cam->wapi, ADDR_CAM_W9_WAPI) | + le32_encode_bits(addr_cam->sec_ent_mode, ADDR_CAM_W9_SEC_ENT_MODE) | + le32_encode_bits(addr_cam->sec_ent_keyid[0], ADDR_CAM_W9_SEC_ENT0_KEYID) | + le32_encode_bits(addr_cam->sec_ent_keyid[1], ADDR_CAM_W9_SEC_ENT1_KEYID) | + le32_encode_bits(addr_cam->sec_ent_keyid[2], ADDR_CAM_W9_SEC_ENT2_KEYID) | + le32_encode_bits(addr_cam->sec_ent_keyid[3], ADDR_CAM_W9_SEC_ENT3_KEYID) | + le32_encode_bits(addr_cam->sec_ent_keyid[4], ADDR_CAM_W9_SEC_ENT4_KEYID) | + le32_encode_bits(addr_cam->sec_ent_keyid[5], ADDR_CAM_W9_SEC_ENT5_KEYID) | + le32_encode_bits(addr_cam->sec_ent_keyid[6], ADDR_CAM_W9_SEC_ENT6_KEYID); + h2c->w10 = le32_encode_bits(addr_cam->sec_cam_map[0] & 0xff, ADDR_CAM_W10_SEC_ENT_VALID) | + le32_encode_bits(addr_cam->sec_ent[0], ADDR_CAM_W10_SEC_ENT0) | + le32_encode_bits(addr_cam->sec_ent[1], ADDR_CAM_W10_SEC_ENT1) | + le32_encode_bits(addr_cam->sec_ent[2], ADDR_CAM_W10_SEC_ENT2); + h2c->w11 = le32_encode_bits(addr_cam->sec_ent[3], ADDR_CAM_W11_SEC_ENT3) | + le32_encode_bits(addr_cam->sec_ent[4], ADDR_CAM_W11_SEC_ENT4) | + le32_encode_bits(addr_cam->sec_ent[5], ADDR_CAM_W11_SEC_ENT5) | + le32_encode_bits(addr_cam->sec_ent[6], ADDR_CAM_W11_SEC_ENT6); rcu_read_unlock(); } diff --git a/drivers/net/wireless/realtek/rtw89/cam.h b/drivers/net/wireless/realtek/rtw89/cam.h index 8fd2d776408e..2bc8fbf79c0b 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.h +++ b/drivers/net/wireless/realtek/rtw89/cam.h @@ -12,345 +12,92 @@ #define RTW89_BSSID_MATCH_ALL GENMASK(5, 0) #define RTW89_BSSID_MATCH_5_BYTES GENMASK(4, 0) -static inline void FWCMD_SET_ADDR_IDX(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_OFFSET(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(15, 8)); -} - -static inline void FWCMD_SET_ADDR_LEN(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_VALID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, BIT(0)); -} - -static inline void FWCMD_SET_ADDR_NET_TYPE(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(2, 1)); -} - -static inline void FWCMD_SET_ADDR_BCN_HIT_COND(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(4, 3)); -} - -static inline void FWCMD_SET_ADDR_HIT_RULE(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(6, 5)); -} - -static inline void FWCMD_SET_ADDR_BB_SEL(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, BIT(7)); -} - -static inline void FWCMD_SET_ADDR_ADDR_MASK(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(13, 8)); -} - -static inline void FWCMD_SET_ADDR_MASK_SEL(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(15, 14)); -} - -static inline void FWCMD_SET_ADDR_SMA_HASH(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_TMA_HASH(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(31, 24)); -} - -static inline void FWCMD_SET_ADDR_BSSID_CAM_IDX(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 3, value, GENMASK(5, 0)); -} - -static inline void FWCMD_SET_ADDR_SMA0(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_SMA1(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(15, 8)); -} - -static inline void FWCMD_SET_ADDR_SMA2(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_SMA3(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(31, 24)); -} - -static inline void FWCMD_SET_ADDR_SMA4(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_SMA5(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(15, 8)); -} - -static inline void FWCMD_SET_ADDR_TMA0(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_TMA1(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(31, 24)); -} - -static inline void FWCMD_SET_ADDR_TMA2(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_TMA3(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(15, 8)); -} - -static inline void FWCMD_SET_ADDR_TMA4(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_TMA5(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(31, 24)); -} - -static inline void FWCMD_SET_ADDR_MACID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_PORT_INT(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(10, 8)); -} - -static inline void FWCMD_SET_ADDR_TSF_SYNC(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(13, 11)); -} - -static inline void FWCMD_SET_ADDR_TF_TRS(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 8, value, BIT(14)); -} - -static inline void FWCMD_SET_ADDR_LSIG_TXOP(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 8, value, BIT(15)); -} - -static inline void FWCMD_SET_ADDR_TGT_IND(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(26, 24)); -} - -static inline void FWCMD_SET_ADDR_FRM_TGT_IND(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(29, 27)); -} - -static inline void FWCMD_SET_ADDR_AID12(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(11, 0)); -} - -static inline void FWCMD_SET_ADDR_AID12_0(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_AID12_1(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(11, 8)); -} - -static inline void FWCMD_SET_ADDR_WOL_PATTERN(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(12)); -} - -static inline void FWCMD_SET_ADDR_WOL_UC(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(13)); -} - -static inline void FWCMD_SET_ADDR_WOL_MAGIC(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(14)); -} - -static inline void FWCMD_SET_ADDR_WAPI(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(15)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT_MODE(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(17, 16)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT0_KEYID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(19, 18)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT1_KEYID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(21, 20)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT2_KEYID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(23, 22)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT3_KEYID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(25, 24)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT4_KEYID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(27, 26)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT5_KEYID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(29, 28)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT6_KEYID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(31, 30)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT_VALID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT0(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(15, 8)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT1(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT2(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(31, 24)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT3(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT4(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(15, 8)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT5(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_SEC_ENT6(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(31, 24)); -} - -static inline void FWCMD_SET_ADDR_BSSID_IDX(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_BSSID_OFFSET(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(15, 8)); -} - -static inline void FWCMD_SET_ADDR_BSSID_LEN(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_BSSID_VALID(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 13, value, BIT(0)); -} - -static inline void FWCMD_SET_ADDR_BSSID_BB_SEL(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 13, value, BIT(1)); -} - -static inline void FWCMD_SET_ADDR_BSSID_MASK(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(7, 2)); -} - -static inline void FWCMD_SET_ADDR_BSSID_BSS_COLOR(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(13, 8)); -} - -static inline void FWCMD_SET_ADDR_BSSID_BSSID0(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_BSSID_BSSID1(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(31, 24)); -} - -static inline void FWCMD_SET_ADDR_BSSID_BSSID2(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(7, 0)); -} - -static inline void FWCMD_SET_ADDR_BSSID_BSSID3(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(15, 8)); -} - -static inline void FWCMD_SET_ADDR_BSSID_BSSID4(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(23, 16)); -} - -static inline void FWCMD_SET_ADDR_BSSID_BSSID5(void *cmd, u32 value) -{ - le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(31, 24)); -} +struct rtw89_h2c_addr_cam { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; + __le32 w4; + __le32 w5; + __le32 w6; + __le32 w7; + __le32 w8; + __le32 w9; + __le32 w10; + __le32 w11; + __le32 w12; + __le32 w13; + __le32 w14; +} __packed; + +#define ADDR_CAM_W1_IDX GENMASK(7, 0) +#define ADDR_CAM_W1_OFFSET GENMASK(15, 8) +#define ADDR_CAM_W1_LEN GENMASK(23, 16) +#define ADDR_CAM_W2_VALID BIT(0) +#define ADDR_CAM_W2_NET_TYPE GENMASK(2, 1) +#define ADDR_CAM_W2_BCN_HIT_COND GENMASK(4, 3) +#define ADDR_CAM_W2_HIT_RULE GENMASK(6, 5) +#define ADDR_CAM_W2_BB_SEL BIT(7) +#define ADDR_CAM_W2_ADDR_MASK GENMASK(13, 8) +#define ADDR_CAM_W2_MASK_SEL GENMASK(15, 14) +#define ADDR_CAM_W2_SMA_HASH GENMASK(23, 16) +#define ADDR_CAM_W2_TMA_HASH GENMASK(31, 24) +#define ADDR_CAM_W3_BSSID_CAM_IDX GENMASK(5, 0) +#define ADDR_CAM_W4_SMA0 GENMASK(7, 0) +#define ADDR_CAM_W4_SMA1 GENMASK(15, 8) +#define ADDR_CAM_W4_SMA2 GENMASK(23, 16) +#define ADDR_CAM_W4_SMA3 GENMASK(31, 24) +#define ADDR_CAM_W5_SMA4 GENMASK(7, 0) +#define ADDR_CAM_W5_SMA5 GENMASK(15, 8) +#define ADDR_CAM_W5_TMA0 GENMASK(23, 16) +#define ADDR_CAM_W5_TMA1 GENMASK(31, 24) +#define ADDR_CAM_W6_TMA2 GENMASK(7, 0) +#define ADDR_CAM_W6_TMA3 GENMASK(15, 8) +#define ADDR_CAM_W6_TMA4 GENMASK(23, 16) +#define ADDR_CAM_W6_TMA5 GENMASK(31, 24) +#define ADDR_CAM_W8_MACID GENMASK(7, 0) +#define ADDR_CAM_W8_PORT_INT GENMASK(10, 8) +#define ADDR_CAM_W8_TSF_SYNC GENMASK(13, 11) +#define ADDR_CAM_W8_TF_TRS BIT(14) +#define ADDR_CAM_W8_LSIG_TXOP BIT(15) +#define ADDR_CAM_W8_TGT_IND GENMASK(26, 24) +#define ADDR_CAM_W8_FRM_TGT_IND GENMASK(29, 27) +#define ADDR_CAM_W9_AID12 GENMASK(11, 0) +#define ADDR_CAM_W9_AID12_0 GENMASK(7, 0) +#define ADDR_CAM_W9_AID12_1 GENMASK(11, 8) +#define ADDR_CAM_W9_WOL_PATTERN BIT(12) +#define ADDR_CAM_W9_WOL_UC BIT(13) +#define ADDR_CAM_W9_WOL_MAGIC BIT(14) +#define ADDR_CAM_W9_WAPI BIT(15) +#define ADDR_CAM_W9_SEC_ENT_MODE GENMASK(17, 16) +#define ADDR_CAM_W9_SEC_ENT0_KEYID GENMASK(19, 18) +#define ADDR_CAM_W9_SEC_ENT1_KEYID GENMASK(21, 20) +#define ADDR_CAM_W9_SEC_ENT2_KEYID GENMASK(23, 22) +#define ADDR_CAM_W9_SEC_ENT3_KEYID GENMASK(25, 24) +#define ADDR_CAM_W9_SEC_ENT4_KEYID GENMASK(27, 26) +#define ADDR_CAM_W9_SEC_ENT5_KEYID GENMASK(29, 28) +#define ADDR_CAM_W9_SEC_ENT6_KEYID GENMASK(31, 30) +#define ADDR_CAM_W10_SEC_ENT_VALID GENMASK(7, 0) +#define ADDR_CAM_W10_SEC_ENT0 GENMASK(15, 8) +#define ADDR_CAM_W10_SEC_ENT1 GENMASK(23, 16) +#define ADDR_CAM_W10_SEC_ENT2 GENMASK(31, 24) +#define ADDR_CAM_W11_SEC_ENT3 GENMASK(7, 0) +#define ADDR_CAM_W11_SEC_ENT4 GENMASK(15, 8) +#define ADDR_CAM_W11_SEC_ENT5 GENMASK(23, 16) +#define ADDR_CAM_W11_SEC_ENT6 GENMASK(31, 24) +#define ADDR_CAM_W12_BSSID_IDX GENMASK(7, 0) +#define ADDR_CAM_W12_BSSID_OFFSET GENMASK(15, 8) +#define ADDR_CAM_W12_BSSID_LEN GENMASK(23, 16) +#define ADDR_CAM_W13_BSSID_VALID BIT(0) +#define ADDR_CAM_W13_BSSID_BB_SEL BIT(1) +#define ADDR_CAM_W13_BSSID_MASK GENMASK(7, 2) +#define ADDR_CAM_W13_BSSID_BSS_COLOR GENMASK(13, 8) +#define ADDR_CAM_W13_BSSID_BSSID0 GENMASK(23, 16) +#define ADDR_CAM_W13_BSSID_BSSID1 GENMASK(31, 24) +#define ADDR_CAM_W14_BSSID_BSSID2 GENMASK(7, 0) +#define ADDR_CAM_W14_BSSID_BSSID3 GENMASK(15, 8) +#define ADDR_CAM_W14_BSSID_BSSID4 GENMASK(23, 16) +#define ADDR_CAM_W14_BSSID_BSSID5 GENMASK(31, 24) struct rtw89_h2c_dctlinfo_ud_v1 { __le32 c0; @@ -552,9 +299,10 @@ int rtw89_cam_init_bssid_cam(struct rtw89_dev *rtwdev, void rtw89_cam_deinit_bssid_cam(struct rtw89_dev *rtwdev, struct rtw89_bssid_cam_entry *bssid_cam); void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *vif, + struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, - const u8 *scan_mac_addr, u8 *cmd); + const u8 *scan_mac_addr, + struct rtw89_h2c_addr_cam *h2c); void rtw89_cam_fill_dctl_sec_cam_info_v1(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, @@ -565,7 +313,8 @@ void rtw89_cam_fill_dctl_sec_cam_info_v2(struct rtw89_dev *rtwdev, struct rtw89_h2c_dctlinfo_ud_v2 *h2c); int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, - struct rtw89_sta_link *rtwsta_link, u8 *cmd); + struct rtw89_sta_link *rtwsta_link, + struct rtw89_h2c_addr_cam *h2c); int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 0dc5aa5ed218..9f132a469629 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2132,28 +2132,31 @@ void rtw89_fw_log_dump(struct rtw89_dev *rtwdev, u8 *buf, u32 len) } -#define H2C_CAM_LEN 60 int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr) { + struct rtw89_h2c_addr_cam *h2c; + u32 len = sizeof(*h2c); struct sk_buff *skb; int ret; - skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_CAM_LEN); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for fw dl\n"); return -ENOMEM; } - skb_put(skb, H2C_CAM_LEN); - rtw89_cam_fill_addr_cam_info(rtwdev, rtwvif_link, rtwsta_link, scan_mac_addr, - skb->data); - rtw89_cam_fill_bssid_cam_info(rtwdev, rtwvif_link, rtwsta_link, skb->data); + skb_put(skb, len); + h2c = (struct rtw89_h2c_addr_cam *)skb->data; + + rtw89_cam_fill_addr_cam_info(rtwdev, rtwvif_link, rtwsta_link, + scan_mac_addr, h2c); + rtw89_cam_fill_bssid_cam_info(rtwdev, rtwvif_link, rtwsta_link, h2c); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_ADDR_CAM_UPDATE, H2C_FUNC_MAC_ADDR_CAM_UPD, 0, 1, - H2C_CAM_LEN); + len); ret = rtw89_h2c_tx(rtwdev, skb, false); if (ret) { From fc2e8c873f4620f07a34f1acd5697187c3ca8a8c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 14 Nov 2025 14:01:26 +0800 Subject: [PATCH 66/80] wifi: rtw89: add addr cam H2C command v1 The coming RTL8922D uses different format of address CAM command, so add the new format accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/cam.c | 39 +++++++++++++------ drivers/net/wireless/realtek/rtw89/cam.h | 11 ++++++ drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + 9 files changed, 46 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c index 8233d91024e8..5f282a02a356 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.c +++ b/drivers/net/wireless/realtek/rtw89/cam.c @@ -819,12 +819,15 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, struct rtw89_addr_cam_entry *addr_cam = rtw89_get_addr_cam_of(rtwvif_link, rtwsta_link); struct ieee80211_sta *sta = rtwsta_link_to_sta_safe(rtwsta_link); + const struct rtw89_chip_info *chip = rtwdev->chip; struct ieee80211_link_sta *link_sta; const u8 *sma = scan_mac_addr ? scan_mac_addr : rtwvif_link->mac_addr; u8 sma_hash, tma_hash, addr_msk_start; + u8 ver = chip->addrcam_ver; u8 sma_start = 0; u8 tma_start = 0; const u8 *tma; + u8 mac_id; rcu_read_lock(); @@ -845,9 +848,17 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, sma_hash = rtw89_cam_addr_hash(sma_start, sma); tma_hash = rtw89_cam_addr_hash(tma_start, tma); - h2c->w1 = le32_encode_bits(addr_cam->addr_cam_idx, ADDR_CAM_W1_IDX) | - le32_encode_bits(addr_cam->offset, ADDR_CAM_W1_OFFSET) | - le32_encode_bits(addr_cam->len, ADDR_CAM_W1_LEN); + mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id; + + if (ver == 0) + h2c->w1 = le32_encode_bits(addr_cam->addr_cam_idx, ADDR_CAM_W1_IDX) | + le32_encode_bits(addr_cam->offset, ADDR_CAM_W1_OFFSET) | + le32_encode_bits(addr_cam->len, ADDR_CAM_W1_LEN); + else + h2c->w1 = le32_encode_bits(addr_cam->addr_cam_idx, ADDR_CAM_W1_V1_IDX) | + le32_encode_bits(addr_cam->offset, ADDR_CAM_W1_V1_OFFSET) | + le32_encode_bits(addr_cam->len, ADDR_CAM_W1_V1_LEN); + h2c->w2 = le32_encode_bits(addr_cam->valid, ADDR_CAM_W2_VALID) | le32_encode_bits(rtwvif_link->net_type, ADDR_CAM_W2_NET_TYPE) | le32_encode_bits(rtwvif_link->bcn_hit_cond, ADDR_CAM_W2_BCN_HIT_COND) | @@ -870,14 +881,20 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, le32_encode_bits(tma[3], ADDR_CAM_W6_TMA3) | le32_encode_bits(tma[4], ADDR_CAM_W6_TMA4) | le32_encode_bits(tma[5], ADDR_CAM_W6_TMA5); - h2c->w8 = le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_PORT_INT) | - le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_TSF_SYNC) | - le32_encode_bits(rtwvif_link->trigger, ADDR_CAM_W8_TF_TRS) | - le32_encode_bits(rtwvif_link->lsig_txop, ADDR_CAM_W8_LSIG_TXOP) | - le32_encode_bits(rtwvif_link->tgt_ind, ADDR_CAM_W8_TGT_IND) | - le32_encode_bits(rtwvif_link->frm_tgt_ind, ADDR_CAM_W8_FRM_TGT_IND) | - le32_encode_bits(rtwsta_link ? rtwsta_link->mac_id : - rtwvif_link->mac_id, ADDR_CAM_W8_MACID); + if (ver == 0) + h2c->w8 = le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_PORT_INT) | + le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_TSF_SYNC) | + le32_encode_bits(rtwvif_link->trigger, ADDR_CAM_W8_TF_TRS) | + le32_encode_bits(rtwvif_link->lsig_txop, ADDR_CAM_W8_LSIG_TXOP) | + le32_encode_bits(rtwvif_link->tgt_ind, ADDR_CAM_W8_TGT_IND) | + le32_encode_bits(rtwvif_link->frm_tgt_ind, ADDR_CAM_W8_FRM_TGT_IND) | + le32_encode_bits(mac_id, ADDR_CAM_W8_MACID); + else + h2c->w8 = le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_V1_PORT_INT) | + le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_V1_TSF_SYNC) | + le32_encode_bits(rtwvif_link->trigger, ADDR_CAM_W8_V1_TF_TRS) | + le32_encode_bits(rtwvif_link->lsig_txop, ADDR_CAM_W8_V1_LSIG_TXOP) | + le32_encode_bits(mac_id, ADDR_CAM_W8_V1_MACID); if (rtwvif_link->net_type == RTW89_NET_TYPE_INFRA) h2c->w9 = le32_encode_bits(vif->cfg.aid & 0xfff, ADDR_CAM_W9_AID12); diff --git a/drivers/net/wireless/realtek/rtw89/cam.h b/drivers/net/wireless/realtek/rtw89/cam.h index 2bc8fbf79c0b..833a956f0176 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.h +++ b/drivers/net/wireless/realtek/rtw89/cam.h @@ -33,6 +33,9 @@ struct rtw89_h2c_addr_cam { #define ADDR_CAM_W1_IDX GENMASK(7, 0) #define ADDR_CAM_W1_OFFSET GENMASK(15, 8) #define ADDR_CAM_W1_LEN GENMASK(23, 16) +#define ADDR_CAM_W1_V1_IDX GENMASK(9, 0) +#define ADDR_CAM_W1_V1_OFFSET GENMASK(23, 16) +#define ADDR_CAM_W1_V1_LEN GENMASK(31, 24) #define ADDR_CAM_W2_VALID BIT(0) #define ADDR_CAM_W2_NET_TYPE GENMASK(2, 1) #define ADDR_CAM_W2_BCN_HIT_COND GENMASK(4, 3) @@ -62,6 +65,14 @@ struct rtw89_h2c_addr_cam { #define ADDR_CAM_W8_LSIG_TXOP BIT(15) #define ADDR_CAM_W8_TGT_IND GENMASK(26, 24) #define ADDR_CAM_W8_FRM_TGT_IND GENMASK(29, 27) +#define ADDR_CAM_W8_V1_MACID GENMASK(9, 0) +#define ADDR_CAM_W8_V1_PORT_INT GENMASK(18, 16) +#define ADDR_CAM_W8_V1_TSF_SYNC GENMASK(21, 19) +#define ADDR_CAM_W8_V1_TF_TRS BIT(22) +#define ADDR_CAM_W8_V1_LSIG_TXOP BIT(23) +#define ADDR_CAM_W8_V1_TB_RANGING BIT(24) +#define ADDR_CAM_W8_V1_TB_SENSING BIT(25) +#define ADDR_CAM_W8_V1_SENS_EN BIT(26) #define ADDR_CAM_W9_AID12 GENMASK(11, 0) #define ADDR_CAM_W9_AID12_0 GENMASK(7, 0) #define ADDR_CAM_W9_AID12_1 GENMASK(11, 8) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 1f99d5f264d2..c33ff7c0090f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4458,6 +4458,7 @@ struct rtw89_chip_info { u8 bacam_num; u8 bacam_dynamic_num; enum rtw89_bacam_ver bacam_ver; + u8 addrcam_ver; u8 ppdu_max_usr; u8 sec_ctrl_efuse_size; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 2019f6022cbb..84b628d23882 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2648,6 +2648,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .bacam_num = 2, .bacam_dynamic_num = 4, .bacam_ver = RTW89_BACAM_V0, + .addrcam_ver = 0, .ppdu_max_usr = 4, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 48205aa4a980..8677723e3561 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2334,6 +2334,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .bacam_num = 2, .bacam_dynamic_num = 4, .bacam_ver = RTW89_BACAM_V0, + .addrcam_ver = 0, .ppdu_max_usr = 4, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 38cd151f8c3f..70fb05bc5e98 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -959,6 +959,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .bacam_num = 2, .bacam_dynamic_num = 4, .bacam_ver = RTW89_BACAM_V0, + .addrcam_ver = 0, .ppdu_max_usr = 4, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 15ba780492d6..f956474c3b72 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -818,6 +818,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .bacam_num = 2, .bacam_dynamic_num = 4, .bacam_ver = RTW89_BACAM_V0, + .addrcam_ver = 0, .ppdu_max_usr = 4, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index ee81a6792eee..db99450e9158 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3178,6 +3178,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .bacam_num = 8, .bacam_dynamic_num = 8, .bacam_ver = RTW89_BACAM_V0_EXT, + .addrcam_ver = 0, .ppdu_max_usr = 8, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 1216, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 11e8b50a3291..4437279c554b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2931,6 +2931,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .bacam_num = 24, .bacam_dynamic_num = 8, .bacam_ver = RTW89_BACAM_V1, + .addrcam_ver = 0, .ppdu_max_usr = 16, .sec_ctrl_efuse_size = 4, .physical_efuse_size = 0x1300, From 9dab26b9fa457ee538be650ca0bddf352c059e79 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Fri, 14 Nov 2025 14:01:27 +0800 Subject: [PATCH 67/80] wifi: rtw89: update format of addr cam H2C command The addr cam H2C command is to tell firmware the addr related info. For RTL8922D and RTL8922A after firmware version 0.35.84.0, the addr cam must be updated with update mode to avoid clearing previously set fields. Update it accordingly. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/cam.c | 10 ++++--- drivers/net/wireless/realtek/rtw89/cam.h | 12 ++++++--- drivers/net/wireless/realtek/rtw89/core.c | 12 ++++++--- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/fw.c | 26 ++++++++++++++++--- drivers/net/wireless/realtek/rtw89/fw.h | 3 ++- drivers/net/wireless/realtek/rtw89/mac.c | 4 +-- drivers/net/wireless/realtek/rtw89/mac80211.c | 4 +-- drivers/net/wireless/realtek/rtw89/wow.c | 6 +++-- 9 files changed, 56 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c index 5f282a02a356..9370cbda945c 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.c +++ b/drivers/net/wireless/realtek/rtw89/cam.c @@ -236,7 +236,8 @@ static int __rtw89_cam_detach_sec_cam(struct rtw89_dev *rtwdev, if (ret) rtw89_err(rtwdev, "failed to update dctl cam del key: %d\n", ret); - ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL, + RTW89_ROLE_INFO_CHANGE); if (ret) rtw89_err(rtwdev, "failed to update cam del key: %d\n", ret); } @@ -276,7 +277,8 @@ static int __rtw89_cam_attach_sec_cam(struct rtw89_dev *rtwdev, ret); return ret; } - ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL, + RTW89_ROLE_INFO_CHANGE); if (ret) { rtw89_err(rtwdev, "failed to update addr cam sec entry: %d\n", ret); @@ -761,7 +763,7 @@ int rtw89_cam_init(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, - struct rtw89_h2c_addr_cam *h2c) + struct rtw89_h2c_addr_cam_v0 *h2c) { struct rtw89_bssid_cam_entry *bssid_cam = rtw89_get_bssid_cam_of(rtwvif_link, rtwsta_link); @@ -813,7 +815,7 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr, - struct rtw89_h2c_addr_cam *h2c) + struct rtw89_h2c_addr_cam_v0 *h2c) { struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); struct rtw89_addr_cam_entry *addr_cam = diff --git a/drivers/net/wireless/realtek/rtw89/cam.h b/drivers/net/wireless/realtek/rtw89/cam.h index 833a956f0176..c46b6f91bbdb 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.h +++ b/drivers/net/wireless/realtek/rtw89/cam.h @@ -12,7 +12,7 @@ #define RTW89_BSSID_MATCH_ALL GENMASK(5, 0) #define RTW89_BSSID_MATCH_5_BYTES GENMASK(4, 0) -struct rtw89_h2c_addr_cam { +struct rtw89_h2c_addr_cam_v0 { __le32 w0; __le32 w1; __le32 w2; @@ -30,6 +30,11 @@ struct rtw89_h2c_addr_cam { __le32 w14; } __packed; +struct rtw89_h2c_addr_cam { + struct rtw89_h2c_addr_cam_v0 v0; + __le32 w15; +} __packed; + #define ADDR_CAM_W1_IDX GENMASK(7, 0) #define ADDR_CAM_W1_OFFSET GENMASK(15, 8) #define ADDR_CAM_W1_LEN GENMASK(23, 16) @@ -109,6 +114,7 @@ struct rtw89_h2c_addr_cam { #define ADDR_CAM_W14_BSSID_BSSID3 GENMASK(15, 8) #define ADDR_CAM_W14_BSSID_BSSID4 GENMASK(23, 16) #define ADDR_CAM_W14_BSSID_BSSID5 GENMASK(31, 24) +#define ADDR_CAM_W15_UPD_MODE GENMASK(2, 0) struct rtw89_h2c_dctlinfo_ud_v1 { __le32 c0; @@ -313,7 +319,7 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr, - struct rtw89_h2c_addr_cam *h2c); + struct rtw89_h2c_addr_cam_v0 *h2c); void rtw89_cam_fill_dctl_sec_cam_info_v1(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, @@ -325,7 +331,7 @@ void rtw89_cam_fill_dctl_sec_cam_info_v2(struct rtw89_dev *rtwdev, int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, - struct rtw89_h2c_addr_cam *h2c); + struct rtw89_h2c_addr_cam_v0 *h2c); int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index ea22925b8772..a14edc321ca7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4823,7 +4823,8 @@ int rtw89_core_sta_link_disconnect(struct rtw89_dev *rtwdev, } /* update cam aid mac_id net_type */ - ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL, + RTW89_ROLE_CON_DISCONN); if (ret) { rtw89_warn(rtwdev, "failed to send h2c cam\n"); return ret; @@ -4897,7 +4898,8 @@ int rtw89_core_sta_link_assoc(struct rtw89_dev *rtwdev, } /* update cam aid mac_id net_type */ - ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL, + RTW89_ROLE_CON_DISCONN); if (ret) { rtw89_warn(rtwdev, "failed to send h2c cam\n"); return ret; @@ -6029,7 +6031,8 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv rtw89_phy_config_edcca(rtwdev, bb, true); rtw89_tas_scan(rtwdev, true); - rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr); + rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr, + RTW89_ROLE_INFO_CHANGE); } void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, @@ -6049,7 +6052,8 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, rcu_read_unlock(); - rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL); + rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, + RTW89_ROLE_INFO_CHANGE); rtw89_chip_rfk_scan(rtwdev, rtwvif_link, false); rtw89_btc_ntfy_scan_finish(rtwdev, rtwvif_link->phy_idx); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c33ff7c0090f..ab71489b89a3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4678,6 +4678,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_RFK_NTFY_MCC_V0, RTW89_FW_FEATURE_LPS_DACK_BY_C2H_REG, RTW89_FW_FEATURE_BEACON_TRACKING, + RTW89_FW_FEATURE_ADDR_CAM_V0, }; struct rtw89_fw_suit { diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 9f132a469629..18eab404885c 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -881,6 +881,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 76, 0, LPS_DACK_BY_C2H_REG), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 79, 0, CRASH_TRIGGER_TYPE_1), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 80, 0, BEACON_TRACKING), + __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, @@ -2133,25 +2134,42 @@ void rtw89_fw_log_dump(struct rtw89_dev *rtwdev, u8 *buf, u32 len) } int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, - struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr) + struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr, + enum rtw89_upd_mode upd_mode) { + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_addr_cam_v0 *h2c_v0; struct rtw89_h2c_addr_cam *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; + u8 ver = U8_MAX; int ret; + if (RTW89_CHK_FW_FEATURE(ADDR_CAM_V0, &rtwdev->fw) || + chip->chip_gen == RTW89_CHIP_AX) { + len = sizeof(*h2c_v0); + ver = 0; + } + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for fw dl\n"); return -ENOMEM; } skb_put(skb, len); - h2c = (struct rtw89_h2c_addr_cam *)skb->data; + h2c_v0 = (struct rtw89_h2c_addr_cam_v0 *)skb->data; rtw89_cam_fill_addr_cam_info(rtwdev, rtwvif_link, rtwsta_link, - scan_mac_addr, h2c); - rtw89_cam_fill_bssid_cam_info(rtwdev, rtwvif_link, rtwsta_link, h2c); + scan_mac_addr, h2c_v0); + rtw89_cam_fill_bssid_cam_info(rtwdev, rtwvif_link, rtwsta_link, h2c_v0); + if (ver == 0) + goto hdr; + + h2c = (struct rtw89_h2c_addr_cam *)skb->data; + h2c->w15 = le32_encode_bits(upd_mode, ADDR_CAM_W15_UPD_MODE); + +hdr: rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_ADDR_CAM_UPDATE, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index e62b61f584fb..cedb4a47a769 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4887,7 +4887,8 @@ int rtw89_fw_h2c_tbtt_tuning(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, u32 offset); int rtw89_fw_h2c_pwr_lvl(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif_link *vif, - struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr); + struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr, + enum rtw89_upd_mode upd_mode); int rtw89_fw_h2c_dctl_sec_cam_v1(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link); diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 386cc982d7c9..bde3791dd330 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4835,7 +4835,7 @@ int rtw89_mac_vif_init(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l if (ret) return ret; - ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_CREATE); if (ret) return ret; @@ -4860,7 +4860,7 @@ int rtw89_mac_vif_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif rtw89_cam_deinit(rtwdev, rtwvif_link); - ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_REMOVE); if (ret) return ret; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 4a7364432428..82598673ddec 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -759,7 +759,7 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BSSID) { ether_addr_copy(rtwvif_link->bssid, conf->bssid); rtw89_cam_bssid_changed(rtwdev, rtwvif_link); - rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL); + rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_INFO_CHANGE); WRITE_ONCE(rtwvif_link->sync_bcn_tsf, 0); } @@ -818,7 +818,7 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL); rtw89_fw_h2c_role_maintain(rtwdev, rtwvif_link, NULL, RTW89_ROLE_TYPE_CHANGE); rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true); - rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL); + rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_TYPE_CHANGE); rtw89_chip_rfk_channel(rtwdev, rtwvif_link); if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) { diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index b9fce0be3ab7..46aba4cb2ee9 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -1221,7 +1221,8 @@ static int rtw89_wow_cfg_wake(struct rtw89_dev *rtwdev, bool wow) } } - ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL, + RTW89_ROLE_INFO_CHANGE); if (ret) { rtw89_warn(rtwdev, "failed to send h2c cam\n"); return ret; @@ -1318,7 +1319,8 @@ static int rtw89_wow_swap_fw(struct rtw89_dev *rtwdev, bool wow) return ret; } - ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL); + ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL, + RTW89_ROLE_FW_RESTORE); if (ret) { rtw89_warn(rtwdev, "failed to send h2c cam\n"); return ret; From 09afd209a84421948ac9bf2076459a0418e102ec Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Fri, 14 Nov 2025 14:01:28 +0800 Subject: [PATCH 68/80] wifi: rtw89: correct user macid mask of RX info for RTL8922D The user MAC ID mask of RX info in MAC PPDU for RTL8922A and RTL8922D is different, correct it accordingly. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114060128.35363-15-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 6 +++++- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/txrx.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a14edc321ca7..8078c9aa0ac8 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1834,9 +1834,13 @@ static int rtw89_core_rx_process_mac_ppdu(struct rtw89_dev *rtwdev, /* For WiFi 7 chips, RXWD.mac_id of PPDU status is not set * by hardware, so update mac_id by rxinfo_user[].mac_id. */ - if (chip_gen == RTW89_CHIP_BE) + if (chip->chip_id == RTL8922A) phy_ppdu->mac_id = le32_get_bits(user->w0, RTW89_RXINFO_USER_MACID); + else if (chip->chip_id == RTL8922D) + phy_ppdu->mac_id = + le32_get_bits(user->w0, RTW89_RXINFO_USER_MACID_V1); + phy_ppdu->has_data = le32_get_bits(user->w0, RTW89_RXINFO_USER_DATA); phy_ppdu->has_bcn = diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ab71489b89a3..a9cb47ea0b93 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -155,6 +155,7 @@ enum rtw89_core_chip_id { RTL8852C, RTL8851B, RTL8922A, + RTL8922D, }; enum rtw89_chip_gen { diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index b37dbac7b790..fa324b4a1dde 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -421,6 +421,7 @@ struct rtw89_rxinfo_user { #define RTW89_RXINFO_USER_MGMT BIT(3) #define RTW89_RXINFO_USER_BCN BIT(4) #define RTW89_RXINFO_USER_MACID GENMASK(15, 8) +#define RTW89_RXINFO_USER_MACID_V1 GENMASK(31, 20) struct rtw89_rxinfo { __le32 w0; From c8d212fa81c5e0cad15655918b5fb8fcf6e6fe57 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 13 Nov 2025 13:34:59 +0800 Subject: [PATCH 69/80] wifi: rtw89: use separated function to set RX filter Normally use rtw89_write32_mask() with a consecutive bit mask, but mask of RX filter B_AX_RX_FLTR_CFG_MASK is bits 31-22 and 15-0, which excludes bits of B_AX_RX_MPDU_MAX_LEN_MASK (bits 21-16). Though the original logic is well to set RX filter, change it to a separate function to avoid the tricky design. Cc: Geert Uytterhoeven Signed-off-by: Ping-Ke Shih Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20251113053459.34995-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 5 +---- drivers/net/wireless/realtek/rtw89/fw.c | 10 ++-------- drivers/net/wireless/realtek/rtw89/mac.c | 14 ++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 11 ++--------- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 8078c9aa0ac8..0824940c91ae 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4173,12 +4173,10 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { - const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; struct ieee80211_hw *hw = rtwdev->hw; struct rtw89_roc *roc = &rtwvif->roc; struct rtw89_vif_link *rtwvif_link; struct rtw89_vif *tmp_vif; - u32 reg; int ret; lockdep_assert_wiphy(hw->wiphy); @@ -4195,8 +4193,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) return; } - reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); - rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr); + rtw89_mac_set_rx_fltr(rtwdev, rtwvif_link->mac_idx, rtwdev->hal.rx_fltr); roc->state = RTW89_ROC_IDLE; rtw89_config_roc_chandef(rtwdev, rtwvif_link, NULL); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 18eab404885c..d50c35328363 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -8103,7 +8103,6 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct ieee80211_scan_request *scan_req) { - const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev); struct cfg80211_scan_request *req = &scan_req->req; const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, @@ -8115,7 +8114,6 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, }; u32 rx_fltr = rtwdev->hal.rx_fltr; u8 mac_addr[ETH_ALEN]; - u32 reg; int ret; /* clone op and keep it during scan */ @@ -8155,8 +8153,7 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, rx_fltr &= ~B_AX_A_BC; rx_fltr &= ~B_AX_A_A1_MATCH; - reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); - rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rx_fltr); + rtw89_mac_set_rx_fltr(rtwdev, rtwvif_link->mac_idx, rx_fltr); rtw89_chanctx_pause(rtwdev, &pause_parm); rtw89_phy_dig_suspend(rtwdev); @@ -8174,20 +8171,17 @@ struct rtw89_hw_scan_complete_cb_data { static int rtw89_hw_scan_complete_cb(struct rtw89_dev *rtwdev, void *data) { - const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev); struct rtw89_hw_scan_complete_cb_data *cb_data = data; struct rtw89_vif_link *rtwvif_link = cb_data->rtwvif_link; struct cfg80211_scan_info info = { .aborted = cb_data->aborted, }; - u32 reg; if (!rtwvif_link) return -EINVAL; - reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); - rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr); + rtw89_mac_set_rx_fltr(rtwdev, rtwvif_link->mac_idx, rtwdev->hal.rx_fltr); rtw89_core_scan_complete(rtwdev, rtwvif_link, true); ieee80211_scan_completed(rtwdev->hw, &info); diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index bde3791dd330..d78fbe73e365 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -2543,6 +2543,20 @@ static int rtw89_mac_typ_fltr_opt_ax(struct rtw89_dev *rtwdev, return 0; } +void rtw89_mac_set_rx_fltr(struct rtw89_dev *rtwdev, u8 mac_idx, u32 rx_fltr) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + u32 reg; + u32 val; + + reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, mac_idx); + + val = rtw89_read32(rtwdev, reg); + /* B_AX_RX_FLTR_CFG_MASK is not a consecutive bit mask */ + val = (val & ~B_AX_RX_FLTR_CFG_MASK) | (rx_fltr & B_AX_RX_FLTR_CFG_MASK); + rtw89_write32(rtwdev, reg, val); +} + static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) { int ret, i; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index d203db444a34..0007229d6753 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1340,6 +1340,7 @@ int rtw89_mac_cfg_ppdu_status_bands(struct rtw89_dev *rtwdev, bool enable) return rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_1, enable); } +void rtw89_mac_set_rx_fltr(struct rtw89_dev *rtwdev, u8 mac_idx, u32 rx_fltr); void rtw89_mac_update_rts_threshold(struct rtw89_dev *rtwdev); void rtw89_mac_flush_txq(struct rtw89_dev *rtwdev, u32 queues, bool drop); int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex *coex); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 82598673ddec..f39ca1c2ed10 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -307,7 +307,6 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, u64 multicast) { struct rtw89_dev *rtwdev = hw->priv; - const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u32 rx_fltr; lockdep_assert_wiphy(hw->wiphy); @@ -369,16 +368,10 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, rx_fltr &= ~B_AX_A_A1_MATCH; } - rtw89_write32_mask(rtwdev, - rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_0), - B_AX_RX_FLTR_CFG_MASK, - rx_fltr); + rtw89_mac_set_rx_fltr(rtwdev, RTW89_MAC_0, rx_fltr); if (!rtwdev->dbcc_en) return; - rtw89_write32_mask(rtwdev, - rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_1), - B_AX_RX_FLTR_CFG_MASK, - rx_fltr); + rtw89_mac_set_rx_fltr(rtwdev, RTW89_MAC_1, rx_fltr); } static const u8 ac_to_fw_idx[IEEE80211_NUM_ACS] = { From 9b5b9c042b30befc5b37e4539ace95af70843473 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Fri, 14 Nov 2025 15:15:26 +0530 Subject: [PATCH 70/80] wifi: rtl818x: Fix potential memory leaks in rtl8180_init_rx_ring() In rtl8180_init_rx_ring(), memory is allocated for skb packets and DMA allocations in a loop. When an allocation fails, the previously successful allocations are not freed on exit. Fix that by jumping to err_free_rings label on error, which calls rtl8180_free_rx_ring() to free the allocations. Remove the free of rx_ring in rtl8180_init_rx_ring() error path, and set the freed priv->rx_buf entry to null, to avoid double free. Fixes: f653211197f3 ("Add rtl8180 wireless driver") Signed-off-by: Abdun Nihaal Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251114094527.79842-1-nihaal@cse.iitm.ac.in --- drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c index 2905baea6239..070c0431c482 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c @@ -1023,9 +1023,6 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) dma_addr_t *mapping; entry = priv->rx_ring + priv->rx_ring_sz*i; if (!skb) { - dma_free_coherent(&priv->pdev->dev, - priv->rx_ring_sz * 32, - priv->rx_ring, priv->rx_ring_dma); wiphy_err(dev->wiphy, "Cannot allocate RX skb\n"); return -ENOMEM; } @@ -1037,9 +1034,7 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) if (dma_mapping_error(&priv->pdev->dev, *mapping)) { kfree_skb(skb); - dma_free_coherent(&priv->pdev->dev, - priv->rx_ring_sz * 32, - priv->rx_ring, priv->rx_ring_dma); + priv->rx_buf[i] = NULL; wiphy_err(dev->wiphy, "Cannot map DMA for RX skb\n"); return -ENOMEM; } @@ -1130,7 +1125,7 @@ static int rtl8180_start(struct ieee80211_hw *dev) ret = rtl8180_init_rx_ring(dev); if (ret) - return ret; + goto err_free_rings; for (i = 0; i < (dev->queues + 1); i++) if ((ret = rtl8180_init_tx_ring(dev, i, 16))) From c453d9c5bb8aa1510078a53eef37ea437c21ab07 Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Mon, 17 Nov 2025 07:44:03 +0800 Subject: [PATCH 71/80] wifi: rtw89: Add default ID 0bda:b831 for RTL8831BU Add 0bda:b831 for RTL8831BU-based adapters that use this default ID. Tested with TOTOLINK X900USM in station mode very briefly. Signed-off-by: Zenm Chen Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251116234403.8803-1-zenmchen@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8851bu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index b8799a37add3..959d62aefdd8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -36,6 +36,8 @@ static const struct rtw89_driver_info rtw89_8851bu_info = { }; static const struct usb_device_id rtw_8851bu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb831, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb851, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, /* D-Link AX9U rev. A1 */ From b647d2574e4583c2e3b0ab35568f60c88e910840 Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 17 Nov 2025 20:32:59 -0500 Subject: [PATCH 72/80] wifi: rtl818x: rtl8187: Fix potential buffer underflow in rtl8187_rx_cb() The rtl8187_rx_cb() calculates the rx descriptor header address by subtracting its size from the skb tail pointer. However, it does not validate if the received packet (skb->len from urb->actual_length) is large enough to contain this header. If a truncated packet is received, this will lead to a buffer underflow, reading memory before the start of the skb data area, and causing a kernel panic. Add length checks for both rtl8187 and rtl8187b descriptor headers before attempting to access them, dropping the packet cleanly if the check fails. Fixes: 6f7853f3cbe4 ("rtl8187: change rtl8187_dev.c to support RTL8187B (part 2)") Signed-off-by: Seungjin Bae Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251118013258.1789949-2-eeodqql09@gmail.com --- .../wireless/realtek/rtl818x/rtl8187/dev.c | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 0c5c66401daa..7aa2da0cd63c 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -338,14 +338,16 @@ static void rtl8187_rx_cb(struct urb *urb) spin_unlock_irqrestore(&priv->rx_queue.lock, f); skb_put(skb, urb->actual_length); - if (unlikely(urb->status)) { - dev_kfree_skb_irq(skb); - return; - } + if (unlikely(urb->status)) + goto free_skb; if (!priv->is_rtl8187b) { - struct rtl8187_rx_hdr *hdr = - (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); + struct rtl8187_rx_hdr *hdr; + + if (skb->len < sizeof(struct rtl8187_rx_hdr)) + goto free_skb; + + hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); flags = le32_to_cpu(hdr->flags); /* As with the RTL8187B below, the AGC is used to calculate * signal strength. In this case, the scaling @@ -355,8 +357,12 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.antenna = (hdr->signal >> 7) & 1; rx_status.mactime = le64_to_cpu(hdr->mac_time); } else { - struct rtl8187b_rx_hdr *hdr = - (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); + struct rtl8187b_rx_hdr *hdr; + + if (skb->len < sizeof(struct rtl8187b_rx_hdr)) + goto free_skb; + + hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); /* The Realtek datasheet for the RTL8187B shows that the RX * header contains the following quantities: signal quality, * RSSI, AGC, the received power in dB, and the measured SNR. @@ -409,6 +415,11 @@ static void rtl8187_rx_cb(struct urb *urb) skb_unlink(skb, &priv->rx_queue); dev_kfree_skb_irq(skb); } + return; + +free_skb: + dev_kfree_skb_irq(skb); + return; } static int rtl8187_init_urbs(struct ieee80211_hw *dev) From 7cbec00dc731e9ddb3cdb350d05cbc26d45b3273 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Tue, 18 Nov 2025 11:20:31 +0100 Subject: [PATCH 73/80] wifi: rtlwifi: add WQ_UNBOUND to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. For more details see the Link tag below. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") This change adds the WQ_UNBOUND flag to explicitly request alloc_workqueue() to be unbound, because this specific workload has no benefit being per-cpu. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://lore.kernel.org/all/20250221112003.1dSuoGyc@linutronix.de/ Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251118102032.54375-2-marco.crivellari@suse.com --- drivers/net/wireless/realtek/rtlwifi/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index e26feb8de658..09e5a16d7252 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -445,7 +445,7 @@ static int _rtl_init_deferred_work(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct workqueue_struct *wq; - wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name); + wq = alloc_workqueue("%s", WQ_UNBOUND, 0, rtlpriv->cfg->name); if (!wq) return -ENOMEM; From 9c194fe4625db18f93d5abcfb7f7997557a0b29d Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Tue, 18 Nov 2025 11:20:32 +0100 Subject: [PATCH 74/80] wifi: rtw88: add WQ_UNBOUND to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueues a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. For more details see the Link tag below. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") This change adds the WQ_UNBOUND flag to explicitly request alloc_workqueue() to be unbound, because this specific workload has no benefit being per-cpu. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://lore.kernel.org/all/20250221112003.1dSuoGyc@linutronix.de/ Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251118102032.54375-3-marco.crivellari@suse.com --- drivers/net/wireless/realtek/rtw88/usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 3b5126ffc81a..009202c627d2 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -965,7 +965,8 @@ static int rtw_usb_init_rx(struct rtw_dev *rtwdev) struct sk_buff *rx_skb; int i; - rtwusb->rxwq = alloc_workqueue("rtw88_usb: rx wq", WQ_BH, 0); + rtwusb->rxwq = alloc_workqueue("rtw88_usb: rx wq", WQ_BH | WQ_UNBOUND, + 0); if (!rtwusb->rxwq) { rtw_err(rtwdev, "failed to create RX work queue\n"); return -ENOMEM; From 2a2aae3655343b753dfef86b341be99f7273f2ba Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 20 Nov 2025 11:10:43 +0800 Subject: [PATCH 75/80] wifi: rtw89: 8852a: correct field mask of reset DAC/ADC FIFO The field mask should be bits 16-31, but suddenly use wrong bits 24-31, rarely causing a little performance degraded if DAC/DAC FIFO stays on an unexpected state. Found this by Geert who works on bit field functions. Reported-by: Geert Uytterhoeven Closes: https://lore.kernel.org/linux-wireless/CAMuHMdVt+5yOA6tuasX4KQgZud5wtRwu0A15UkEfQJbcd_xvVw@mail.gmail.com/ Cc: Geert Uytterhoeven Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20251120031044.12493-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c index e74257d19412..463399413318 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c @@ -756,8 +756,8 @@ static void _iqk_rxk_setting(struct rtw89_dev *rtwdev, u8 path) rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_FLTRST, 0x1); rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15_H2, 0x0); udelay(1); - rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0303); - rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0000); + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0303); + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0000); switch (iqk_info->iqk_band[path]) { case RTW89_BAND_2G: @@ -1239,8 +1239,8 @@ static void _iqk_txk_setting(struct rtw89_dev *rtwdev, u8 path) udelay(1); rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0x0041); udelay(1); - rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0303); - rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0000); + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0303); + rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0000); switch (iqk_info->iqk_band[path]) { case RTW89_BAND_2G: rtw89_write_rf(rtwdev, path, RR_XALNA2, RR_XALNA2_SW, 0x00); From 5511ba3de434892e5ef3594d6eabbd12b1629356 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 20 Nov 2025 16:10:01 +0200 Subject: [PATCH 76/80] wifi: rtl8xxxu: Fix HT40 channel config for RTL8192CU, RTL8723AU Flip the response rate subchannel. It was backwards, causing low speeds when using 40 MHz channel width. "iw dev ... station dump" showed a low RX rate, 11M or less. Also fix the channel width field of RF6052_REG_MODE_AG. Tested only with RTL8192CU, but these settings are identical for RTL8723AU. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/1f46571d-855b-43e1-8bfc-abacceb96043@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index d82cb7714314..be8ee6c30034 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -1252,7 +1252,7 @@ void rtl8xxxu_gen1_config_channel(struct ieee80211_hw *hw) opmode &= ~BW_OPMODE_20MHZ; rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode); rsr &= ~RSR_RSC_BANDWIDTH_40M; - if (sec_ch_above) + if (!sec_ch_above) rsr |= RSR_RSC_UPPER_SUB_CHANNEL; else rsr |= RSR_RSC_LOWER_SUB_CHANNEL; @@ -1321,9 +1321,8 @@ void rtl8xxxu_gen1_config_channel(struct ieee80211_hw *hw) for (i = RF_A; i < priv->rf_paths; i++) { val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG); - if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40) - val32 &= ~MODE_AG_CHANNEL_20MHZ; - else + val32 &= ~MODE_AG_BW_MASK; + if (hw->conf.chandef.width != NL80211_CHAN_WIDTH_40) val32 |= MODE_AG_CHANNEL_20MHZ; rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32); } From bdb4c850c368d4009a0b109b6135042540aca749 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 20 Nov 2025 16:11:58 +0200 Subject: [PATCH 77/80] wifi: rtl8xxxu: Make RTL8192CU, RTL8723AU TX with 40 MHz width Set the required fields in the TX descriptor to allow these chips to transmit with 40 MHz channel width when the access point supports it. Tested only with RTL8192CU, but these settings are identical for RTL8723AU. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/30d95228-69b2-48f9-8854-c98d2408c4d3@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index be8ee6c30034..7700e4074dc3 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -5221,9 +5221,19 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, tx_desc->txdw5 |= cpu_to_le32(TXDESC32_RETRY_LIMIT_ENABLE); } - if (ieee80211_is_data_qos(hdr->frame_control)) + if (ieee80211_is_data_qos(hdr->frame_control)) { tx_desc->txdw4 |= cpu_to_le32(TXDESC32_QOS); + if (conf_is_ht40(&hw->conf)) { + tx_desc->txdw4 |= cpu_to_le32(TXDESC_DATA_BW); + + if (conf_is_ht40_minus(&hw->conf)) + tx_desc->txdw4 |= cpu_to_le32(TXDESC_PRIME_CH_OFF_UPPER); + else + tx_desc->txdw4 |= cpu_to_le32(TXDESC_PRIME_CH_OFF_LOWER); + } + } + if (short_preamble) tx_desc->txdw4 |= cpu_to_le32(TXDESC32_SHORT_PREAMBLE); From fc44314a377ace14c76b7502cf0d785eb35e5cc4 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 20 Nov 2025 16:12:35 +0200 Subject: [PATCH 78/80] wifi: rtl8xxxu: Fix the 40 MHz subchannel for RTL8192EU, RTL8723BU rtl8xxxu_gen2_config_channel() was missing the subchannel setting. This function is used by RTL8192EU and RTL8723BU. This change seems to make no difference in my testing on channel 13 with either chip. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/a5de8d39-45c1-4667-ab4c-7109de6eb13d@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 7700e4074dc3..739456640ca8 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -1373,9 +1373,11 @@ void rtl8xxxu_gen2_config_channel(struct ieee80211_hw *hw) hw->conf.chandef.chan->center_freq) { sec_ch_above = 1; channel += 2; + subchannel = 2; } else { sec_ch_above = 0; channel -= 2; + subchannel = 1; } val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); From 41a21d0ff3a0b05de78f9cbeb29945d0eb18102a Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 20 Nov 2025 16:13:17 +0200 Subject: [PATCH 79/80] wifi: rtl8xxxu: Fix RX channel width reported by RTL8192FU The other chips report the RX channel width in the RX descriptor, but this one doesn't. Get the RX channel width from the PHY status. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/1c6c1fd4-92f6-4327-a24e-f0747ab21819@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 739456640ca8..a18f73aea837 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -5701,7 +5701,7 @@ static void jaguar2_rx_parse_phystats_type1(struct rtl8xxxu_priv *priv, !rtl8xxxu_is_sta_sta(priv) && (rtl8xxxu_is_packet_match_bssid(priv, hdr, 0) || rtl8xxxu_is_packet_match_bssid(priv, hdr, 1)); - u8 pwdb_max = 0; + u8 pwdb_max = 0, rxsc; int rx_path; if (parse_cfo) { @@ -5716,6 +5716,16 @@ static void jaguar2_rx_parse_phystats_type1(struct rtl8xxxu_priv *priv, pwdb_max = max(pwdb_max, phy_stats1->pwdb[rx_path]); rx_status->signal = pwdb_max - 110; + + if (rxmcs >= DESC_RATE_6M && rxmcs <= DESC_RATE_54M) + rxsc = phy_stats1->l_rxsc; + else + rxsc = phy_stats1->ht_rxsc; + + if (phy_stats1->rf_mode == 0 || rxsc == 1 || rxsc == 2) + rx_status->bw = RATE_INFO_BW_20; + else + rx_status->bw = RATE_INFO_BW_40; } static void jaguar2_rx_parse_phystats_type2(struct rtl8xxxu_priv *priv, From dbf9b7bb0edfa192d43ebb41dd0e1041f8a9c5b0 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 20 Nov 2025 16:13:49 +0200 Subject: [PATCH 80/80] wifi: rtl8xxxu: Enable 40 MHz width by default 40 MHz support is hidden behind the ht40_2g module parameter with this comment: /* * Some APs will negotiate HT20_40 in a noisy environment leading * to miserable performance. Rather than defaulting to this, only * enable it if explicitly requested at module load time. */ This parameter was added in commit 26f1fad29ad9 ("New driver: rtl8xxxu (mac80211)"). Back then rtl8xxxu only supported RTL8723AU and the RTL8192CU family. It's entirely possible the miserable performance was due to mistakes in the channel switching function, which were fixed in a previous patch. Delete the ht40_2g module parameter. If someone still needs to disable 40 MHz support, cfg80211 has the module parameter cfg80211_disable_40mhz_24ghz. That works too. Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/4f053103-adfd-4ead-acb3-ef69127a4bab@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index a18f73aea837..c06ad064f37c 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -20,7 +20,6 @@ #define DRIVER_NAME "rtl8xxxu" int rtl8xxxu_debug; -static bool rtl8xxxu_ht40_2g; static bool rtl8xxxu_dma_aggregation; static int rtl8xxxu_dma_agg_timeout = -1; static int rtl8xxxu_dma_agg_pages = -1; @@ -45,8 +44,6 @@ MODULE_FIRMWARE("rtlwifi/rtl8192fufw.bin"); module_param_named(debug, rtl8xxxu_debug, int, 0600); MODULE_PARM_DESC(debug, "Set debug mask"); -module_param_named(ht40_2g, rtl8xxxu_ht40_2g, bool, 0600); -MODULE_PARM_DESC(ht40_2g, "Enable HT40 support on the 2.4GHz band"); module_param_named(dma_aggregation, rtl8xxxu_dma_aggregation, bool, 0600); MODULE_PARM_DESC(dma_aggregation, "Enable DMA packet aggregation"); module_param_named(dma_agg_timeout, rtl8xxxu_dma_agg_timeout, int, 0600); @@ -4896,8 +4893,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, sgi = 1; highest_rate = fls(ramask) - 1; - if (rtl8xxxu_ht40_2g && - (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) bw = RATE_INFO_BW_40; else bw = RATE_INFO_BW_20; @@ -7851,7 +7847,8 @@ 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; + sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_SUP_WIDTH_20_40; 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; @@ -7860,15 +7857,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; } sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - /* - * Some APs will negotiate HT20_40 in a noisy environment leading - * to miserable performance. Rather than defaulting to this, only - * enable it if explicitly requested at module load time. - */ - if (rtl8xxxu_ht40_2g) { - dev_info(&udev->dev, "Enabling HT_20_40 on the 2.4GHz band\n"); - sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } + hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; hw->wiphy->rts_threshold = 2347;