From 7068c379cf9aa8afe4dce4d9d82390187aa9c4d0 Mon Sep 17 00:00:00 2001 From: Christos Longros Date: Sun, 29 Mar 2026 09:38:57 +0200 Subject: [PATCH 001/285] wifi: rtw89: fix wrong pci_get_drvdata type in AER handlers rtw89 stores an ieee80211_hw pointer via pci_set_drvdata() at probe time, but io_error_detected() and io_resume() retrieve it as a net_device pointer. This causes netif_device_detach/attach to operate on an ieee80211_hw struct, reading and writing at wrong offsets. The adjacent io_slot_reset() already does it correctly. Use ieee80211_stop_queues/wake_queues instead, consistent with every other queue stop/start path in the driver. Tested on RTL8852CE by calling the handlers from a test module before and after the fix. Fixes: 16e3d93c6183 ("wifi: rtw89: pci: add PCI Express error handling") Signed-off-by: Christos Longros Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260329073857.113081-1-chris.longros@gmail.com --- drivers/net/wireless/realtek/rtw89/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 43c61b3dc969..64554eb35a72 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4624,9 +4624,9 @@ EXPORT_SYMBOL(rtw89_pm_ops); static pci_ers_result_t rtw89_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -4643,12 +4643,12 @@ static pci_ers_result_t rtw89_pci_io_slot_reset(struct pci_dev *pdev) static void rtw89_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw89_pci_err_handler = { From d603f2cfa623e9446b44d0c9bcc2cdd92cbda594 Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Tue, 7 Apr 2026 05:34:27 -0700 Subject: [PATCH 002/285] wifi: rtlwifi: rtl8821ae: Remove dead code in rtl8821ae_update_hal_rate_table() The variable 'mimo_ps' is initialized to IEEE80211_SMPS_OFF and never modified throughout the function. This makes the condition checking for IEEE80211_SMPS_STATIC always evaluate to false, rendering the entire if-branch unreachable dead code. The error was reported by Coverity Scan. Signed-off-by: Chelsy Ratnawat Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260407123427.14612-1-chelsyratnawat2001@gmail.com --- .../wireless/realtek/rtlwifi/rtl8821ae/hw.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 3a4a33476205..c4dcae2d375e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -3257,9 +3257,9 @@ static void rtl8821ae_update_hal_rate_table(struct ieee80211_hw *hw, u32 ratr_value; u8 ratr_index = 0; u8 b_nmode = mac->ht_enable; - u8 mimo_ps = IEEE80211_SMPS_OFF; u16 shortgi_rate; u32 tmp_ratr_value; + u32 ratr_mask; u8 curtxbw_40mhz = mac->bw_40; u8 b_curshortgi_40mhz = (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; @@ -3288,19 +3288,14 @@ static void rtl8821ae_update_hal_rate_table(struct ieee80211_hw *hw, case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: b_nmode = 1; - if (mimo_ps == IEEE80211_SMPS_STATIC) { - ratr_value &= 0x0007F005; - } else { - u32 ratr_mask; - if (get_rf_type(rtlphy) == RF_1T2R || - get_rf_type(rtlphy) == RF_1T1R) - ratr_mask = 0x000ff005; - else - ratr_mask = 0x0f0ff005; + if (get_rf_type(rtlphy) == RF_1T2R || + get_rf_type(rtlphy) == RF_1T1R) + ratr_mask = 0x000ff005; + else + ratr_mask = 0x0f0ff005; - ratr_value &= ratr_mask; - } + ratr_value &= ratr_mask; break; default: if (rtlphy->rf_type == RF_1T2R) From 706183dbef4a79d120d4e928f693bea50df496f8 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Mon, 13 Apr 2026 14:59:26 +0800 Subject: [PATCH 003/285] wifi: rtw88: fix wrong pci_get_drvdata type in AER handlers rtw88 stores an ieee80211_hw pointer via pci_set_drvdata() at probe time, but io_error_detected() and io_resume() retrieve it as a net_device pointer. This causes netif_device_detach/attach to operate on an ieee80211_hw struct, reading and writing at wrong offsets. Use ieee80211_stop_queues/wake_queues instead, consistent with every other queue stop/start path in the driver. Fixes: cdb82c80b934 ("wifi: rtw88: pci: add PCI Express error handling") Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260413065926.17027-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index bba370ad510c..c56beacbb1b0 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1711,9 +1711,9 @@ static void rtw_pci_napi_deinit(struct rtw_dev *rtwdev) static pci_ers_result_t rtw_pci_io_err_detected(struct pci_dev *pdev, pci_channel_state_t state) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); - netif_device_detach(netdev); + ieee80211_stop_queues(hw); return PCI_ERS_RESULT_NEED_RESET; } @@ -1730,12 +1730,12 @@ static pci_ers_result_t rtw_pci_io_slot_reset(struct pci_dev *pdev) static void rtw_pci_io_resume(struct pci_dev *pdev) { - struct net_device *netdev = pci_get_drvdata(pdev); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); /* ack any pending wake events, disable PME */ pci_enable_wake(pdev, PCI_D0, 0); - netif_device_attach(netdev); + ieee80211_wake_queues(hw); } const struct pci_error_handlers rtw_pci_err_handler = { From 26769329c40a4d7f443f22f526de19eeedea67a2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 14 Apr 2026 14:22:27 +0800 Subject: [PATCH 004/285] wifi: rtl8xxxu: validate action frame size before using in rtl8xxxu_dump_action() The rtl8xxxu_dump_action() is to print action frames when turning on debug mask. Validate the skb->len size to prevent potential broken in monitor mode injection. Compile tested only. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260414062229.21047-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index f20fade0c099..508137e4a87a 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -5126,7 +5126,7 @@ static void rtl8xxxu_tx_complete(struct urb *urb) } static void rtl8xxxu_dump_action(struct device *dev, - struct ieee80211_hdr *hdr) + struct ieee80211_hdr *hdr, unsigned int skb_len) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)hdr; u16 cap, timeout; @@ -5134,8 +5134,14 @@ static void rtl8xxxu_dump_action(struct device *dev, if (!(rtl8xxxu_debug & RTL8XXXU_DEBUG_ACTION)) return; + if (skb_len < IEEE80211_MIN_ACTION_SIZE(action_code)) + return; + switch (mgmt->u.action.action_code) { case WLAN_ACTION_ADDBA_RESP: + if (skb_len < IEEE80211_MIN_ACTION_SIZE(addba_resp)) + break; + cap = le16_to_cpu(mgmt->u.action.addba_resp.capab); timeout = le16_to_cpu(mgmt->u.action.addba_resp.timeout); dev_info(dev, "WLAN_ACTION_ADDBA_RESP: " @@ -5148,6 +5154,9 @@ static void rtl8xxxu_dump_action(struct device *dev, le16_to_cpu(mgmt->u.action.addba_resp.status)); break; case WLAN_ACTION_ADDBA_REQ: + if (skb_len < IEEE80211_MIN_ACTION_SIZE(addba_req)) + break; + cap = le16_to_cpu(mgmt->u.action.addba_req.capab); timeout = le16_to_cpu(mgmt->u.action.addba_req.timeout); dev_info(dev, "WLAN_ACTION_ADDBA_REQ: " @@ -5437,7 +5446,7 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, } if (ieee80211_is_action(hdr->frame_control)) - rtl8xxxu_dump_action(dev, hdr); + rtl8xxxu_dump_action(dev, hdr, skb->len); tx_info->rate_driver_data[0] = hw; From c9f10419c38ed619fadad7b8d72f05fca5b4ade8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 14 Apr 2026 14:22:28 +0800 Subject: [PATCH 005/285] wifi: rtlwifi: validate action frame size in rtl_action_proc() Since action frames might be malformed from RX or injected TX, validate the size before using. More, use struct ieee80211_mgmt to access fields of action frames instead of counting barely. Tested with ping on RTL8723BE. The add BA request action frames of TX/RX can be handled properly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260414062229.21047-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtlwifi/base.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index aad377864e73..9e98c01bb90e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1369,18 +1369,19 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct ieee80211_hdr *hdr = rtl_get_hdr(skb); struct rtl_priv *rtlpriv = rtl_priv(hw); + struct ieee80211_mgmt *mgmt; __le16 fc = rtl_get_fc(skb); - u8 *act = (u8 *)(((u8 *)skb->data + MAC80211_3ADDR_LEN)); - u8 category; if (!ieee80211_is_action(fc)) return true; - category = *act; - act++; - switch (category) { + mgmt = (void *)skb->data; + if (skb->len < IEEE80211_MIN_ACTION_SIZE(action_code)) + return true; + + switch (mgmt->u.action.category) { case ACT_CAT_BA: - switch (*act) { + switch (mgmt->u.action.action_code) { case ACT_ADDBAREQ: if (mac->act_scanning) return false; @@ -1394,9 +1395,11 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) struct ieee80211_sta *sta = NULL; struct rtl_sta_info *sta_entry = NULL; struct rtl_tid_data *tid_data; - struct ieee80211_mgmt *mgmt = (void *)skb->data; u16 capab = 0, tid = 0; + if (skb->len < IEEE80211_MIN_ACTION_SIZE(addba_req)) + return true; + rcu_read_lock(); sta = rtl_find_sta(hw, hdr->addr3); if (sta == NULL) { From 043f09bb78ddb98dd5d587bf8fe4836daeb3a735 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 14 Apr 2026 14:22:29 +0800 Subject: [PATCH 006/285] wifi: rtlwifi: validate action frame size before using in _rtl_pci_tx_isr() Since TX action frames might be malformed, validate the size before using. Tested on RTL8723BE. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260414062229.21047-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtlwifi/pci.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 9cc0a871ea3c..73018a0498b4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -505,9 +505,10 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio) } } if (ieee80211_is_action(fc)) { - struct ieee80211_mgmt *action_frame = - (struct ieee80211_mgmt *)skb->data; - if (action_frame->u.action.action_code == + struct ieee80211_mgmt *action_frame = (void *)skb->data; + + if (skb->len >= IEEE80211_MIN_ACTION_SIZE(action_code) && + action_frame->u.action.action_code == WLAN_HT_ACTION_SMPS) { dev_kfree_skb(skb); goto tx_status_ok; From d5e6f353ce1e1c25b8458ea390ed09d2377412c5 Mon Sep 17 00:00:00 2001 From: Panagiotis Petrakopoulos Date: Wed, 15 Apr 2026 08:29:59 +0300 Subject: [PATCH 007/285] wifi: rtw88: Add NULL check for chip->edcca_th in rtw_fw_adaptivity_result() It was recently reported that rtw_fw_adaptivity_result() in fw.c dereferences rtwdev->chip->edcca_th without a NULL check. The issue is that devices with the 8821CE chip don't define edcca_th in their chip info. As a result, when rtw_fw_adaptivity_result() tries to dereference it, the kernel triggers an oops. Add a NULL check for edcca_th before dereferencing it in rtw_fw_adaptivity_result() in fw.c. Placing the check at the function entry avoids logging any garbage values. This change does not address the root cause for this behavior, but it prevents the NULL dereference and the resulting oops while a more permanent solution is developed. Tested on a 8822CE chip which defines edcca_th, so this issue is not present on it, but it still uses this driver and I can verify there are no regressions. Suggested-by: Ping-Ke Shih Reported-by: Oleksandr Havrylov Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221286 Signed-off-by: Panagiotis Petrakopoulos Acked-by: Ping-Ke Shih Tested-by: Oleksandr Havrylov Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260415052959.14844-1-npetrakopoulos2003@gmail.com --- drivers/net/wireless/realtek/rtw88/fw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 48207052e3f8..945fedcd375b 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -279,6 +279,9 @@ static void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload, const struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th; struct rtw_c2h_adaptivity *result = (struct rtw_c2h_adaptivity *)payload; + if (!edcca_th) + return; + rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Adaptivity: density %x igi %x l2h_th_init %x l2h %x h2l %x option %x\n", result->density, result->igi, result->l2h_th_init, result->l2h, From 5055188134c3cc755333fa19e5b8a0f3cb6fbb9a Mon Sep 17 00:00:00 2001 From: Louis Kotze Date: Thu, 16 Apr 2026 06:55:36 +0200 Subject: [PATCH 008/285] wifi: rtw89: phy: increase RF calibration timeouts for USB transport USB transport adds significant latency to H2C/C2H round-trips used by RF calibration. The existing timeout values were designed for PCIe and are too tight for USB, causing "failed to wait RF DACK", "failed to wait RF TSSI" and similar errors on USB adapters. Apply a 4x timeout multiplier when the device uses USB transport. The multiplier is applied in rtw89_phy_rfk_report_wait() so all calibrations benefit without changing any call sites or PCIe timeout values. The 4x multiplier was chosen based on measured data from two independent testers (RTL8922AU, 6GHz MLO and 2.4/5GHz): Calibration PCIe timeout Max measured (USB) 4x timeout PRE_NTFY 5ms 1ms 20ms DACK 58ms 72ms 232ms RX_DCK 128ms 374ms 512ms TSSI normal 20ms 24ms 80ms TSSI scan 6ms 14ms 24ms TXGAPK 54ms 18ms 216ms IQK 84ms 53ms 336ms DPK 34ms 30ms 136ms Tested with RTL8922AU on 6GHz MLO (5GHz + 6GHz simultaneous): 25 connect/disconnect cycles with zero failures. The 4x multiplier was also verified under adverse host conditions on 5GHz. 5 cycles per scenario, stress-ng as the load generator, max observed time per calibration: Calibration PCIe 4x Baseline CPU stress Mem stress Combined PRE_NTFY 5 20 0 0 0 1 DACK 58 232 71 (!) 71 (!) 71 (!) 71 (!) RX_DCK 128 512 23 22 22 23 IQK 84 336 53 53 53 53 DPK 34 136 23 23 26 23 TSSI 20 80 6 9 14 9 TXGAPK 54 216 16 16 16 16 Legend: (!) = exceeds PCIe budget but within 4x budget. Two observations from that matrix: 1. DACK exceeds the stock PCIe budget (58ms) in baseline on 5GHz on this hardware. Without the 4x multiplier, DACK fails -ETIMEDOUT deterministically on every connect, no stress needed. This is the condition the patch addresses. 2. Calibration times appear dominated by USB transport round-trip latency rather than host load, though hardware and external component factors may also contribute. DACK stays at 71ms across all four scenarios. Host-side stress has essentially zero effect on observed calibration duration. Bumping the multiplier above 4x would not address a failure mode that this stress matrix produces. Reported-by: Devin Wittmayer Link: https://github.com/Lucid-Duck/rtw89-usb3-gap/tree/main/evidence/crash-2026-04-11 Signed-off-by: Louis Kotze Tested-by: Devin Wittmayer # RTL8922AU (BrosTrend BE6500) Tested-by: Devin Wittmayer # RTL8852AU (D-Link DWA-X1850 A1) Tested-by: Devin Wittmayer # RTL8852AU (D-Link DWA-X1850 B1) Tested-by: Devin Wittmayer # RTL8852BU (BrosTrend AX4L) Tested-by: Devin Wittmayer # RTL8852CU (EDUP AX5400) Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260416045536.817930-1-loukot@gmail.com --- drivers/net/wireless/realtek/rtw89/phy.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index e70d0e283987..1f249c297006 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3956,6 +3956,14 @@ int rtw89_phy_rfk_report_wait(struct rtw89_dev *rtwdev, const char *rfk_name, struct rtw89_rfk_wait_info *wait = &rtwdev->rfk_wait; unsigned long time_left; + /* + * USB transport adds latency to H2C/C2H round-trips, so RF + * calibrations take longer than on PCIe. Apply a 4x multiplier + * to avoid spurious timeouts. + */ + if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + ms *= 4; + /* Since we can't receive C2H event during SER, use a fixed delay. */ if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) { fsleep(1000 * ms / 2); From 718c023ffe410ded96d5655bdec5c3a7239a369c Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Fri, 17 Apr 2026 16:05:17 -0400 Subject: [PATCH 009/285] wifi: rtlwifi: fix typos in comments in rtl8821ae_card_disable() Fix typos in rtl8821ae_card_disable() comments. Signed-off-by: Hugo Villeneuve Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260417200520.3919437-1-hugo@hugovil.com --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index c4dcae2d375e..9b119a51bc30 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -2355,7 +2355,7 @@ void rtl8821ae_card_disable(struct ieee80211_hw *hw) rtl8821ae_set_fw_disconnect_decision_ctrl_cmd(hw, true); } - /* 3 <3> Hw Configutations */ + /* 3 <3> Hw Configurations */ /* Wait until Rx DMA Finished before host sleep. * FW Pause Rx DMA may happen when received packet doing DMA. From 8d722eeac1a7964cf6f77b464e87082418e9657e Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:40:10 +0300 Subject: [PATCH 010/285] wifi: rtw89: usb: Support 2 bulk in endpoints RTL8912AU has 2 bulk in endpoints, not 1, so raise the limit. The second bulk-in is for USB interrupt mode for SER (system error recovery) flow. SER is not currently implemented for USB devices in rtw89. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/3d30c8d1-fa25-48d0-b27d-7d634c5aa005@gmail.com --- drivers/net/wireless/realtek/rtw89/usb.c | 15 +++++++++++---- drivers/net/wireless/realtek/rtw89/usb.h | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 767a95f759b1..d3c7ed29cfe5 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -511,7 +511,7 @@ static void rtw89_usb_rx_resubmit(struct rtw89_usb *rtwusb, rxcb->rx_skb = rx_skb; usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev, - usb_rcvbulkpipe(rtwusb->udev, rtwusb->in_pipe), + usb_rcvbulkpipe(rtwusb->udev, rtwusb->in_pipe[0]), rxcb->rx_skb->data, RTW89_USB_RECVBUF_SZ, rtw89_usb_read_port_complete, rxcb); @@ -948,6 +948,7 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); struct usb_endpoint_descriptor *endpoint; int num_out_pipes = 0; + int num_in_pipes = 0; u8 num; int i; @@ -963,13 +964,14 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, if (usb_endpoint_dir_in(endpoint) && usb_endpoint_xfer_bulk(endpoint)) { - if (rtwusb->in_pipe) { + if (num_in_pipes >= RTW89_MAX_BULKIN_NUM) { rtw89_err(rtwdev, - "found more than 1 bulk in endpoint\n"); + "found more than %d bulk in endpoint\n", + RTW89_MAX_BULKIN_NUM); return -EINVAL; } - rtwusb->in_pipe = num; + rtwusb->in_pipe[num_in_pipes++] = num; } if (usb_endpoint_dir_out(endpoint) && @@ -985,6 +987,11 @@ static int rtw89_usb_parse(struct rtw89_dev *rtwdev, } } + if (num_in_pipes < 1) { + rtw89_err(rtwdev, "no bulk in endpoints found\n"); + return -EINVAL; + } + if (num_out_pipes < 1) { rtw89_err(rtwdev, "no bulk out endpoints found\n"); return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index 507f61f58ed9..82de700eb142 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -18,6 +18,7 @@ #define RTW89_USB_MOD512_PADDING 4 #define RTW89_MAX_ENDPOINT_NUM 9 +#define RTW89_MAX_BULKIN_NUM 2 #define RTW89_MAX_BULKOUT_NUM 7 #define R_AX_RXAGG_0_V1 0x6000 @@ -65,7 +66,7 @@ struct rtw89_usb { atomic_t continual_io_error; - u8 in_pipe; + u8 in_pipe[RTW89_MAX_BULKIN_NUM]; u8 out_pipe[RTW89_MAX_BULKOUT_NUM]; struct workqueue_struct *rxwq; From 6f4621340312d7cfd4f2acc3115cd5879fd770c3 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:40:59 +0300 Subject: [PATCH 011/285] wifi: rtw89: Fix rtw89_usb_ops_mac_lv1_rcvy() for RTL8922AU RTL8922AU uses a different register and mask to reset TX/RX. Add them here. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/fd6ae7f6-0035-45d8-8832-49a053fd217d@gmail.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 ++++ drivers/net/wireless/realtek/rtw89/usb.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 42ffe83931a3..570d5883761b 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4806,6 +4806,10 @@ #define R_BE_LTR_LATENCY_IDX2_V1 0x361C #define R_BE_LTR_LATENCY_IDX3_V1 0x3620 +#define R_BE_USB2_WLAN_TRX_OPT_PAR2 0x41BC +#define B_BE_USB2_USBRX_RST BIT(1) +#define B_BE_USB2_USBTX_RST BIT(0) + #define R_BE_HCI_BUF_IMR 0x6018 #define B_BE_HCI_BUF_IMR_CLR 0xC0000303 #define B_BE_HCI_BUF_IMR_SET 0xC0000301 diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index d3c7ed29cfe5..fafc2b33e561 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -871,6 +871,10 @@ static int rtw89_usb_ops_mac_lv1_rcvy(struct rtw89_dev *rtwdev, reg = R_AX_USB_WLAN0_1_V1; mask = B_AX_USBRX_RST_V1 | B_AX_USBTX_RST_V1; break; + case RTL8922A: + reg = R_BE_USB2_WLAN_TRX_OPT_PAR2; + mask = B_BE_USB2_USBRX_RST | B_BE_USB2_USBTX_RST; + break; default: rtw89_err(rtwdev, "%s: fix me\n", __func__); return -EOPNOTSUPP; From 0cc43249ea97946cc8d5e315246bad8369c31f27 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:42:38 +0300 Subject: [PATCH 012/285] wifi: rtw89: Fix rtw89_usb_ops_mac_pre_init() for RTL8922AU Return early because RTL8922AU doesn't need to do anything here. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/58bf4dc4-dd4d-4caa-b6c0-e90749769cea@gmail.com --- drivers/net/wireless/realtek/rtw89/usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index fafc2b33e561..3d9113ae7fb4 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -756,6 +756,9 @@ static int rtw89_usb_ops_mac_pre_init(struct rtw89_dev *rtwdev) const struct rtw89_usb_info *info = rtwusb->info; u32 val32; + if (rtwdev->chip->chip_id == RTL8922A) + return 0; + rtw89_write32_set(rtwdev, info->usb_host_request_2, B_AX_R_USBIO_MODE); From 0df7f0c22d96024f4a9db9f32693c6ea527c0c9d Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:43:12 +0300 Subject: [PATCH 013/285] wifi: rtw89: Fix rtw89_usb_ops_mac_post_init() for RTL8922AU Skip most of the function because RTL8922AU only needs to configure the RX aggregation here. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/71329cd6-0249-4654-be1d-74c8823f4888@gmail.com --- drivers/net/wireless/realtek/rtw89/usb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 3d9113ae7fb4..a08e43c8275e 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -827,6 +827,9 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) enum usb_device_speed speed; u32 ep; + if (rtwdev->chip->chip_id == RTL8922A) + goto rx_agg_cfg; + rtw89_write32_clr(rtwdev, info->usb3_mac_npi_config_intf_0, B_AX_SSPHY_LFPS_FILTER); @@ -848,6 +851,7 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) rtw89_write8(rtwdev, info->usb_endpoint_2 + 1, NUMP); } +rx_agg_cfg: rtw89_usb_rx_agg_cfg(rtwdev); return 0; From 32cee8a9bb66ceb434b9eae32beb17403287c4d5 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:43:53 +0300 Subject: [PATCH 014/285] wifi: rtw89: usb: Enable RX aggregation for RTL8922AU It uses the same settings as RTL8852CU, but the register and bit names have "BE" instead of "AX". Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/bd9e444f-257c-48c8-8adb-f58432b2c5c2@gmail.com --- drivers/net/wireless/realtek/rtw89/usb.c | 14 ++++++++++++++ drivers/net/wireless/realtek/rtw89/usb.h | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index a08e43c8275e..c6d55e669776 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -803,6 +803,17 @@ static void rtw89_usb_rx_agg_cfg_v2(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_AX_RXAGG_1_V1, 0x1F); } +static void rtw89_usb_rx_agg_cfg_v3(struct rtw89_dev *rtwdev) +{ + const u32 rxagg_0 = FIELD_PREP_CONST(B_BE_RXAGG_0_EN, 1) | + FIELD_PREP_CONST(B_BE_RXAGG_0_NUM_TH, 255) | + FIELD_PREP_CONST(B_BE_RXAGG_0_TIME_32US_TH, 32) | + FIELD_PREP_CONST(B_BE_RXAGG_0_BUF_SZ_1K, 20); + + rtw89_write32(rtwdev, R_BE_RXAGG_0_V1, rxagg_0); + rtw89_write32(rtwdev, R_BE_RXAGG_1_V1, 0x1F); +} + static void rtw89_usb_rx_agg_cfg(struct rtw89_dev *rtwdev) { switch (rtwdev->chip->chip_id) { @@ -814,6 +825,9 @@ static void rtw89_usb_rx_agg_cfg(struct rtw89_dev *rtwdev) case RTL8852C: rtw89_usb_rx_agg_cfg_v2(rtwdev); break; + case RTL8922A: + rtw89_usb_rx_agg_cfg_v3(rtwdev); + break; default: rtw89_warn(rtwdev, "%s: USB RX agg not support\n", __func__); return; diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index 82de700eb142..bdf312559743 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -29,6 +29,14 @@ #define R_AX_RXAGG_1_V1 0x6004 +#define R_BE_RXAGG_0_V1 0x6000 +#define B_BE_RXAGG_0_EN BIT(31) +#define B_BE_RXAGG_0_NUM_TH GENMASK(23, 16) +#define B_BE_RXAGG_0_TIME_32US_TH GENMASK(15, 8) +#define B_BE_RXAGG_0_BUF_SZ_1K GENMASK(7, 0) + +#define R_BE_RXAGG_1_V1 0x6004 + #define R_AX_RXAGG_0 0x8900 #define B_AX_RXAGG_0_BUF_SZ_4K GENMASK(7, 0) From c3dfef1e3552a25ce0daa6c8610caf048375c588 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:44:54 +0300 Subject: [PATCH 015/285] wifi: rtw89: Fix rtw8922a_pwr_{on,off}_func() for USB There are a few differences in the power on/off functions between PCIE and USB. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/dccdb944-c31d-483f-8d1a-a51bcfe23916@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 92 +++++++++++++------ 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 8f6cf64271e8..2bea44faa109 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -298,17 +298,24 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_clr(rtwdev, R_BE_FEN_RST_ENABLE, B_BE_R_SYM_ISO_ADDA_P02PP | B_BE_R_SYM_ISO_ADDA_P12PP); rtw89_write8_set(rtwdev, R_BE_PLATFORM_ENABLE, B_BE_PLATFORM_EN); - rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, val32 & B_BE_HAXIDMA_IO_ST, - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + val32 & B_BE_HAXIDMA_IO_ST, + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + } rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN); @@ -317,7 +324,9 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32_clr(rtwdev, R_BE_SYS_SDIO_CTRL, B_BE_PCIE_FORCE_IBX_EN); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32_clr(rtwdev, R_BE_SYS_SDIO_CTRL, + B_BE_PCIE_FORCE_IBX_EN); ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_PLL, 0x02, 0x02); if (ret) @@ -371,6 +380,10 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) if (ret) return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_SRAM_CTRL, 0, XTAL_SI_SRAM_DIS); + if (ret) + return ret; + if (hal->cv != CHIP_CAV) { rtw89_write32_set(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); rtw89_write32_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE); @@ -382,14 +395,16 @@ static int rtw8922a_pwr_on_func(struct rtw89_dev *rtwdev) rtw89_write32_clr(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); } - rtw89_write32_set(rtwdev, R_BE_DMAC_FUNC_EN, - B_BE_MAC_FUNC_EN | B_BE_DMAC_FUNC_EN | B_BE_MPDU_PROC_EN | - B_BE_WD_RLS_EN | B_BE_DLE_WDE_EN | B_BE_TXPKT_CTRL_EN | - B_BE_STA_SCH_EN | B_BE_DLE_PLE_EN | B_BE_PKT_BUF_EN | - B_BE_DMAC_TBL_EN | B_BE_PKT_IN_EN | B_BE_DLE_CPUIO_EN | - B_BE_DISPATCHER_EN | B_BE_BBRPT_EN | B_BE_MAC_SEC_EN | - B_BE_H_AXIDMA_EN | B_BE_DMAC_MLO_EN | B_BE_PLRLS_EN | - B_BE_P_AXIDMA_EN | B_BE_DLE_DATACPUIO_EN | B_BE_LTR_CTL_EN); + val32 = B_BE_MAC_FUNC_EN | B_BE_DMAC_FUNC_EN | B_BE_MPDU_PROC_EN | + B_BE_WD_RLS_EN | B_BE_DLE_WDE_EN | B_BE_TXPKT_CTRL_EN | + B_BE_STA_SCH_EN | B_BE_DLE_PLE_EN | B_BE_PKT_BUF_EN | + B_BE_DMAC_TBL_EN | B_BE_PKT_IN_EN | B_BE_DLE_CPUIO_EN | + B_BE_DISPATCHER_EN | B_BE_BBRPT_EN | B_BE_MAC_SEC_EN | + B_BE_H_AXIDMA_EN | B_BE_DMAC_MLO_EN | B_BE_PLRLS_EN | + B_BE_P_AXIDMA_EN | B_BE_DLE_DATACPUIO_EN; + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + val32 |= B_BE_LTR_CTL_EN; + rtw89_write32_set(rtwdev, R_BE_DMAC_FUNC_EN, val32); set_bit(RTW89_FLAG_DMAC_FUNC, rtwdev->flags); @@ -465,17 +480,23 @@ static int rtw8922a_pwr_off_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_IO_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_IO_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; - ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), - 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); - if (ret) - return ret; + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), + 1000, 3000000, false, rtwdev, + R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + } rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN); @@ -491,9 +512,22 @@ static int rtw8922a_pwr_off_func(struct rtw89_dev *rtwdev) if (ret) return ret; - rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x0000A1B2); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x0000A1B2); + else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_SOP_EASWR); + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_XTAL_OFF_A_DIE); - rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); + + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); + } else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) { + val32 = rtw89_read32(rtwdev, R_BE_SYS_PW_CTRL); + val32 |= B_BE_AFSM_WLSUS_EN; + val32 &= ~B_BE_AFSM_PCIE_SUS_EN; + rtw89_write32(rtwdev, R_BE_SYS_PW_CTRL, val32); + } + rtw89_write32(rtwdev, R_BE_UDM1, 0); return 0; From c423ed43c93cd1e44dbcd77f94aff747df7b20a1 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:45:23 +0300 Subject: [PATCH 016/285] wifi: rtw89: Let hfc_param_ini have separate settings for USB 2/3 RTL8912AU needs different settings for USB 2 and USB 3. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/588bf309-4b92-454f-bfd6-5e81248abee3@gmail.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 +- drivers/net/wireless/realtek/rtw89/mac.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +- 9 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index fd29dbbb120d..2d7d3a11be16 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4544,7 +4544,7 @@ struct rtw89_chip_info { u16 max_rx_agg_num; bool dis_2g_40m_ul_ofdma; u32 rsvd_ple_ofst; - const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_TYPE_NUM]; + const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_DLE_TYPE_NUM]; const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM]; u8 wde_qempty_acq_grpnum; u8 wde_qempty_mgq_grpsel; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 54aad37485d6..8d5375ec33a2 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -898,7 +898,7 @@ static int hfc_reset_param(struct rtw89_dev *rtwdev) struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; u8 qta_mode = rtwdev->mac.dle_info.qta_mode; - param_inis = rtwdev->chip->hfc_param_ini[rtwdev->hci.type]; + param_inis = rtwdev->chip->hfc_param_ini[rtwdev->hci.dle_type]; if (!param_inis) return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 84bdd39b3ceb..ff4113eb9114 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2600,6 +2600,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, .hfc_param_ini = {rtw8851b_hfc_param_ini_pcie, + rtw8851b_hfc_param_ini_usb, rtw8851b_hfc_param_ini_usb, NULL}, .dle_mem = {rtw8851b_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 1d4f1df524a1..ea4d22616512 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2336,6 +2336,7 @@ 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, + rtw8852a_hfc_param_ini_usb, rtw8852a_hfc_param_ini_usb, NULL}, .dle_mem = {rtw8852a_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 5e8738bb2dc2..417b1502a2dd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -931,6 +931,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, .hfc_param_ini = {rtw8852b_hfc_param_ini_pcie, + rtw8852b_hfc_param_ini_usb, rtw8852b_hfc_param_ini_usb, NULL}, .dle_mem = {rtw8852b_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index ab4263bc8b9f..7d86e9598ca8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -776,7 +776,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL}, + .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL, NULL}, .dle_mem = {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL}, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 40db7e3c0d97..9c2c05f80393 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3126,6 +3126,7 @@ 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, + rtw8852c_hfc_param_ini_usb, rtw8852c_hfc_param_ini_usb, NULL}, .dle_mem = {rtw8852c_dle_mem_pcie, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 2bea44faa109..b247980cc9ec 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2969,7 +2969,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x8f800, - .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, NULL, NULL}, + .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, NULL, NULL, NULL}, .dle_mem = {rtw8922a_dle_mem_pcie, NULL, NULL, NULL}, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index e3b77cd23514..96e7b70a3b93 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2948,7 +2948,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .max_rx_agg_num = 256, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x5f800, - .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL}, + .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL, NULL}, .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, .wde_qempty_acq_grpnum = 8, .wde_qempty_mgq_grpsel = 8, From bd87e927006c6f548c65e23a00169c2ee87825ea Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:45:55 +0300 Subject: [PATCH 017/285] wifi: rtw89: Add rtw8922a_hfc_param_ini_usb{2,3} "hfc" means "hci fc" which is "Host Control Interface Flow Control". These are some parameters needed for RTL8922AU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/474eb37a-a2e1-434a-b648-9e4b78e604d2@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 3 + drivers/net/wireless/realtek/rtw89/mac.h | 3 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 125 +++++++++++++++++- 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 8d5375ec33a2..9058312322e0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1716,6 +1716,9 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .hfc_preccfg_pcie = {2, 40, 0, 0, 1, 0, 0, 0}, .hfc_prec_cfg_c0 = {2, 32, 0, 0, 0, 0, 0, 0, 2, 32, 0, 0}, .hfc_prec_cfg_c2 = {0, 256, 0, 0, 0, 0, 0, 0, 0, 256, 0, 0}, + .hfc_prec_cfg_c3 = {18, 32, 148, 148, 1, 1, 1, 1}, + .hfc_prec_cfg_c5 = {18, 32, 148, 148, 1, 1, 0, 1}, + .hfc_prec_cfg_c6 = {8, 32, 148, 148, 1, 1, 0, 1}, /* PCIE 64 */ .wde_size0 = {RTW89_WDE_PG_64, 4095, 1,}, .wde_size0_v1 = {RTW89_WDE_PG_64, 3328, 0, 0,}, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 9db9ae219cd6..3aad234b2a0f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -926,6 +926,9 @@ struct rtw89_mac_size_set { const struct rtw89_hfc_prec_cfg hfc_preccfg_pcie; const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c0; const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c2; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c3; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c5; + const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c6; const struct rtw89_dle_size wde_size0; const struct rtw89_dle_size wde_size1; const struct rtw89_dle_size wde_size0_v1; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index b247980cc9ec..380049ef15b5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -57,6 +57,126 @@ static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_pcie[] = { [RTW89_QTA_INVALID] = {NULL}, }; +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch7[] = { + {54, 222, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 222, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 222, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {54, 222, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {54, 222, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 222, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p7 = { + 492, /* Group 0 */ + 0, /* Group 1 */ + 492, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch8[] = { + {24, 196, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 226, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 196, grp_1}, /* ACH 4 */ + {0, 0, grp_1}, /* ACH 5 */ + {54, 196, grp_1}, /* ACH 6 */ + {0, 0, grp_1}, /* ACH 7 */ + {54, 226, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 196, grp_1}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p8 = { + 304, /* Group 0 */ + 304, /* Group 1 */ + 608, /* Public Max */ + 96, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_usb2[] = { + [RTW89_QTA_SCC] = {rtw8922a_hfc_chcfg_ch7, &rtw8922a_hfc_pubcfg_p7, + &rtw89_mac_size.hfc_prec_cfg_c5, RTW89_HCIFC_STF}, + [RTW89_QTA_DBCC] = {rtw8922a_hfc_chcfg_ch8, &rtw8922a_hfc_pubcfg_p8, + &rtw89_mac_size.hfc_prec_cfg_c6, RTW89_HCIFC_STF}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch4[] = { + {54, 606, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 606, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 606, grp_0}, /* ACH 4 */ + {0, 0, grp_0}, /* ACH 5 */ + {54, 606, grp_0}, /* ACH 6 */ + {0, 0, grp_0}, /* ACH 7 */ + {54, 606, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 606, grp_0}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p4 = { + 876, /* Group 0 */ + 0, /* Group 1 */ + 876, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_ch_cfg rtw8922a_hfc_chcfg_ch5[] = { + {54, 311, grp_0}, /* ACH 0 */ + {0, 0, grp_0}, /* ACH 1 */ + {54, 311, grp_0}, /* ACH 2 */ + {0, 0, grp_0}, /* ACH 3 */ + {54, 311, grp_1}, /* ACH 4 */ + {0, 0, grp_1}, /* ACH 5 */ + {54, 311, grp_1}, /* ACH 6 */ + {0, 0, grp_1}, /* ACH 7 */ + {54, 311, grp_0}, /* B0MGQ */ + {0, 0, grp_0}, /* B0HIQ */ + {54, 311, grp_1}, /* B1MGQ */ + {0, 0, grp_0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922a_hfc_pubcfg_p5 = { + 419, /* Group 0 */ + 419, /* Group 1 */ + 838, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922a_hfc_param_ini_usb3[] = { + [RTW89_QTA_SCC] = {rtw8922a_hfc_chcfg_ch4, &rtw8922a_hfc_pubcfg_p4, + &rtw89_mac_size.hfc_prec_cfg_c3, RTW89_HCIFC_STF}, + [RTW89_QTA_DBCC] = {rtw8922a_hfc_chcfg_ch5, &rtw8922a_hfc_pubcfg_p5, + &rtw89_mac_size.hfc_prec_cfg_c3, RTW89_HCIFC_STF}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + static const struct rtw89_dle_mem rtw8922a_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size0_v1, &rtw89_mac_size.ple_size0_v1, &rtw89_mac_size.wde_qt0_v1, @@ -2969,7 +3089,10 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x8f800, - .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, NULL, NULL, NULL}, + .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, + rtw8922a_hfc_param_ini_usb2, + rtw8922a_hfc_param_ini_usb3, + NULL}, .dle_mem = {rtw8922a_dle_mem_pcie, NULL, NULL, NULL}, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, From abe23df7c89f42741fd55f853e81be120996e991 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:46:31 +0300 Subject: [PATCH 018/285] wifi: rtw89: Add rtw8922a_dle_mem_usb{2,3} Add rtw8922a_dle_mem_usb2 and rtw8922a_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 RTL8922AU. Also rename wde_size4_v1 to wde_size3_v1 because the name seemed incorrect. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/f9ac366b-ca8d-492e-83bf-440f8557c629@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 35 +++++++++++++- drivers/net/wireless/realtek/rtw89/mac.h | 22 +++++++++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 47 ++++++++++++++++++- 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 9058312322e0..6f76ae2a99ce 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1725,12 +1725,18 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* 8852A USB */ .wde_size1 = {RTW89_WDE_PG_64, 768, 0,}, /* DLFW */ + .wde_size3_v1 = {RTW89_WDE_PG_64, 0, 3328, 0,}, .wde_size4 = {RTW89_WDE_PG_64, 0, 4096,}, - .wde_size4_v1 = {RTW89_WDE_PG_64, 0, 3328, 0,}, + /* 8922A USB3.0 */ + .wde_size4_v1 = {RTW89_WDE_PG_64, 892, 4, 0,}, + .wde_size5_v1 = {RTW89_WDE_PG_64, 864, 32, 0,}, /* PCIE 64 */ .wde_size6 = {RTW89_WDE_PG_64, 512, 0,}, /* 8852B PCIE SCC */ .wde_size7 = {RTW89_WDE_PG_64, 510, 2,}, + /* 8922A USB2.0 */ + .wde_size7_v1 = {RTW89_WDE_PG_64, 508, 4,}, + .wde_size8_v1 = {RTW89_WDE_PG_64, 634, 6,}, /* DLFW */ .wde_size9 = {RTW89_WDE_PG_64, 0, 1024,}, .wde_size16_v1 = {RTW89_WDE_PG_64, 639, 1, 0,}, @@ -1754,8 +1760,13 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_size3_v1 = {RTW89_PLE_PG_128, 2928, 0, 212992,}, /* DLFW */ .ple_size4 = {RTW89_PLE_PG_128, 64, 1472,}, + /* 8922A USB3.0 */ + .ple_size4_v1 = {RTW89_PLE_PG_256, 1963, 109, 57344,}, /* PCIE 64 */ .ple_size6 = {RTW89_PLE_PG_128, 496, 16,}, + /* 8922A USB2.0 */ + .ple_size6_v1 = {RTW89_PLE_PG_256, 2059, 109, 32768,}, + .ple_size7_v1 = {RTW89_PLE_PG_256, 2027, 109, 40960,}, /* DLFW */ .ple_size8 = {RTW89_PLE_PG_128, 64, 960,}, .ple_size9 = {RTW89_PLE_PG_128, 2288, 16,}, @@ -1781,10 +1792,16 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt3 = {0, 0, 0, 0,}, /* DLFW */ .wde_qt4 = {0, 0, 0, 0,}, + /* 8922A USB3.0 */ + .wde_qt4_v1 = {876, 6, 0, 10,}, + .wde_qt5_v1 = {838, 6, 0, 20,}, /* PCIE 64 */ .wde_qt6 = {448, 48, 0, 16,}, /* 8852B PCIE SCC */ .wde_qt7 = {446, 48, 0, 16,}, + /* 8922A USB2.0 */ + .wde_qt7_v1 = {492, 6, 0, 10,}, + .wde_qt8_v1 = {608, 6, 0, 20,}, /* 8852C USB3.0 */ .wde_qt16 = {344, 2, 0, 8,}, /* 8852C DLFW */ @@ -1804,9 +1821,22 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* PCIE SCC */ .ple_qt5 = {264, 0, 32, 20, 64, 13, 1101, 0, 64, 128, 120,}, .ple_qt5_v2 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,}, + /* 8922A USB3.0 */ + .ple_qt6_v1 = {1806, 0, 16, 24, 7, 14, 57, 0, 12, 9, 1, 4, 0,}, + .ple_qt7_v1 = {1806, 0, 16, 24, 20, 27, 70, 0, 25, 9, 1, 17, 0,}, + .ple_qt8_v1 = {1638, 0, 16, 24, 7, 14, 99, 99, 24, 9, 1, 4, 0,}, + /* PCIE SCC */ .ple_qt9 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 1, 0, 0,}, + /* 8922A USB3.0 */ + .ple_qt9_v1 = {1638, 0, 16, 24, 35, 42, 127, 127, 52, 9, 1, 32, 0,}, + /* 8922A USB2.0 */ + .ple_qt12_v1 = {463, 0, 16, 24, 7, 14, 57, 0, 12, 9, 1, 4, 0,}, /* DLFW */ .ple_qt13 = {0, 0, 16, 48, 0, 0, 0, 0, 0, 0, 0,}, + /* 8922A USB2.0 */ + .ple_qt13_v1 = {463, 0, 16, 24, 1459, 1466, 1509, 0, 1464, 9, 1, 1456, 0,}, + .ple_qt14_v1 = {939, 0, 16, 24, 7, 14, 57, 57, 24, 9, 1, 4, 0,}, + .ple_qt15_v1 = {939, 0, 16, 24, 882, 889, 932, 932, 899, 9, 1, 879, 0,}, /* PCIE 64 */ .ple_qt18 = {147, 0, 16, 20, 17, 13, 89, 0, 32, 14, 8, 0,}, /* 8852A USB SCC */ @@ -1850,8 +1880,11 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt_51b_wow = {147, 0, 16, 20, 157, 13, 133, 0, 172, 14, 24, 0,}, .ple_rsvd_qt0 = {2, 107, 107, 6, 6, 6, 6, 0, 0, 0,}, .ple_rsvd_qt1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_rsvd_qt2 = {1, 56, 28, 6, 6, 6, 6, 0, 0, 0,}, .ple_rsvd_qt9 = {1, 44, 44, 6, 6, 6, 6, 69, 0, 0,}, .rsvd0_size0 = {212992, 0,}, + .rsvd0_size3 = {57344, 0,}, + .rsvd0_size5 = {32768, 0,}, .rsvd0_size6 = {40960, 0,}, .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 3aad234b2a0f..75691a9549a7 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -932,10 +932,14 @@ struct rtw89_mac_size_set { 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_size3_v1; const struct rtw89_dle_size wde_size4; const struct rtw89_dle_size wde_size4_v1; + const struct rtw89_dle_size wde_size5_v1; const struct rtw89_dle_size wde_size6; const struct rtw89_dle_size wde_size7; + const struct rtw89_dle_size wde_size7_v1; + const struct rtw89_dle_size wde_size8_v1; const struct rtw89_dle_size wde_size9; const struct rtw89_dle_size wde_size16_v1; const struct rtw89_dle_size wde_size17; @@ -949,8 +953,11 @@ struct rtw89_mac_size_set { 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_v1; const struct rtw89_dle_size ple_size4; const struct rtw89_dle_size ple_size6; + const struct rtw89_dle_size ple_size6_v1; + const struct rtw89_dle_size ple_size7_v1; const struct rtw89_dle_size ple_size8; const struct rtw89_dle_size ple_size9; const struct rtw89_dle_size ple_size17; @@ -966,8 +973,12 @@ struct rtw89_mac_size_set { const struct rtw89_wde_quota wde_qt0_v1; const struct rtw89_wde_quota wde_qt3; const struct rtw89_wde_quota wde_qt4; + const struct rtw89_wde_quota wde_qt4_v1; + const struct rtw89_wde_quota wde_qt5_v1; const struct rtw89_wde_quota wde_qt6; const struct rtw89_wde_quota wde_qt7; + const struct rtw89_wde_quota wde_qt7_v1; + const struct rtw89_wde_quota wde_qt8_v1; const struct rtw89_wde_quota wde_qt16; const struct rtw89_wde_quota wde_qt17; const struct rtw89_wde_quota wde_qt18; @@ -980,8 +991,16 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt4; const struct rtw89_ple_quota ple_qt5; const struct rtw89_ple_quota ple_qt5_v2; + const struct rtw89_ple_quota ple_qt6_v1; + const struct rtw89_ple_quota ple_qt7_v1; + const struct rtw89_ple_quota ple_qt8_v1; const struct rtw89_ple_quota ple_qt9; + const struct rtw89_ple_quota ple_qt9_v1; + const struct rtw89_ple_quota ple_qt12_v1; const struct rtw89_ple_quota ple_qt13; + const struct rtw89_ple_quota ple_qt13_v1; + const struct rtw89_ple_quota ple_qt14_v1; + const struct rtw89_ple_quota ple_qt15_v1; const struct rtw89_ple_quota ple_qt18; const struct rtw89_ple_quota ple_qt25; const struct rtw89_ple_quota ple_qt26; @@ -1009,8 +1028,11 @@ struct rtw89_mac_size_set { const struct rtw89_rsvd_quota ple_rsvd_qt0; const struct rtw89_rsvd_quota ple_rsvd_qt1; const struct rtw89_rsvd_quota ple_rsvd_qt1_v1; + const struct rtw89_rsvd_quota ple_rsvd_qt2; const struct rtw89_rsvd_quota ple_rsvd_qt9; const struct rtw89_dle_rsvd_size rsvd0_size0; + const struct rtw89_dle_rsvd_size rsvd0_size3; + const struct rtw89_dle_rsvd_size rsvd0_size5; const struct rtw89_dle_rsvd_size rsvd0_size6; const struct rtw89_dle_rsvd_size rsvd1_size0; const struct rtw89_dle_rsvd_size rsvd1_size2; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 380049ef15b5..3f94a5e80afe 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -188,7 +188,47 @@ static const struct rtw89_dle_mem rtw8922a_dle_mem_pcie[] = { &rtw89_mac_size.wde_qt0_v1, &rtw89_mac_size.ple_qt0, &rtw89_mac_size.ple_qt1, &rtw89_mac_size.ple_rsvd_qt0, &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, - [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size4_v1, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, + &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, + &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, + &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_dle_mem rtw8922a_dle_mem_usb2[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size7_v1, + &rtw89_mac_size.ple_size6_v1, &rtw89_mac_size.wde_qt7_v1, + &rtw89_mac_size.wde_qt7_v1, &rtw89_mac_size.ple_qt12_v1, + &rtw89_mac_size.ple_qt13_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size5, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size8_v1, + &rtw89_mac_size.ple_size7_v1, &rtw89_mac_size.wde_qt8_v1, + &rtw89_mac_size.wde_qt8_v1, &rtw89_mac_size.ple_qt14_v1, + &rtw89_mac_size.ple_qt15_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, + &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, + &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, + &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size0, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_dle_mem rtw8922a_dle_mem_usb3[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size4_v1, + &rtw89_mac_size.ple_size4_v1, &rtw89_mac_size.wde_qt4_v1, + &rtw89_mac_size.wde_qt4_v1, &rtw89_mac_size.ple_qt6_v1, + &rtw89_mac_size.ple_qt7_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size3, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size5_v1, + &rtw89_mac_size.ple_size4_v1, &rtw89_mac_size.wde_qt5_v1, + &rtw89_mac_size.wde_qt5_v1, &rtw89_mac_size.ple_qt8_v1, + &rtw89_mac_size.ple_qt9_v1, &rtw89_mac_size.ple_rsvd_qt2, + &rtw89_mac_size.rsvd0_size3, &rtw89_mac_size.rsvd1_size0}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size3_v1, &rtw89_mac_size.ple_size3_v1, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_qt9, &rtw89_mac_size.ple_rsvd_qt1, @@ -3093,7 +3133,10 @@ const struct rtw89_chip_info rtw8922a_chip_info = { rtw8922a_hfc_param_ini_usb2, rtw8922a_hfc_param_ini_usb3, NULL}, - .dle_mem = {rtw8922a_dle_mem_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8922a_dle_mem_pcie, + rtw8922a_dle_mem_usb2, + rtw8922a_dle_mem_usb3, + NULL}, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, From 6bb58cd6e17699e41fedd2944b875ef594bcc2bb Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:47:42 +0300 Subject: [PATCH 019/285] wifi: rtw89: Add rtw8922au.c This is the entry point of the new rtw89_8922au module. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/0d22cd72-4048-41ad-a4aa-511681e6ba51@gmail.com --- .../net/wireless/realtek/rtw89/rtw8922au.c | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922au.c diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922au.c b/drivers/net/wireless/realtek/rtw89/rtw8922au.c new file mode 100644 index 000000000000..347bde171391 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922au.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2026 Realtek Corporation + */ + +#include +#include +#include "rtw8922a.h" +#include "usb.h" + +static const struct rtw89_usb_info rtw8922a_usb_info = { + .usb_host_request_2 = 0, + .usb_wlan0_1 = 0, + .hci_func_en = 0, + .usb3_mac_npi_config_intf_0 = 0, + .usb_endpoint_0 = 0, + .usb_endpoint_2 = 0, + .rx_agg_alignment = 16, + .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_8922au_info = { + .chip = &rtw8922a_chip_info, + .variant = NULL, + .quirks = NULL, + .bus = { + .usb = &rtw8922a_usb_info, + }, +}; + +static const struct usb_device_id rtw_8922au_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03ef, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0502, 0x76d7, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4025, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4026, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1bcf, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1bd2, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x8912, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0xda0e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x332b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x37ad, 0x0100, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x37ad, 0x0101, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x3822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x4822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x5822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8922au_info }, + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8922au_id_table); + +static struct usb_driver rtw_8922au_driver = { + .name = KBUILD_MODNAME, + .id_table = rtw_8922au_id_table, + .probe = rtw89_usb_probe, + .disconnect = rtw89_usb_disconnect, +}; +module_usb_driver(rtw_8922au_driver); + +MODULE_AUTHOR("Bitterblue Smith "); +MODULE_DESCRIPTION("Realtek 802.11be wireless 8922AU driver"); +MODULE_LICENSE("Dual BSD/GPL"); From f07d1009e9729f65adadcbc1cf7a2b10e807ba05 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sun, 19 Apr 2026 16:49:45 +0300 Subject: [PATCH 020/285] wifi: rtw89: Enable the new rtw89_8922au module Tested without MLO, mostly in station mode and a little in AP mode. It can reach 1.5 Gbps RX, 1 Gbps TX speed. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/c0bd60ba-4e2d-41cb-8b0c-38454a341d7a@gmail.com --- drivers/net/wireless/realtek/rtw89/Kconfig | 12 ++++++++++++ drivers/net/wireless/realtek/rtw89/Makefile | 3 +++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index 44d8a7f32bf2..43e3b0ef44da 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -157,6 +157,18 @@ config RTW89_8922AE The variant 8922AE-VS has the same features except 1024-QAM. +config RTW89_8922AU + tristate "Realtek 8922AU USB wireless network (Wi-Fi 7) adapter" + depends on USB + select RTW89_CORE + select RTW89_USB + select RTW89_8922A + help + Select this option will enable support for 8922AU chipset + + 802.11be USB wireless network (Wi-Fi 7) adapter + supporting 2x2 2GHz/5GHz/6GHz 4096-QAM 160MHz channels. + config RTW89_DEBUG bool diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile index 1be81f254fca..475bad743d75 100644 --- a/drivers/net/wireless/realtek/rtw89/Makefile +++ b/drivers/net/wireless/realtek/rtw89/Makefile @@ -88,6 +88,9 @@ rtw89_8922a-objs := rtw8922a.o \ obj-$(CONFIG_RTW89_8922AE) += rtw89_8922ae.o rtw89_8922ae-objs := rtw8922ae.o +obj-$(CONFIG_RTW89_8922AU) += rtw89_8922au.o +rtw89_8922au-objs := rtw8922au.o + rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o From d6a3aac6ed5d50aa2ce3d5dfed70d97ca6217f21 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 20 Apr 2026 11:40:36 +0800 Subject: [PATCH 021/285] wifi: rtw89: 8922d: fix typo rx_freq_frome_ie The commit 127ea8d0b068 ("wifi: rtw89: fix typo "frome" -> "from" in rx_freq_frome_ie") change the struct field to correct the typo, but miss to fix this for RTL8922D because it is submitting at the same time. As RTL8922D is not enabled yet, this build error doesn't appear. Fix it accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 96e7b70a3b93..a550ff8de8fc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2988,7 +2988,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .support_noise = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, - .rx_freq_frome_ie = false, + .rx_freq_from_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, .hw_tkip_crypto = true, From fdf0eb355182372e7f62d81837d3186e76b4bef9 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Mon, 20 Apr 2026 11:40:37 +0800 Subject: [PATCH 022/285] wifi: rtw89: 8852a: refine power save to lower latency Improve user experience while using interactive applications. Adjust power saving decisions under different scenarios. Minimize latency for delay-sensitive connections by exiting power-save on packet bursts and maintain active until a period of inactivity is reached. For connections that are not that sensitive to delays, keep the current aggressive power save logic. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 87 +++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 9 +++ 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 70feab97dccb..c9c4ec1d93af 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -341,10 +341,26 @@ static const struct rtw89_hw_rate_def { }, }; +static void rtw89_core_tx_skb_proto_stats(struct rtw89_traffic_stats *stats, + struct sk_buff *skb) +{ + switch (ip_hdr(skb)->protocol) { + case IPPROTO_TCP: + stats->tcp++; + break; + case IPPROTO_UDP: + stats->udp++; + break; + default: + break; + } +} + static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats, struct sk_buff *skb, bool tx) { if (tx) { + rtw89_core_tx_skb_proto_stats(stats, skb); stats->tx_cnt++; stats->tx_unicast += skb->len; } else { @@ -2902,6 +2918,23 @@ static void rtw89_core_bcn_track_reset(struct rtw89_dev *rtwdev) memset(&rtwdev->bcn_track, 0, sizeof(rtwdev->bcn_track)); } +static bool rtw89_core_proto_stats_can_lps(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + enum rtw89_tfc_interval interval) +{ + if (rtwdev->chip->chip_id != RTL8852A) + return true; + + if (rtwvif->burst_active) + return false; + + if (interval == RTW89_TFC_INTERVAL_100MS && + hweight8(rtwvif->stats_ps.active_histogram) < 3) + return false; + + return true; +} + static void rtw89_vif_rx_bcn_stat(struct rtw89_dev *rtwdev, struct sk_buff *skb) { #define RTW89_APPEND_TSF_2GHZ 384 @@ -4540,9 +4573,42 @@ static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev, return RTW89_TFC_ULTRA_LOW; } +static void rtw89_calc_vif_active_histogram(struct rtw89_dev *rtwdev, + struct rtw89_traffic_stats *stats, + enum rtw89_tfc_interval interval) +{ + struct rtw89_vif *rtwvif; + + stats->udp_ratio = stats->tx_cnt ? + DIV_ROUND_DOWN_ULL(stats->udp * 100, stats->tx_cnt) : 0; + stats->active_histogram <<= 1; + + switch (interval) { + case RTW89_TFC_INTERVAL_2SEC: + rtwvif = container_of(stats, struct rtw89_vif, stats); + + if (stats->tcp >= RTW89_TCP_TH && stats->tx_cnt >= stats->rx_cnt) + stats->active_histogram |= BIT(0); + + if (stats->active_histogram & RTW89_RECENT_ACTIVE_HIST) + rtwvif->burst_active = true; + else + rtwvif->burst_active = false; + + break; + case RTW89_TFC_INTERVAL_100MS: + if (stats->udp_ratio >= RTW89_UDP_RATIO_TH) + stats->active_histogram |= BIT(0); + break; + } + + stats->tcp = 0; + stats->udp = 0; +} + static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats, - enum rtw89_tfc_interval interval) + enum rtw89_tfc_interval interval, bool by_vif) { enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv; enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv; @@ -4564,6 +4630,9 @@ static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev, stats->rx_avg_len = stats->rx_cnt ? DIV_ROUND_DOWN_ULL(stats->rx_unicast, stats->rx_cnt) : 0; + if (by_vif) + rtw89_calc_vif_active_histogram(rtwdev, stats, interval); + stats->tx_unicast = 0; stats->rx_unicast = 0; stats->tx_cnt = 0; @@ -4585,11 +4654,11 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev) bool tfc_changed; tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats, - RTW89_TFC_INTERVAL_2SEC); + RTW89_TFC_INTERVAL_2SEC, false); rtw89_for_each_rtwvif(rtwdev, rtwvif) { rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats, - RTW89_TFC_INTERVAL_2SEC); + RTW89_TFC_INTERVAL_2SEC, true); rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) rtw89_fw_h2c_tp_offload(rtwdev, rtwvif_link); @@ -4598,7 +4667,8 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev) return tfc_changed; } -static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev) +static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev, + enum rtw89_tfc_interval interval) { struct ieee80211_vif *vif; struct rtw89_vif *rtwvif; @@ -4622,6 +4692,9 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev) if (!rtw89_core_bcn_track_can_lps(rtwdev)) continue; + if (!rtw89_core_proto_stats_can_lps(rtwdev, rtwvif, interval)) + continue; + rtw89_enter_lps(rtwdev, rtwvif, true); } } @@ -4771,13 +4844,13 @@ static void rtw89_track_ps_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_for_each_rtwvif(rtwdev, rtwvif) rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats_ps, - RTW89_TFC_INTERVAL_100MS); + RTW89_TFC_INTERVAL_100MS, true); if (rtwdev->scanning) return; if (rtwdev->lps_enabled && !rtwdev->btc.lps) - rtw89_enter_lps_track(rtwdev); + rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_100MS); } static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) @@ -4825,7 +4898,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_core_mlo_track(rtwdev); if (rtwdev->lps_enabled && !rtwdev->btc.lps) - rtw89_enter_lps_track(rtwdev); + rtw89_enter_lps_track(rtwdev, RTW89_TFC_INTERVAL_2SEC); } void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 2d7d3a11be16..ea123567ee17 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1477,6 +1477,10 @@ enum rtw89_tfc_lv { DECLARE_EWMA(tp, 10, 2); +#define RTW89_TCP_TH 40 +#define RTW89_UDP_RATIO_TH 70 +#define RTW89_RECENT_ACTIVE_HIST GENMASK(4, 0) + struct rtw89_traffic_stats { /* units in bytes */ u64 tx_unicast; @@ -1504,6 +1508,10 @@ struct rtw89_traffic_stats { u16 tx_rate; u16 rx_rate; + + /* used by rtwvif only */ + u64 tcp, udp, udp_ratio; + u8 active_histogram; }; struct rtw89_btc_chdef { @@ -6354,6 +6362,7 @@ struct rtw89_vif { struct rtw89_roc roc; bool offchan; + bool burst_active; enum rtw89_mlo_mode mlo_mode; struct rtw89_vif_ml_trans ml_trans; From 3da737c77764878ee05aaf6ec467a6996b5a232e Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Mon, 20 Apr 2026 11:40:38 +0800 Subject: [PATCH 023/285] wifi: rtw89: debug: disable hw_scan for latency-sensitive scenarios This helps avoid scan-triggered off-channel activity during latency-sensitive scenarios. Add RTW89_DM_HW_SCAN to the disabled_dm bitmap via debugfs and check it in the hw_scan path. When set, the driver rejects hw_scan requests and returns -EBUSY. Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/debug.c | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ea123567ee17..b2dfed064555 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5178,6 +5178,7 @@ enum rtw89_dm_type { RTW89_DM_THERMAL_PROTECT, RTW89_DM_TAS, RTW89_DM_MLO, + RTW89_DM_HW_SCAN, }; #define RTW89_THERMAL_PROT_LV_MAX 5 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 7d8d22311018..02386d1d07c6 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4347,6 +4347,7 @@ static const struct rtw89_disabled_dm_info { DM_INFO(THERMAL_PROTECT), DM_INFO(TAS), DM_INFO(MLO), + DM_INFO(HW_SCAN), }; static ssize_t diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 501c3af1da01..9ee2aa225976 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -1241,6 +1241,7 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; + struct rtw89_hal *hal = &rtwdev->hal; int ret; lockdep_assert_wiphy(hw->wiphy); @@ -1248,6 +1249,12 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return 1; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_HW_SCAN)) { + rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, + "reject hw scan due to disabled_dm\n"); + return -EBUSY; + } + if (rtwdev->scanning || rtwvif->offchan) return -EBUSY; From bd2d1abf74e49ac4f0350f8f076f26e38986487d Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Mon, 20 Apr 2026 11:40:39 +0800 Subject: [PATCH 024/285] wifi: rtw89: debug: disable inactive power save to reduce bus overhead This helps avoid excessive bus traffic and I/O overhead on slower HCIs (e.g., USB, SDIO) caused by frequent power state transitions. Add RTW89_DM_INACTIVE_PS to the disabled_dm bitmap via debugfs and check it in the rtw89_enter_ips path. When set, the driver skips entering inactive power save and returns immediately. Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/debug.c | 1 + drivers/net/wireless/realtek/rtw89/ps.c | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b2dfed064555..ac399ecaea6b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5179,6 +5179,7 @@ enum rtw89_dm_type { RTW89_DM_TAS, RTW89_DM_MLO, RTW89_DM_HW_SCAN, + RTW89_DM_INACTIVE_PS, }; #define RTW89_THERMAL_PROT_LV_MAX 5 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 02386d1d07c6..ebe3b1f9c39d 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4348,6 +4348,7 @@ static const struct rtw89_disabled_dm_info { DM_INFO(TAS), DM_INFO(MLO), DM_INFO(HW_SCAN), + DM_INFO(INACTIVE_PS), }; static ssize_t diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index 125cf14fa581..31bb5dcd284a 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -234,8 +234,14 @@ void rtw89_enter_ips(struct rtw89_dev *rtwdev) { struct rtw89_vif_link *rtwvif_link; struct rtw89_vif *rtwvif; + struct rtw89_hal *hal = &rtwdev->hal; unsigned int link_id; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_INACTIVE_PS)) { + rtw89_debug(rtwdev, RTW89_DBG_PS, "skip enter IPS due to disabled_dm\n"); + return; + } + set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags); if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) From e3ed82f19f13943d86990cba0263d2bd39f40a16 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Mon, 20 Apr 2026 11:40:40 +0800 Subject: [PATCH 025/285] wifi: rtw89: phy: support static PD level setting PD (Packet Detection) threshold is a key parameter in the DIG (Dynamic Initial Gain) algorithm that determines the sensitivity of packet detection. Current implementation only supports dynamic PD adjustment based on environment. This patch adds support for static PD threshold via debugfs allowing users to set a fixed PD value for testing or specific scenarios. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 + drivers/net/wireless/realtek/rtw89/debug.c | 94 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.c | 44 +++++++--- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ac399ecaea6b..0b212f6edc8b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5180,6 +5180,7 @@ enum rtw89_dm_type { RTW89_DM_MLO, RTW89_DM_HW_SCAN, RTW89_DM_INACTIVE_PS, + RTW89_DM_DIG_PD, }; #define RTW89_THERMAL_PROT_LV_MAX 5 @@ -5221,6 +5222,9 @@ struct rtw89_hal { u8 thermal_prot_th; u8 thermal_prot_lv; /* 0 ~ RTW89_THERMAL_PROT_LV_MAX */ + + u8 fixed_dig_pd_th; /* v = (X(dBm) + 102)/2 */ + s8 fixed_dig_cck_pd_th; /* dBm */ }; #define RTW89_MAX_MAC_ID_NUM 128 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index ebe3b1f9c39d..2cb6e441b2f8 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 phy_info; struct rtw89_debugfs_priv stations; struct rtw89_debugfs_priv disable_dm; + struct rtw89_debugfs_priv static_pd_th; struct rtw89_debugfs_priv mlo_mode; struct rtw89_debugfs_priv beacon_info; struct rtw89_debugfs_priv diag_mac; @@ -4349,6 +4350,7 @@ static const struct rtw89_disabled_dm_info { DM_INFO(MLO), DM_INFO(HW_SCAN), DM_INFO(INACTIVE_PS), + DM_INFO(DIG_PD), }; static ssize_t @@ -4394,6 +4396,96 @@ rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, return count; } +#define RTW89_DIG_PD_TH_MIN_DBM -102 +#define RTW89_DIG_PD_TH_MAX_DBM -40 +#define RTW89_DIG_PD_TH_STEP 2 + +static s8 rtw89_dig_pd_th_to_dbm(u8 reg_val) +{ + return RTW89_DIG_PD_TH_MIN_DBM + RTW89_DIG_PD_TH_STEP * reg_val; +} + +static u8 rtw89_dig_pd_th_dbm_to_reg(s8 dbm) +{ + return (dbm - RTW89_DIG_PD_TH_MIN_DBM) / RTW89_DIG_PD_TH_STEP; +} + +static ssize_t +rtw89_debug_priv_static_pd_th_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; + bool disabled; + s8 pd_th_dbm; + s8 cck_pd_th; + + disabled = hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD); + + if (disabled) { + pd_th_dbm = rtw89_dig_pd_th_to_dbm(hal->fixed_dig_pd_th); + cck_pd_th = hal->fixed_dig_cck_pd_th; + + p += scnprintf(p, end - p, "DIG: static\n"); + p += scnprintf(p, end - p, "OFDM PD threshold: %d dBm\n", pd_th_dbm); + p += scnprintf(p, end - p, "CCK PD threshold: %d dBm\n", cck_pd_th); + } else { + p += scnprintf(p, end - p, "DIG: dynamic\n"); + } + + p += scnprintf(p, end - p, "\nUsage: echo [pd_th] > static_pd_th\n"); + p += scnprintf(p, end - p, " mode: 0 = dynamic, 1 = static\n"); + p += scnprintf(p, end - p, " pd_th: PD threshold in dBm (-102 ~ -40)\n"); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_static_pd_th_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + struct rtw89_hal *hal = &rtwdev->hal; + int ret; + u32 mode; + s32 pd_th_dbm; + + ret = sscanf(buf, "%u %d", &mode, &pd_th_dbm); + if (ret < 1) + return -EINVAL; + + if (mode > 1) + return -EINVAL; + + if (mode == 0) { + rtw89_core_dm_disable_clr(rtwdev, RTW89_DM_DIG_PD); + hal->fixed_dig_pd_th = 0; + hal->fixed_dig_cck_pd_th = 0; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "DIG static mode disabled\n"); + } else { + if (ret < 2 || pd_th_dbm < RTW89_DIG_PD_TH_MIN_DBM || + pd_th_dbm > RTW89_DIG_PD_TH_MAX_DBM) + return -EINVAL; + + rtw89_core_dm_disable_set(rtwdev, RTW89_DM_DIG_PD); + hal->fixed_dig_pd_th = clamp(rtw89_dig_pd_th_dbm_to_reg(pd_th_dbm), + 0, 0x1f); + /* CCK uses dBm directly */ + hal->fixed_dig_cck_pd_th = pd_th_dbm; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "DIG static mode: PD=0x%02x (%d dBm), CCK=%d dBm\n", + hal->fixed_dig_pd_th, + rtw89_dig_pd_th_to_dbm(hal->fixed_dig_pd_th), + hal->fixed_dig_cck_pd_th); + } + + return count; +} + static void rtw89_debug_mlo_mode_set_mlsr(struct rtw89_dev *rtwdev, unsigned int link_id) { @@ -4882,6 +4974,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .phy_info = rtw89_debug_priv_get(phy_info), .stations = rtw89_debug_priv_get(stations, RLOCK), .disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK), + .static_pd_th = rtw89_debug_priv_set_and_get(static_pd_th, 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), @@ -4930,6 +5023,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_r(phy_info); rtw89_debugfs_add_r(stations); rtw89_debugfs_add_rw(disable_dm); + rtw89_debugfs_add_rw(static_pd_th); rtw89_debugfs_add_rw(mlo_mode); rtw89_debugfs_add_r(beacon_info); rtw89_debugfs_add_r(diag_mac); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 1f249c297006..2663fc102e77 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -7170,11 +7170,19 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; struct rtw89_dig_info *dig = &bb->dig; + struct rtw89_hal *hal = &rtwdev->hal; u8 final_rssi, under_region = dig->pd_low_th_ofst; s8 cck_cca_th; u32 pd_val; - pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan); + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) { + pd_val = hal->fixed_dig_pd_th; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "Fixed DIG: PD_low=%d\n", pd_val); + } else { + pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan); + } dig->bak_dig = pd_val; rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, @@ -7182,17 +7190,24 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, dig_regs->pd_spatial_reuse_en, enable, bb->phy_idx); - if (!rtwdev->hal.support_cckpd) + if (!hal->support_cckpd) return; - final_rssi = min_t(u8, rssi, dig->igi_rssi); - under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan); - cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI); - pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX); + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) { + pd_val = hal->fixed_dig_cck_pd_th; - rtw89_debug(rtwdev, RTW89_DBG_DIG, - "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n", - final_rssi, cck_cca_th, under_region, pd_val); + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "Fixed DIG: cck_PD_low=((%d))dB\n", pd_val); + } else { + final_rssi = min_t(u8, rssi, dig->igi_rssi); + under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan); + cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI); + pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX); + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n", + final_rssi, cck_cca_th, under_region, pd_val); + } rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_reg, dig_regs->bmode_cca_rssi_limit_en, enable, bb->phy_idx); @@ -7323,6 +7338,7 @@ static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; struct rtw89_dig_info *dig = &bb->dig; + struct rtw89_hal *hal = &rtwdev->hal; bool en_dig; u32 pd_val; @@ -7331,10 +7347,16 @@ static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb if (pause_dig) { en_dig = false; - pd_val = 0; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) + pd_val = hal->fixed_dig_pd_th; + else + pd_val = 0; } else { en_dig = rtwdev->total_sta_assoc > 0; - pd_val = restore ? dig->bak_dig : 0; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) + pd_val = hal->fixed_dig_pd_th; + else + pd_val = restore ? dig->bak_dig : 0; } rtw89_debug(rtwdev, RTW89_DBG_DIG, "%s <%s> PD_low=%d", __func__, From 08fdcb529df6df3562dd2b0035f88dd5be8b3c68 Mon Sep 17 00:00:00 2001 From: Shin-Yi Lin Date: Mon, 20 Apr 2026 11:40:41 +0800 Subject: [PATCH 026/285] wifi: rtw89: Correct data type for scan index to avoid infinite loop A kernel soft lockup was observed during Wi-Fi scanning on the 6GHz band. The CPU becomes stuck in rtw89_hw_scan_add_chan_ax for over 20 seconds, leading to a system panic. RIP points to 0f b6 c3 (movzbl %bl, %eax), which zero-extends the low 8 bits of RBX into RAX. RBX (the counter i) has reached a huge value: 0x137466a1. watchdog: BUG: soft lockup - CPU#2 stuck for 26s! [kworker/u16:4:6124] Workqueue: events_unbound cfg80211_wiphy_work [cfg80211] RIP: 0010:rtw89_hw_scan_add_chan_ax+0xb3/0x6e0 [rtw89_core] Code: a0 48 89 45 a8 44 89 6d 9c 44 89 75 98 eb 29 66 66 2e 0f 1f 84 00 00 00 00 00 66 66 2e 0f 1f 84 00 00 00 00 00 66 90 83 c3 01 <0f> b6 c3 41 3b 44 24 74 0f 83 0b 02 00 00 0f b6 c3 48 8d 14 80 49 RSP: 0018:ffffcb48cbaa39f8 EFLAGS: 00000202 RAX: 0000000000000005 RBX: 00000000137466a1 RCX: 0000000000000000 RDX: ffff89ffc9d851a8 RSI: 0000000000004f0d RDI: 0000000096af0130 RBP: ffffcb48cbaa3a60 R08: 0000000000000000 R09: ffff8a00b7502080 R10: ffff8a00b75ff600 R11: 0000000000000000 R12: ffff89ffc7553870 R13: ffff8a00b7ac8f19 R14: ffff8a00b75020d8 R15: ffff89ffc3d54d80 FS: 0000000000000000(0000) GS:ffff8a014f962000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007558d7f9f4c4 CR3: 0000000178040001 CR4: 00000000001706f0 Call Trace: rtw89_hw_scan_prep_chan_list_ax+0x8a/0x400 [rtw89_core] rtw89_hw_scan_start+0x546/0x8a0 [rtw89_core] ? rtw89_fw_h2c_default_cmac_tbl+0x13c/0x1f0 [rtw89_core] rtw89_ops_hw_scan+0xae/0x120 [rtw89_core] drv_hw_scan+0xbb/0x180 [mac80211] __ieee80211_start_scan+0x2fc/0x750 [mac80211] ieee80211_request_scan+0xe/0x20 [mac80211] ieee80211_scan+0x123/0x190 [mac80211] rdev_scan+0x40/0x110 [cfg80211] cfg80211_scan_6ghz+0x5a1/0xa30 [cfg80211] By objdump with source: for (i = 0; i < req->n_6ghz_params; i++) { 5fbc0: 83 c3 01 add $0x1,%ebx --> i++ 5fbc3: 0f b6 c3 movzbl %bl,%eax --> get counter fbc6: 41 3b 44 24 74 cmp 0x74(%r12),%eax * RBX: 00000000137466a1 -> %bl = a1 -> EAX = 000000a1 (161) Fixes: c6aa9a9c4725 ("wifi: rtw89: add RNR support for 6 GHz scan") Signed-off-by: Shin-Yi Lin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ffa04315a62b..b578f5316b5f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -8318,7 +8318,7 @@ static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev, struct sk_buff *skb; bool found; int ret = 0; - u8 i; + u32 i; if (!req->n_6ghz_params) return 0; From 72b781d3bdd38c0bc8dd3218826918287b9602fd Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Mon, 20 Apr 2026 11:40:42 +0800 Subject: [PATCH 027/285] wifi: rtw89: 8852bt: configure support_noise field explicitly RTL8852BT missed to configure support_noise field in chip_info. Although RTL8852BT doesn't support that, add the configuration to make things clear. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 7d86e9598ca8..c2184b092395 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -811,6 +811,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .support_ant_gain = true, .support_tas = false, .support_sar_by_ant = true, + .support_noise = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, From ae3d327515f2f46e2ca403f4a93494ef2050c764 Mon Sep 17 00:00:00 2001 From: Chia-Yuan Li Date: Mon, 20 Apr 2026 11:40:43 +0800 Subject: [PATCH 028/285] wifi: rtw89: add IO offload support via firmware Offload register write operations to the firmware to improve IO efficiency. Instead of the host performing direct bus access for every register, the operations are now encapsulated into firmware commands. This implementation allows the host to aggregate multiple register write actions into a single command buffer. By batching these operations, we reduce the number of individual transactions on the bus and minimize per-transfer overhead, leading to faster overall IO performance. Add a support_fw_cmd_ofld field to the chip_info structure to control whether firmware-offloaded batch writes are enabled. Enable it by default for RTL8832CU. Co-developed-by: Eric Huang Signed-off-by: Eric Huang Co-developed-by: Johnson Tsai Signed-off-by: Johnson Tsai Signed-off-by: Chia-Yuan Li Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 + drivers/net/wireless/realtek/rtw89/core.h | 82 ++++- drivers/net/wireless/realtek/rtw89/fw.c | 347 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 57 +++ drivers/net/wireless/realtek/rtw89/phy.h | 30 +- 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 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 1 + 12 files changed, 515 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c9c4ec1d93af..7fe979e47ad5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6302,6 +6302,8 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) struct rtw89_btc *btc = &rtwdev->btc; u8 band; + rtwdev->io = rtw89_fw_cmd_ofld_alloc_and_get_io_ops(rtwdev); + bitmap_or(rtwdev->quirks, rtwdev->quirks, &rtwdev->chip->default_quirks, NUM_OF_RTW89_QUIRKS); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0b212f6edc8b..bf5585c701ad 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -27,6 +27,7 @@ struct rtw89_phy_calc_efuse_gain; struct rtw89_debugfs; struct rtw89_regd_data; struct rtw89_wow_cam_info; +struct rtw89_fw_cmd_ofld_info; extern const struct ieee80211_ops rtw89_ops; @@ -4569,6 +4570,7 @@ struct rtw89_chip_info { bool support_tas; bool support_sar_by_ant; bool support_noise; + bool support_fw_cmd_ofld; bool ul_tb_waveform_ctrl; bool ul_tb_pwr_diff; bool rx_freq_from_ie; @@ -6202,10 +6204,26 @@ struct rtw89_tid_stats { bool started; }; +struct rtw89_io_ops { + int (*pack)(struct rtw89_dev *rtwdev); + int (*unpack)(struct rtw89_dev *rtwdev); + void (*do_udelay)(struct rtw89_dev *rtwdev, u32 us); + void (*do_mdelay)(struct rtw89_dev *rtwdev, u32 ms); + void (*write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data); + void (*write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data); + void (*write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data); + void (*phy_write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data); + void (*phy_write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data); + void (*phy_write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data); + void (*write_rf)(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data); +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; const struct ieee80211_ops *ops; + const struct rtw89_io_ops *io; bool dbcc_en; bool support_mlo; @@ -6227,6 +6245,8 @@ struct rtw89_dev { struct rtw89_rfe_data *rfe_data; enum rtw89_custid custid; + struct rtw89_fw_cmd_ofld_info *fw_cmd_ofld_info; + struct rtw89_sta_link __rcu *assoc_link_on_macid[RTW89_MAX_MAC_ID_NUM]; refcount_t refcount_ap_info; @@ -6688,6 +6708,38 @@ struct rtw89_tx_skb_data *RTW89_TX_SKB_CB(struct sk_buff *skb) return (struct rtw89_tx_skb_data *)info->driver_data; } +static inline int rtw89_io_pack(struct rtw89_dev *rtwdev) +{ + if (rtwdev->io->pack) + return rtwdev->io->pack(rtwdev); + + return 0; +} + +static inline int rtw89_io_unpack(struct rtw89_dev *rtwdev) +{ + if (rtwdev->io->unpack) + return rtwdev->io->unpack(rtwdev); + + return 0; +} + +static inline void rtw89_io_udelay(struct rtw89_dev *rtwdev, u32 us) +{ + if (rtwdev->io->do_udelay) + rtwdev->io->do_udelay(rtwdev, us); + else + udelay(us); +} + +static inline void rtw89_io_mdelay(struct rtw89_dev *rtwdev, u32 ms) +{ + if (rtwdev->io->do_mdelay) + rtwdev->io->do_mdelay(rtwdev, ms); + else + mdelay(ms); +} + static inline u8 rtw89_read8(struct rtw89_dev *rtwdev, u32 addr) { return rtwdev->hci.ops->read8(rtwdev, addr); @@ -6703,21 +6755,36 @@ static inline u32 rtw89_read32(struct rtw89_dev *rtwdev, u32 addr) return rtwdev->hci.ops->read32(rtwdev, addr); } -static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +static inline void rtw89_raw_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) { rtwdev->hci.ops->write8(rtwdev, addr, data); } -static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +static inline void rtw89_raw_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) { rtwdev->hci.ops->write16(rtwdev, addr, data); } -static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +static inline void rtw89_raw_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) { rtwdev->hci.ops->write32(rtwdev, addr, data); } +static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + rtwdev->io->write8(rtwdev, addr, data); +} + +static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + rtwdev->io->write16(rtwdev, addr, data); +} + +static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + rtwdev->io->write32(rtwdev, addr, data); +} + static inline void rtw89_write8_set(struct rtw89_dev *rtwdev, u32 addr, u8 bit) { @@ -6862,13 +6929,20 @@ rtw89_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, return rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask); } +static inline void +rtw89_raw_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, + u32 addr, u32 mask, u32 data) +{ + rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); +} + static inline void rtw89_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data) { lockdep_assert_wiphy(rtwdev->hw->wiphy); - rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); + rtwdev->io->write_rf(rtwdev, rf_path, addr, mask, data); } static inline u32 rtw89_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index b578f5316b5f..b5d2540cf212 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -11344,3 +11344,350 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev, return parms; } + +static int rtw89_fw_cmd_ofld_pack(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + info->pack_level++; + + return 0; +} + +static void rtw89_fw_cmd_ofld_flush(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + struct sk_buff *skb; + int ret; + u32 len; + + len = info->cnt * sizeof(info->cmds[0]); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "alloc skb fail\n"); + return; + } + + skb_put_data(skb, info->cmds, len); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_FW_OFLD, + H2C_FUNC_CMD_OFLD_PKT, 0, 0, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send cmd ofld\n"); + dev_kfree_skb_any(skb); + return; + } + + if (info->accu_delay) + fsleep(info->accu_delay); + + info->cnt = 0; + info->accu_delay = 0; +} + +static int rtw89_fw_cmd_ofld_unpack(struct rtw89_dev *rtwdev) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (info->pack_level == 0) + return -EFAULT; + + if (--info->pack_level != 0) + return 0; + + if (info->cnt == 0) + return 0; + + rtw89_fw_cmd_ofld_flush(rtwdev); + + return 0; +} + +static int rtw89_fw_cmd_ofld_enqueue(struct rtw89_dev *rtwdev, + const struct rtw89_fw_cmd_ofld_arg *cmd) +{ + struct rtw89_fw_cmd_ofld_info *info = rtwdev->fw_cmd_ofld_info; + struct rtw89_h2c_cmd_ofld *h2c; + u32 base_off; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (info->pack_level == 0) + return 1; + + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + if (cmd->type != RTW89_FW_CMD_OFLD_DELAY && + cmd->src != RTW89_FW_CMD_OFLD_SRC_RF && + cmd->src != RTW89_FW_CMD_OFLD_SRC_RF_DDIE && + !IS_ALIGNED(cmd->offset, 4)) + return -EFAULT; + + if (info->cnt >= ARRAY_SIZE(info->cmds)) + rtw89_fw_cmd_ofld_flush(rtwdev); + + h2c = &info->cmds[info->cnt++]; + + h2c->w0 = le32_encode_bits(cmd->src, RTW89_H2C_CMD_OFLD_W0_SRC) | + le32_encode_bits(cmd->type, RTW89_H2C_CMD_OFLD_W0_TYPE) | + le32_encode_bits(cmd->rf_path, RTW89_H2C_CMD_OFLD_W0_PATH) | + le32_encode_bits(info->cnt, RTW89_H2C_CMD_OFLD_W0_CMD_NUM) | + le32_encode_bits(cmd->offset, RTW89_H2C_CMD_OFLD_W0_OFFSET); + + if (cmd->src == RTW89_FW_CMD_OFLD_SRC_RF_DDIE) + base_off = 1; + else if (cmd->src == RTW89_FW_CMD_OFLD_SRC_RF) + base_off = 0; + else + base_off = cmd->offset >> 16; + + h2c->w1 = le32_encode_bits(cmd->id, RTW89_H2C_CMD_OFLD_W1_ID) | + le32_encode_bits(base_off, RTW89_H2C_CMD_OFLD_W1_BASE_OFFSET); + h2c->w2 = le32_encode_bits(cmd->value, RTW89_H2C_CMD_OFLD_W2_VALUE); + h2c->w3 = le32_encode_bits(cmd->mask, RTW89_H2C_CMD_OFLD_W3_MASK); + + if (cmd->type == RTW89_FW_CMD_OFLD_DELAY) + info->accu_delay += cmd->value; + + return 0; +} + +static const struct rtw89_io_ops rtw89_raw_io = { + .pack = NULL, + .unpack = NULL, + .write8 = rtw89_raw_write8, + .write16 = rtw89_raw_write16, + .write32 = rtw89_raw_write32, + .phy_write8 = rtw89_raw_phy_write8, + .phy_write16 = rtw89_raw_phy_write16, + .phy_write32 = rtw89_raw_phy_write32, + .write_rf = rtw89_raw_write_rf, +}; + +static +void rtw89_fw_cmd_ofld_phy_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 4), + .mask = RTW89_W8_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write8(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_phy_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 2), + .mask = RTW89_W16_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write16(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_phy_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = addr, + .mask = MASKDWORD, + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.phy_write32(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 4), + .mask = RTW89_W8_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write8(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = ALIGN_DOWN(addr, 2), + .mask = RTW89_W16_MASK_OF_ALIGNED_ADDR(addr), + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write16(rtwdev, addr, data); +} + +static +void rtw89_fw_cmd_ofld_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_MAC, + .type = RTW89_FW_CMD_OFLD_WRITE, + .offset = addr, + .mask = MASKDWORD, + .value = data, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write32(rtwdev, addr, data); +} + +static void rtw89_fw_cmd_ofld_write_rf_ddv(struct rtw89_dev *rtwdev, + struct rtw89_fw_cmd_ofld_arg *cmd, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + *cmd = (typeof(*cmd)){ + .src = RTW89_FW_CMD_OFLD_SRC_BB, + .type = RTW89_FW_CMD_OFLD_WRITE, + .rf_path = 0, /* Set rf_path to zero in rf_ddv */ + .offset = chip->rf_base_addr[rf_path] + ((addr & 0xff) << 2), + .mask = mask, + .value = data, + }; +} + +static void rtw89_fw_cmd_ofld_write_rf_dav(struct rtw89_dev *rtwdev, + struct rtw89_fw_cmd_ofld_arg *cmd, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + *cmd = (typeof(*cmd)){ + .src = RTW89_FW_CMD_OFLD_SRC_RF, + .type = RTW89_FW_CMD_OFLD_WRITE, + .rf_path = rf_path, + .offset = addr, + .mask = mask, + .value = data, + }; +} + +static void rtw89_fw_cmd_ofld_write_rf(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rf_path, u32 addr, u32 mask, + u32 data) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + bool ad_sel = u32_get_bits(addr, RTW89_RF_ADDR_ADSEL_MASK); + struct rtw89_fw_cmd_ofld_arg cmd; + int ret; + + if (unlikely(rf_path >= chip->rf_path_num)) { + rtw89_warn(rtwdev, "unsupported rf path (%d) in fw offload\n", rf_path); + return; + } + + if (ad_sel) + rtw89_fw_cmd_ofld_write_rf_ddv(rtwdev, &cmd, rf_path, addr, mask, data); + else + rtw89_fw_cmd_ofld_write_rf_dav(rtwdev, &cmd, rf_path, addr, mask, data); + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + rtw89_raw_io.write_rf(rtwdev, rf_path, addr, mask, data); +} + +static void rtw89_fw_cmd_ofld_udelay(struct rtw89_dev *rtwdev, u32 us) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_OTHER, + .type = RTW89_FW_CMD_OFLD_DELAY, + .value = us, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + udelay(us); +} + +static void rtw89_fw_cmd_ofld_mdelay(struct rtw89_dev *rtwdev, u32 ms) +{ + struct rtw89_fw_cmd_ofld_arg cmd = { + .src = RTW89_FW_CMD_OFLD_SRC_OTHER, + .type = RTW89_FW_CMD_OFLD_DELAY, + .value = ms * 1000, + }; + int ret; + + ret = rtw89_fw_cmd_ofld_enqueue(rtwdev, &cmd); + if (ret) + mdelay(ms); +} + +static const struct rtw89_io_ops rtw89_fw_cmd_ofld_io = { + .pack = rtw89_fw_cmd_ofld_pack, + .unpack = rtw89_fw_cmd_ofld_unpack, + .do_udelay = rtw89_fw_cmd_ofld_udelay, + .do_mdelay = rtw89_fw_cmd_ofld_mdelay, + .write8 = rtw89_fw_cmd_ofld_write8, + .write16 = rtw89_fw_cmd_ofld_write16, + .write32 = rtw89_fw_cmd_ofld_write32, + .phy_write8 = rtw89_fw_cmd_ofld_phy_write8, + .phy_write16 = rtw89_fw_cmd_ofld_phy_write16, + .phy_write32 = rtw89_fw_cmd_ofld_phy_write32, + .write_rf = rtw89_fw_cmd_ofld_write_rf, +}; + +const struct rtw89_io_ops * +rtw89_fw_cmd_ofld_alloc_and_get_io_ops(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_fw_cmd_ofld_info *info; + + if (!(rtwdev->hci.type == RTW89_HCI_TYPE_USB && chip->support_fw_cmd_ofld)) + return &rtw89_raw_io; + + info = devm_kzalloc(rtwdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return &rtw89_raw_io; + + rtwdev->fw_cmd_ofld_info = info; + + return &rtw89_fw_cmd_ofld_io; +} diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index db252d45e498..4d94a7195708 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3115,6 +3115,59 @@ struct rtw89_h2c_trx_protect { #define RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN BIT(0) #define RTW89_H2C_TRX_PROTECT_W1_DFS_EN BIT(1) +enum rtw89_fw_cmd_ofld_arg_src { + RTW89_FW_CMD_OFLD_SRC_BB, + RTW89_FW_CMD_OFLD_SRC_RF, + RTW89_FW_CMD_OFLD_SRC_MAC, + RTW89_FW_CMD_OFLD_SRC_RF_DDIE, + RTW89_FW_CMD_OFLD_SRC_OTHER, +}; + +enum rtw89_fw_cmd_ofld_arg_type { + RTW89_FW_CMD_OFLD_WRITE, + RTW89_FW_CMD_OFLD_COMPARE, + RTW89_FW_CMD_OFLD_DELAY, + RTW89_FW_CMD_OFLD_MOVE, +}; + +struct rtw89_fw_cmd_ofld_arg { + enum rtw89_fw_cmd_ofld_arg_src src; + enum rtw89_fw_cmd_ofld_arg_type type; + enum rtw89_rf_path rf_path; + u32 value; + u32 mask; + u32 offset; + u16 id; +}; + +struct rtw89_h2c_cmd_ofld { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; +} __packed; + +#define RTW89_H2C_CMD_OFLD_W0_SRC GENMASK(1, 0) +#define RTW89_H2C_CMD_OFLD_W0_TYPE GENMASK(3, 2) +#define RTW89_H2C_CMD_OFLD_W0_LC BIT(4) +#define RTW89_H2C_CMD_OFLD_W0_PATH GENMASK(6, 5) +#define RTW89_H2C_CMD_OFLD_W0_CMD_NUM GENMASK(14, 8) +#define RTW89_H2C_CMD_OFLD_W0_OFFSET GENMASK(31, 16) +#define RTW89_H2C_CMD_OFLD_W1_ID GENMASK(15, 0) +#define RTW89_H2C_CMD_OFLD_W1_BASE_OFFSET GENMASK(31, 16) +#define RTW89_H2C_CMD_OFLD_W2_VALUE GENMASK(31, 0) +#define RTW89_H2C_CMD_OFLD_W3_MASK GENMASK(31, 0) +#define RTW89_W8_MASK_OF_ALIGNED_ADDR(offset) (0xff << (((offset) & 0x3) << 3)) +#define RTW89_W16_MASK_OF_ALIGNED_ADDR(offset) (0xffff << (((offset) & 0x2) * 8)) + +#define RTW89_FW_CMD_OFLD_NR 125 +struct rtw89_fw_cmd_ofld_info { + unsigned int pack_level; + u32 cnt; + u32 accu_delay; + struct rtw89_h2c_cmd_ofld cmds[RTW89_FW_CMD_OFLD_NR]; +}; + struct rtw89_h2c_fwips { __le32 w0; } __packed; @@ -4606,6 +4659,7 @@ enum rtw89_fw_ofld_h2c_func { H2C_FUNC_MAC_MACID_PAUSE = 0x8, H2C_FUNC_USR_EDCA = 0xF, H2C_FUNC_TSF32_TOGL = 0x10, + H2C_FUNC_CMD_OFLD_PKT = 0x13, H2C_FUNC_OFLD_CFG = 0x14, H2C_FUNC_ADD_SCANOFLD_CH = 0x16, H2C_FUNC_SCANOFLD = 0x17, @@ -5737,4 +5791,7 @@ enum rtw89_wow_wakeup_ver { RTW89_WOW_REASON_NUM, }; +const struct rtw89_io_ops * +rtw89_fw_cmd_ofld_alloc_and_get_io_ops(struct rtw89_dev *rtwdev); + #endif diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index bde419edf744..3f9d306ff1ca 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -576,30 +576,48 @@ extern const struct rtw89_phy_gen_def rtw89_phy_gen_ax; extern const struct rtw89_phy_gen_def rtw89_phy_gen_be; extern const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1; -static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev, - u32 addr, u8 data) +static inline void rtw89_raw_phy_write8(struct rtw89_dev *rtwdev, + u32 addr, u8 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write8(rtwdev, addr + phy->cr_base, data); } -static inline void rtw89_phy_write16(struct rtw89_dev *rtwdev, - u32 addr, u16 data) +static inline void rtw89_raw_phy_write16(struct rtw89_dev *rtwdev, + u32 addr, u16 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write16(rtwdev, addr + phy->cr_base, data); } -static inline void rtw89_phy_write32(struct rtw89_dev *rtwdev, - u32 addr, u32 data) +static inline void rtw89_raw_phy_write32(struct rtw89_dev *rtwdev, + u32 addr, u32 data) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; rtw89_write32(rtwdev, addr + phy->cr_base, data); } +static inline void rtw89_phy_write8(struct rtw89_dev *rtwdev, + u32 addr, u8 data) +{ + rtwdev->io->phy_write8(rtwdev, addr, data); +} + +static inline void rtw89_phy_write16(struct rtw89_dev *rtwdev, + u32 addr, u16 data) +{ + rtwdev->io->phy_write16(rtwdev, addr, data); +} + +static inline void rtw89_phy_write32(struct rtw89_dev *rtwdev, + u32 addr, u32 data) +{ + rtwdev->io->phy_write32(rtwdev, addr, data); +} + static inline void rtw89_phy_write32_set(struct rtw89_dev *rtwdev, u32 addr, u32 bits) { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index ff4113eb9114..d9a144b4bf58 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2640,6 +2640,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .support_tas = false, .support_sar_by_ant = false, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index ea4d22616512..9cd4c88ee57e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2377,6 +2377,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_tas = false, .support_sar_by_ant = false, .support_noise = true, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 417b1502a2dd..13c942127225 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -973,6 +973,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_tas = false, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index c2184b092395..3fd5990a8bc4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -812,6 +812,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .support_tas = false, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_from_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9c2c05f80393..9ef469c1080e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3170,6 +3170,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .support_tas = true, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, .rx_freq_from_ie = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 3f94a5e80afe..ebb91fcbd6ab 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3173,6 +3173,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .support_tas = true, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_from_ie = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index a550ff8de8fc..d79cdbc67103 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2986,6 +2986,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .support_tas = false, .support_sar_by_ant = true, .support_noise = false, + .support_fw_cmd_ofld = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_from_ie = false, From 63586e841cf32ed7f86ed2c31c0386fc838c4290 Mon Sep 17 00:00:00 2001 From: Chia-Yuan Li Date: Mon, 20 Apr 2026 11:40:44 +0800 Subject: [PATCH 029/285] wifi: rtw89: offload DMAC and CMAC init IO to firmware During the initialization of DMAC and CMAC, the driver performs a large number of consecutive register writes. Sending these I/O requests individually over the host interface introduces significant latency and delays the device bring-up process. Optimize the initialization flow by wrapping the register write sequences in mac.c and mac_be.c with rtw89_io_pack() and rtw89_io_unpack(). This batches the operations and offloads them to the firmware via H2C commands, which significantly accelerates the overall initialization time. (8852C USB2.0 AMD B450 1379 ms -> 1273 ms) Signed-off-by: Chia-Yuan Li Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 53 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac_be.c | 57 ++++++++++++++++++++- 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 6f76ae2a99ce..353d7b88fb1a 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -2336,6 +2336,8 @@ static int preload_init_set_ax(struct rtw89_dev *rtwdev, u8 mac_idx, { u32 reg, max_preld_size, min_rsvd_size; + rtw89_io_pack(rtwdev); + max_preld_size = (mac_idx == RTW89_MAC_0 ? PRELD_B0_ENT_NUM : PRELD_B1_ENT_NUM) * PRELD_AMSDU_SIZE; reg = mac_idx == RTW89_MAC_0 ? @@ -2349,6 +2351,8 @@ static int preload_init_set_ax(struct rtw89_dev *rtwdev, u8 mac_idx, rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_NXT_TXENDWIN_MASK, PRELD_NEXT_WND); rtw89_write32_mask(rtwdev, reg, B_AX_B0_PRELD_NXT_RSVMINSZ_MASK, min_rsvd_size); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2428,11 +2432,15 @@ static int sta_sch_init_ax(struct rtw89_dev *rtwdev) return ret; } + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_AX_SS_CTRL, B_AX_SS_WARM_INIT_FLG); rtw89_write32_clr(rtwdev, R_AX_SS_CTRL, B_AX_SS_NONEMPTY_SS2FINFO_EN); _patch_ss2f_path(rtwdev); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2444,12 +2452,16 @@ static int mpdu_proc_init_ax(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + rtw89_write32(rtwdev, R_AX_ACTION_FWD0, TRXCFG_MPDU_PROC_ACT_FRWD); rtw89_write32(rtwdev, R_AX_TF_FWD, TRXCFG_MPDU_PROC_TF_FRWD); rtw89_write32_set(rtwdev, R_AX_MPDU_PROC, B_AX_APPEND_FCS | B_AX_A_ICV_ERR); rtw89_write32(rtwdev, R_AX_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2463,6 +2475,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + val = rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL); /* init clock */ val |= (B_AX_CLK_EN_CGCMP | B_AX_CLK_EN_WAPI | B_AX_CLK_EN_WEP_TKIP); @@ -2488,6 +2502,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev) rtw89_write32_mask(rtwdev, R_AX_SEC_DEBUG1, B_AX_TX_TIMEOUT_SEL_MASK, AX_TX_TO_VAL); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2571,6 +2587,8 @@ static int scheduler_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PREBKF_CFG_1, mac_idx); if (rtwdev->chip->chip_id == RTL8852C) rtw89_write32_mask(rtwdev, reg, B_AX_SIFS_MACTXEN_T1_MASK, @@ -2599,6 +2617,8 @@ static int scheduler_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) SCH_PREBKF_24US); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -2667,6 +2687,8 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + for (i = RTW89_MGNT; i <= RTW89_DATA; i++) { ret = rtw89_mac_typ_fltr_opt_ax(rtwdev, i, RTW89_FWD_TO_HOST, mac_idx); @@ -2683,6 +2705,8 @@ static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write16(rtwdev, rtw89_mac_reg_by_idx(rtwdev, R_AX_PLCP_HDR_FLTR, mac_idx), plcp_ftlr); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2729,6 +2753,8 @@ static int cca_ctrl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_CCA_CONTROL, mac_idx); val = rtw89_read32(rtwdev, reg); val |= (B_AX_TB_CHK_BASIC_NAV | B_AX_TB_CHK_BTCCA | @@ -2749,16 +2775,22 @@ static int cca_ctrl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) _patch_dis_resp_chk(rtwdev, mac_idx); + rtw89_io_unpack(rtwdev); + return 0; } static int nav_ctrl_init_ax(struct rtw89_dev *rtwdev) { + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_AX_WMAC_NAV_CTL, B_AX_WMAC_PLCP_UP_NAV_EN | B_AX_WMAC_TF_UP_NAV_EN | B_AX_WMAC_NAV_UPPER_EN); rtw89_write32_mask(rtwdev, R_AX_WMAC_NAV_CTL, B_AX_WMAC_NAV_UPPER_MASK, NAV_25MS); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2770,12 +2802,17 @@ static int spatial_reuse_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); if (ret) return ret; + + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_RX_SR_CTRL, mac_idx); rtw89_write8_clr(rtwdev, reg, B_AX_SR_EN); reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_BSSID_SRC_CTRL, mac_idx); rtw89_write8_set(rtwdev, reg, B_AX_PLCP_SRC_EN); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2788,6 +2825,8 @@ static int tmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_MAC_LOOPBACK, mac_idx); rtw89_write32_clr(rtwdev, reg, B_AX_MACLBK_EN); @@ -2798,6 +2837,8 @@ static int tmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_mask(rtwdev, reg, B_AX_TXDFIFO_HIGH_MCS_THRE_MASK, TXDFIFO_HIGH_MCS_THRE); rtw89_write32_mask(rtwdev, reg, B_AX_TXDFIFO_LOW_MCS_THRE_MASK, TXDFIFO_LOW_MCS_THRE); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2812,6 +2853,8 @@ static int trxptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_TRXPTCL_RESP_0, mac_idx); val = rtw89_read32(rtwdev, reg); val &= ~B_AX_WMAC_SPEC_SIFS_CCK_MASK; @@ -2842,6 +2885,8 @@ static int trxptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, rrsr->rsc.addr, mac_idx); rtw89_write32_mask(rtwdev, reg, rrsr->rsc.mask, rrsr->rsc.data); + rtw89_io_unpack(rtwdev); + return 0; } @@ -2876,6 +2921,8 @@ static int rmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (mac_idx == RTW89_MAC_0) rst_bacam(rtwdev); @@ -2917,6 +2964,8 @@ static int rmac_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_PLCP_HDR_FLTR, mac_idx); rtw89_write8_clr(rtwdev, reg, B_AX_VHT_SU_SIGB_CRC_CHK); + rtw89_io_unpack(rtwdev); + return ret; } @@ -2968,6 +3017,8 @@ static int ptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_SIFS_SETTING, mac_idx); val = rtw89_read32(rtwdev, reg); @@ -3005,6 +3056,8 @@ static int ptcl_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx) B_AX_AMPDU_MAX_LEN_VHT_MASK, 0x3FF80); } + rtw89_io_unpack(rtwdev); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 39a28fd27412..a0fa8e059739 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -1000,6 +1000,8 @@ static int mpdu_proc_init_be(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, R_BE_MPDU_PROC, B_BE_APPEND_FCS); rtw89_write32(rtwdev, R_BE_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL | B_BE_CA_CHK_ADDRCAM_EN); @@ -1019,6 +1021,8 @@ static int mpdu_proc_init_be(struct rtw89_dev *rtwdev) val32 = u32_replace_bits(val32, 1, B_BE_FWD_WLAN_CPU_TYPE_1_MASK); rtw89_write32(rtwdev, R_BE_DISP_FWD_WLAN_0, val32); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1031,6 +1035,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev) if (ret) return ret; + rtw89_io_pack(rtwdev); + val32 = rtw89_read32(rtwdev, R_BE_SEC_ENG_CTRL); val32 |= B_BE_CLK_EN_CGCMP | B_BE_CLK_EN_WAPI | B_BE_CLK_EN_WEP_TKIP | B_BE_SEC_TX_ENC | B_BE_SEC_RX_DEC | @@ -1041,6 +1047,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_BE_SEC_MPDU_PROC, B_BE_APPEND_ICV | B_BE_APPEND_MIC); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1083,13 +1091,16 @@ static int mlo_init_be(struct rtw89_dev *rtwdev) u32 reg; int ret; - val32 = rtw89_read32(rtwdev, R_BE_MLO_INIT_CTL); + rtw89_io_pack(rtwdev); + val32 = rtw89_read32(rtwdev, R_BE_MLO_INIT_CTL); val32 |= B_BE_MLO_TABLE_REINIT; rtw89_write32(rtwdev, R_BE_MLO_INIT_CTL, val32); val32 &= ~B_BE_MLO_TABLE_REINIT; rtw89_write32(rtwdev, R_BE_MLO_INIT_CTL, val32); + rtw89_io_unpack(rtwdev); + ret = read_poll_timeout_atomic(rtw89_read32, val32, val32 & B_BE_MLO_TABLE_INIT_DONE, 1, 1000, false, rtwdev, R_BE_MLO_INIT_CTL); @@ -1101,9 +1112,13 @@ static int mlo_init_be(struct rtw89_dev *rtwdev) else reg = R_BE_SS_CTRL_V1; + rtw89_io_pack(rtwdev); + rtw89_write32_set(rtwdev, reg, B_BE_MLO_HW_CHGLINK_EN); rtw89_write32_set(rtwdev, R_BE_CMAC_SHARE_ACQCHK_CFG_0, B_BE_R_MACID_ACQ_CHK_EN); + rtw89_io_unpack(rtwdev); + return ret; } @@ -1173,6 +1188,8 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (chip->chip_id == RTL8922D) { reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SCH_EXT_CTRL, mac_idx); rtw89_write32_set(rtwdev, reg, B_BE_CWCNT_PLUS_MODE); @@ -1216,6 +1233,8 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_set(rtwdev, reg, B_BE_TX_NAV_RST_EDCA_EN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1292,6 +1311,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) u32 reg; u32 val; + rtw89_io_pack(rtwdev); + rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_MGNT, RTW89_FWD_TO_HOST, mac_idx); rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_CTRL, RTW89_FWD_TO_HOST, mac_idx); rtw89_mac_typ_fltr_opt_be(rtwdev, RTW89_DATA, RTW89_FWD_TO_HOST, mac_idx); @@ -1309,6 +1330,8 @@ static int rx_fltr_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) B_BE_LSIG_PARITY_CHK_EN | B_BE_CCK_SIG_CHK | B_BE_CCK_CRC_CHK; rtw89_write16(rtwdev, reg, val); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1322,6 +1345,8 @@ static int nav_ctrl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) u32 val32; u32 reg; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMAC_NAV_CTL, mac_idx); val32 = rtw89_read32(rtwdev, reg); @@ -1337,6 +1362,8 @@ static int nav_ctrl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TRXPTCL_RESP_0, mac_idx); rtw89_write32_set(rtwdev, reg, B_BE_WMAC_MBA_DUR_FORCE); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1349,12 +1376,16 @@ static int spatial_reuse_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RX_SR_CTRL, mac_idx); rtw89_write8_clr(rtwdev, reg, B_BE_SR_EN | B_BE_SR_CTRL_PLCP_EN); reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_BSSID_SRC_CTRL, mac_idx); rtw89_write8_set(rtwdev, reg, B_BE_PLCP_SRC_EN); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1364,6 +1395,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) struct rtw89_hal *hal = &rtwdev->hal; u32 reg; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_TB_PPDU_CTRL, mac_idx); rtw89_write32_clr(rtwdev, reg, B_BE_QOSNULL_UPD_MUEDCA_EN); @@ -1378,6 +1411,8 @@ static int tmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_clr(rtwdev, reg, CLEAR_DTOP_DIS); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1394,6 +1429,8 @@ static int trxptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MAC_LOOPBACK, mac_idx); val32 = rtw89_read32(rtwdev, reg); val32 = u32_replace_bits(val32, S_BE_MACLBK_PLCP_DLY_DEF, @@ -1453,6 +1490,8 @@ static int trxptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_mask(rtwdev, reg, B_BE_RSC_MASK, 1); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1495,6 +1534,8 @@ static int rmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) return ret; } + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_DLK_PROTECT_CTL, mac_idx); val16 = rtw89_read16(rtwdev, reg); val16 = u16_replace_bits(val16, TRXCFG_RMAC_DATA_TO, B_BE_RX_DLK_DATA_TIME_MASK); @@ -1534,6 +1575,8 @@ static int rmac_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) rtw89_write32_set(rtwdev, reg, B_BE_DIS_CHK_MIN_LEN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1555,10 +1598,14 @@ static int resp_pktctl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) return ret; } + rtw89_io_pack(rtwdev); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_RESP_CSI_RESERVED_PAGE, mac_idx); rtw89_write32_mask(rtwdev, reg, B_BE_CSI_RESERVED_START_PAGE_MASK, qt_cfg.pktid); rtw89_write32_mask(rtwdev, reg, B_BE_CSI_RESERVED_PAGE_NUM_MASK, qt_cfg.pg_num + 1); + rtw89_io_unpack(rtwdev); + return 0; } @@ -1602,6 +1649,8 @@ static int ptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) if (ret) return ret; + rtw89_io_pack(rtwdev); + if (is_qta_poh(rtwdev)) { reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SIFS_SETTING, mac_idx); val32 = rtw89_read32(rtwdev, reg); @@ -1671,6 +1720,8 @@ static int ptcl_init_be(struct rtw89_dev *rtwdev, u8 mac_idx) B_BE_PRELD_HIQ_P0_EN); } + rtw89_io_unpack(rtwdev); + return 0; } @@ -1965,6 +2016,8 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx, if (!(chip->chip_id == RTL8922A || rtw89_mac_chk_preload_allow(rtwdev))) return 0; + rtw89_io_pack(rtwdev); + max_preld_size = mac_idx == RTW89_MAC_0 ? PRELD_B0_ENT_NUM : PRELD_B1_ENT_NUM; if (chip->chip_id == RTL8922D) @@ -1997,6 +2050,8 @@ static int preload_init_be(struct rtw89_dev *rtwdev, u8 mac_idx, val32 |= B_BE_B0_PRELD_FEN; rtw89_write32(rtwdev, reg, val32); + rtw89_io_unpack(rtwdev); + return 0; } From 850216ff7d120ff0f067b964d56a0ab5132f80f1 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Mon, 20 Apr 2026 11:40:45 +0800 Subject: [PATCH 030/285] wifi: rtw89: use firmware offload for PHY and RF batch register writes Use firmware offload to perform batch register writes for PHY and RF. This reduces I/O operations, improves bus transfer efficiency, and decreases hardware initialization time. Performance improvement on USB 2.0 HCI: - rtw89_phy_init_bb_reg: 61 ms -> 5 ms - rtw89_phy_init_rf_reg: 587 ms -> 106 ms Use offload-aware delay functions (do_udelay and do_mdelay) for the delay calls in rtw89_phy_init_reg, rtw89_phy_config_rf_reg, and rtw89_phy_config_rf_reg_v1 to ensure compatibility with batch register writes. Signed-off-by: Eric Huang Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 38 ++++++++++++++++-------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 2663fc102e77..02120132aa14 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1352,17 +1352,17 @@ static void rtw89_phy_config_bb_reg(struct rtw89_dev *rtwdev, u32 addr; if (reg->addr == 0xfe) { - mdelay(50); + rtw89_io_mdelay(rtwdev, 50); } else if (reg->addr == 0xfd) { - mdelay(5); + rtw89_io_mdelay(rtwdev, 5); } else if (reg->addr == 0xfc) { - mdelay(1); + rtw89_io_mdelay(rtwdev, 1); } else if (reg->addr == 0xfb) { - udelay(50); + rtw89_io_udelay(rtwdev, 50); } else if (reg->addr == 0xfa) { - udelay(5); + rtw89_io_udelay(rtwdev, 5); } else if (reg->addr == 0xf9) { - udelay(1); + rtw89_io_udelay(rtwdev, 1); } else if (reg->data == BYPASS_CR_DATA) { rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Bypass CR 0x%x\n", reg->addr); } else { @@ -1692,17 +1692,17 @@ void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev, void *extra_data) { if (reg->addr == 0xfe) { - mdelay(50); + rtw89_io_mdelay(rtwdev, 50); } else if (reg->addr == 0xfd) { - mdelay(5); + rtw89_io_mdelay(rtwdev, 5); } else if (reg->addr == 0xfc) { - mdelay(1); + rtw89_io_mdelay(rtwdev, 1); } else if (reg->addr == 0xfb) { - udelay(50); + rtw89_io_udelay(rtwdev, 50); } else if (reg->addr == 0xfa) { - udelay(5); + rtw89_io_udelay(rtwdev, 5); } else if (reg->addr == 0xf9) { - udelay(1); + rtw89_io_udelay(rtwdev, 1); } else { rtw89_write_rf(rtwdev, rf_path, reg->addr, 0xfffff, reg->data); rtw89_phy_cofig_rf_reg_store(rtwdev, reg, rf_path, @@ -1715,6 +1715,11 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, void *extra_data) { + if (reg->addr == 0xfe) { + rtw89_io_mdelay(rtwdev, 50); + return; + } + rtw89_write_rf(rtwdev, rf_path, reg->addr, RFREG_MASK, reg->data); if (reg->addr < 0x100) @@ -1885,11 +1890,16 @@ void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev) const struct rtw89_phy_table *bb_gain_table; bb_table = elm_info->bb_tbl ? elm_info->bb_tbl : chip->bb_table; + + rtw89_io_pack(rtwdev); + rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, NULL); if (rtwdev->dbcc_en) rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, (void *)RTW89_PHY_1); + rtw89_io_unpack(rtwdev); + rtw89_chip_init_txpwr_unit(rtwdev); bb_gain_table = elm_info->bb_gain ? elm_info->bb_gain : chip->bb_gain_table; @@ -2016,7 +2026,11 @@ void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio) else config = rf_table->config ? rf_table->config : rtw89_phy_config_rf_reg; + + rtw89_io_pack(rtwdev); rtw89_phy_init_reg(rtwdev, rf_table, by_acv, config, (void *)rf_reg_info); + rtw89_io_unpack(rtwdev); + if (rtw89_phy_config_rf_reg_fw(rtwdev, rf_reg_info)) rtw89_warn(rtwdev, "rf path %d reg h2c config failed\n", rf_reg_info->rf_path); From 89058548dd146ee9a56aeb0727d306a7f9720473 Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Mon, 20 Apr 2026 11:40:46 +0800 Subject: [PATCH 031/285] wifi: rtw89: 8832cu: Add ID 2c7c:8206 for RTL8832CU This ID is used by RTL8832CU-based Quectel USB dongle devices. Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 092d2812a4d5..790fd1dec66d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -53,6 +53,8 @@ static const struct usb_device_id rtw_8852cu_id_table[] = { .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x8206, 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), From 8beba37615f2f59ea7e978843daf2f5ec7f03868 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Mon, 20 Apr 2026 11:40:47 +0800 Subject: [PATCH 032/285] wifi: rtw89: use struct to fill C2H recv ack This C2H packet is used to indicate that FW has received the corresponding H2C command. No logic is changed. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.h | 18 ++++++++++-------- drivers/net/wireless/realtek/rtw89/mac.c | 13 ++++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 4d94a7195708..6ef53fcd0cce 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3886,14 +3886,16 @@ struct rtw89_c2h_done_ack { #define RTW89_C2H_SCAN_DONE_ACK_RETURN GENMASK(5, 0) #define RTW89_C2H_DONE_ACK_W2_H2C_SEQ GENMASK(31, 24) -#define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(1, 0)) -#define RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(7, 2)) -#define RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(15, 8)) -#define RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h) \ - le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(23, 16)) +struct rtw89_c2h_rev_ack { + __le32 w0; + __le32 w1; + __le32 w2; +} __packed; + +#define RTW89_C2H_REV_ACK_W2_CAT GENMASK(1, 0) +#define RTW89_C2H_REV_ACK_W2_CLASS GENMASK(7, 2) +#define RTW89_C2H_REV_ACK_W2_FUNC GENMASK(15, 8) +#define RTW89_C2H_REV_ACK_W2_H2C_SEQ GENMASK(23, 16) struct rtw89_fw_c2h_log_fmt { __le16 signature; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 353d7b88fb1a..3f8275bfe91b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5442,16 +5442,19 @@ rtw89_mac_c2h_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, } static void -rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len) { /* N.B. This will run in interrupt context. */ + const struct rtw89_c2h_rev_ack *c2h = (const void *)skb_c2h->data; + + u8 h2c_cat = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CAT); + u8 h2c_class = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CLASS); + u8 h2c_func = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_FUNC); + u8 h2c_seq = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_H2C_SEQ); rtw89_debug(rtwdev, RTW89_DBG_FW, "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n", - RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h->data), - RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h->data)); + h2c_cat, h2c_class, h2c_func, h2c_seq); } static void From 6a9b4babeb9b555a6b16df2e9e3238c68c61edbb Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Mon, 20 Apr 2026 11:40:48 +0800 Subject: [PATCH 033/285] wifi: rtw89: check scan C2H event recv ack instead of C2H event done ack The original flow checks the scan offload C2H event done ack to increase the scan sequence. However, the scan offload response C2H event may be received earlier than the scan offload C2H event done ack, which causes these C2H event to be dropped by rtw89_fw_c2h_purge_obsoleted_scan_events() check because the scan sequence of the scan offload response C2H event is less than the scan offload C2H event done ack. Therefore, checking the scan offload C2H event recv ack instead of C2H event done ack to fix this issue. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 3f8275bfe91b..87e3320b5a81 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5451,10 +5451,30 @@ rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len u8 h2c_class = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_CLASS); u8 h2c_func = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_FUNC); u8 h2c_seq = le32_get_bits(c2h->w2, RTW89_C2H_REV_ACK_W2_H2C_SEQ); + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; rtw89_debug(rtwdev, RTW89_DBG_FW, "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n", h2c_cat, h2c_class, h2c_func, h2c_seq); + + if (h2c_cat != H2C_CAT_MAC) + return; + + switch (h2c_class) { + default: + return; + case H2C_CL_MAC_FW_OFLD: + switch (h2c_func) { + default: + return; + case H2C_FUNC_SCANOFLD: + case H2C_FUNC_SCANOFLD_BE: + scan_info->seq++; + break; + } + + return; + } } static void @@ -5462,7 +5482,6 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le { /* N.B. This will run in interrupt context. */ struct rtw89_wait_info *fw_ofld_wait = &rtwdev->mac.fw_ofld_wait; - struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw89_wait_info *ps_wait = &rtwdev->mac.ps_wait; const struct rtw89_c2h_done_ack *c2h = (const struct rtw89_c2h_done_ack *)skb_c2h->data; @@ -5505,11 +5524,9 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; case H2C_FUNC_SCANOFLD: - scan_info->seq++; cond = RTW89_SCANOFLD_WAIT_COND_START; break; case H2C_FUNC_SCANOFLD_BE: - scan_info->seq++; cond = RTW89_SCANOFLD_BE_WAIT_COND_START; h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; From 602b58bb45b2f2bf848215560406ff87dc9a0ab6 Mon Sep 17 00:00:00 2001 From: Chih-Kang Chang Date: Mon, 20 Apr 2026 11:40:49 +0800 Subject: [PATCH 034/285] wifi: rtw89: suspend DIG when remain-on-channel The PD (Packet Detection) lower bound is set after the STA interface connects. When the P2P interface attempts to find peers, it enters the remain-on-channel flow to listen for probe requests. However, if the RSSI of the connected AP is too high, the P2P interface fails to receive it. To resolve this, suspend the DIG during the remain-on-channel period. Signed-off-by: Chih-Kang Chang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-15-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7fe979e47ad5..41eefe901ab2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4474,6 +4474,7 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); rtw89_write32_clr(rtwdev, reg, B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH); + rtw89_phy_dig_suspend(rtwdev); ieee80211_ready_on_channel(hw); wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); wiphy_delayed_work_queue(hw->wiphy, &rtwvif->roc.roc_work, @@ -4518,6 +4519,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_core_handle_sta_pending_tx(rtwdev, rtwvif_link); queue_work(rtwdev->txq_wq, &rtwdev->txq_work); + rtw89_phy_dig_resume(rtwdev, true); if (hw->conf.flags & IEEE80211_CONF_IDLE) wiphy_delayed_work_queue(hw->wiphy, &roc->roc_work, From 2da590b9ccf52d89fed87ec1c5399d1341ba568f Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Mon, 20 Apr 2026 11:40:50 +0800 Subject: [PATCH 035/285] wifi: rtw89: chan: introduce new helper to get entity current configuration The original helper can only queries target channel, but cannot determine current role's status, e.g. is it MLD or not. These things should be a set. Adding more and more helpers to query them individually without synchronous doesn't seem right. Introduce a new helper to query current channel set and role status. Besides, when single channel, e.g. MLO_2_PLUS_0_1RF and MLO_0_PLUS_2_1RF, the target channel pointer will be duplicated to fill the returned channel set. So, some callers can save trivial things for these cases. The returned channels will be non-NULL, so callers don't need trivial NULL check either. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-16-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 90 +++++++++++-------- drivers/net/wireless/realtek/rtw89/chan.h | 21 ++--- drivers/net/wireless/realtek/rtw89/core.c | 20 ++--- drivers/net/wireless/realtek/rtw89/phy.c | 12 ++- drivers/net/wireless/realtek/rtw89/phy_be.c | 8 +- .../net/wireless/realtek/rtw89/rtw8852b_rfk.c | 6 +- .../wireless/realtek/rtw89/rtw8852bt_rfk.c | 6 +- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 17 +--- .../net/wireless/realtek/rtw89/rtw8922a_rfk.c | 22 +---- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 28 ++++-- .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 22 +---- 11 files changed, 128 insertions(+), 124 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index ceb399fc2b94..cd846cb81f0c 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -398,43 +398,6 @@ static u8 rtw89_entity_role_get_index(struct rtw89_dev *rtwdev) } } -const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, - const char *caller_message, - u8 link_index, bool nullchk) -{ - struct rtw89_hal *hal = &rtwdev->hal; - struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; - enum rtw89_chanctx_idx chanctx_idx; - u8 role_index; - - lockdep_assert_wiphy(rtwdev->hw->wiphy); - - if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) { - WARN(1, "link index %u is invalid (max link inst num: %d)\n", - link_index, __RTW89_MLD_MAX_LINK_NUM); - goto dflt; - } - - role_index = rtw89_entity_role_get_index(rtwdev); - - chanctx_idx = mgnt->chanctx_tbl[role_index][link_index]; - if (chanctx_idx == RTW89_CHANCTX_IDLE) - goto dflt; - - return rtw89_chan_get(rtwdev, chanctx_idx); - -dflt: - if (unlikely(nullchk)) - return NULL; - - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "%s (%s): prefetch NULL on link index %u\n", - __func__, caller_message ?: "", link_index); - - return rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); -} -EXPORT_SYMBOL(__rtw89_mgnt_chan_get); - bool rtw89_entity_check_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { switch (rtwdev->mlo_dbcc_mode) { @@ -457,6 +420,59 @@ void rtw89_entity_force_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) rtw89_debug(rtwdev, RTW89_DBG_CHAN, "%s: (none)\n", __func__); } +void rtw89_entity_get_conf(struct rtw89_dev *rtwdev, struct rtw89_entity_conf *conf) +{ + struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; + enum rtw89_chanctx_idx idxes[ARRAY_SIZE(conf->chans)]; + struct rtw89_vif *role; + u8 ridx; + int i; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + memset(conf, 0, sizeof(*conf)); + + ridx = rtw89_entity_role_get_index(rtwdev); + role = mgnt->active_roles[ridx]; + if (role) { + struct ieee80211_vif *vif = rtwvif_to_vif(role); + + conf->is_mld = ieee80211_vif_is_mld(vif); + conf->en_emlsr = role->mlo_mode == RTW89_MLO_MODE_EMLSR; + } + + for (i = 0; i < ARRAY_SIZE(idxes); i++) + idxes[i] = RTW89_CHANCTX_IDLE; + + switch (rtwdev->mlo_dbcc_mode) { + default: + case MLO_2_PLUS_0_1RF: + set_bit(0, conf->hw_bitmap); + idxes[0] = mgnt->chanctx_tbl[ridx][0]; + idxes[1] = idxes[0]; + break; + case MLO_0_PLUS_2_1RF: + set_bit(1, conf->hw_bitmap); + idxes[1] = mgnt->chanctx_tbl[ridx][1]; + idxes[0] = idxes[1]; + break; + case MLO_1_PLUS_1_1RF: + set_bit(0, conf->hw_bitmap); + set_bit(1, conf->hw_bitmap); + idxes[0] = mgnt->chanctx_tbl[ridx][0]; + idxes[1] = mgnt->chanctx_tbl[ridx][1]; + break; + } + + for (i = 0; i < ARRAY_SIZE(idxes); i++) { + if (idxes[i] == RTW89_CHANCTX_IDLE) + idxes[i] = RTW89_CHANCTX_0; + + conf->chans[i] = rtw89_chan_get(rtwdev, idxes[i]); + } +} +EXPORT_SYMBOL(rtw89_entity_get_conf); + static enum rtw89_mlo_dbcc_mode rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) { diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h index c797cda2e763..a9a5f1b307a2 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.h +++ b/drivers/net/wireless/realtek/rtw89/chan.h @@ -116,6 +116,13 @@ struct rtw89_entity_weight { unsigned int active_roles; }; +struct rtw89_entity_conf { + bool is_mld; + bool en_emlsr; + DECLARE_BITMAP(hw_bitmap, __RTW89_MLD_MAX_LINK_NUM); + const struct rtw89_chan *chans[__RTW89_MLD_MAX_LINK_NUM]; +}; + static inline bool rtw89_get_entity_state(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { @@ -168,6 +175,7 @@ void rtw89_entity_init(struct rtw89_dev *rtwdev); enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev); bool rtw89_entity_check_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw89_entity_force_hw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw89_entity_get_conf(struct rtw89_dev *rtwdev, struct rtw89_entity_conf *conf); void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev); void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, @@ -180,19 +188,6 @@ void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev, const struct rtw89_chanctx_cb_parm *cb_parm); -const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, - const char *caller_message, - u8 link_index, bool nullchk); - -#define rtw89_mgnt_chan_get(rtwdev, link_index) \ - __rtw89_mgnt_chan_get(rtwdev, __func__, link_index, false) - -static inline const struct rtw89_chan * -rtw89_mgnt_chan_get_or_null(struct rtw89_dev *rtwdev, u8 link_index) -{ - return __rtw89_mgnt_chan_get(rtwdev, NULL, link_index, true); -} - struct rtw89_mcc_links_info { struct rtw89_vif_link *links[NUM_OF_RTW89_MCC_ROLES]; }; diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 41eefe901ab2..aeeba52b8c5e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -474,16 +474,16 @@ static void __rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev, void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan; + struct rtw89_entity_conf conf; - chan = rtw89_mgnt_chan_get(rtwdev, 0); - __rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_0); + rtw89_entity_get_conf(rtwdev, &conf); + + __rtw89_core_set_chip_txpwr(rtwdev, conf.chans[0], RTW89_PHY_0); if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) return; - chan = rtw89_mgnt_chan_get(rtwdev, 1); - __rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_1); + __rtw89_core_set_chip_txpwr(rtwdev, conf.chans[1], RTW89_PHY_1); } void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev, @@ -562,7 +562,7 @@ static void __rtw89_set_channel(struct rtw89_dev *rtwdev, int rtw89_set_channel(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan; + struct rtw89_entity_conf conf; enum rtw89_entity_mode mode; mode = rtw89_entity_recalc(rtwdev); @@ -571,14 +571,14 @@ int rtw89_set_channel(struct rtw89_dev *rtwdev) return -EINVAL; } - chan = rtw89_mgnt_chan_get(rtwdev, 0); - __rtw89_set_channel(rtwdev, chan, RTW89_MAC_0, RTW89_PHY_0); + rtw89_entity_get_conf(rtwdev, &conf); + + __rtw89_set_channel(rtwdev, conf.chans[0], RTW89_MAC_0, RTW89_PHY_0); if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) return 0; - chan = rtw89_mgnt_chan_get(rtwdev, 1); - __rtw89_set_channel(rtwdev, chan, RTW89_MAC_1, RTW89_PHY_1); + __rtw89_set_channel(rtwdev, conf.chans[1], RTW89_MAC_1, RTW89_PHY_1); return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 02120132aa14..95b5ace2a27e 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -6847,11 +6847,15 @@ static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev, static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; const u16 *fa_th_src = NULL; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + switch (chan->band_type) { case RTW89_BAND_2G: dig->lna_gain = dig->lna_gain_g; @@ -7181,14 +7185,18 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, u8 rssi, bool enable) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; struct rtw89_dig_info *dig = &bb->dig; struct rtw89_hal *hal = &rtwdev->hal; u8 final_rssi, under_region = dig->pd_low_th_ofst; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; s8 cck_cca_th; u32 pd_val; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DIG_PD)) { pd_val = hal->fixed_dig_pd_th; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 929fac1b10d2..25f1b068daa2 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -865,6 +865,7 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, { enum rtw89_phy_idx phy_idx = mac_idx != RTW89_MAC_0 ? RTW89_PHY_1 : RTW89_PHY_0; enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct rtw89_entity_conf conf; const struct rtw89_chan *chan; if (chip_id != RTL8922D) @@ -879,9 +880,10 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); - chan = rtw89_mgnt_chan_get(rtwdev, phy_idx); - if (chan) - rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[phy_idx]; + + rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); } static void rtw89_phy_bb_wrap_ul_pwr(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 70b1515c00fa..0d443edfe259 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -4167,11 +4167,15 @@ void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8852b_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0); struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {}; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; u8 idx; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[0]; + for (idx = 0; idx < ARRAY_SIZE(desc); idx++) { struct rtw89_rfk_chan_desc *p = &desc[idx]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c index 961b26ba2d3c..433807e712e4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c @@ -4234,11 +4234,15 @@ void rtw8852bt_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8852bt_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0); struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {}; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; u8 idx; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[0]; + for (idx = 0; idx < ARRAY_SIZE(desc); idx++) { struct rtw89_rfk_chan_desc *p = &desc[idx]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index ebb91fcbd6ab..3c453b93c52e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2054,7 +2054,7 @@ static void rtw8922a_digital_pwr_comp(struct rtw89_dev *rtwdev, static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; if (mode == MLO_1_PLUS_1_1RF || mode == DBCC_LEGACY) { rtw89_phy_write32_mask(rtwdev, R_DBCC, B_DBCC_EN, 0x1); @@ -2067,19 +2067,10 @@ static int rtw8922a_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode return -EOPNOTSUPP; } - if (mode == MLO_1_PLUS_1_1RF) { - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - } else if (mode == MLO_0_PLUS_2_1RF) { - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - } else { - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - } + rtw89_entity_get_conf(rtwdev, &conf); - rtw8922a_ctrl_afe_dac(rtwdev, chan0->band_width, RF_PATH_A); - rtw8922a_ctrl_afe_dac(rtwdev, chan1->band_width, RF_PATH_B); + rtw8922a_ctrl_afe_dac(rtwdev, conf.chans[0]->band_width, RF_PATH_A); + rtw8922a_ctrl_afe_dac(rtwdev, conf.chans[1]->band_width, RF_PATH_B); rtw89_phy_write32_mask(rtwdev, R_EMLSR, B_EMLSR_PARM, 0x6180); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c index 98f14b31cf52..1e2b563f2bca 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c @@ -278,27 +278,13 @@ static u8 rtw8922a_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev, static void rtw8922a_chlk_reload(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; u8 s0_tbl, s1_tbl; - switch (rtwdev->mlo_dbcc_mode) { - default: - case MLO_2_PLUS_0_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - break; - case MLO_0_PLUS_2_1RF: - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - break; - case MLO_1_PLUS_1_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - break; - } + rtw89_entity_get_conf(rtwdev, &conf); - s0_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, chan0, 0); - s1_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, chan1, 1); + s0_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0); + s1_tbl = rtw8922a_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1); rtw8922a_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); rtw8922a_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index d79cdbc67103..aa58a0a0d554 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -1943,10 +1943,19 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - const struct rtw89_chan *chan1 = rtw89_mgnt_chan_get(rtwdev, 1); + const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; - if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + rtw89_entity_get_conf(rtwdev, &conf); + chan0 = conf.chans[0]; + chan1 = conf.chans[1]; + + if (conf.en_emlsr) { + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_A, RTW89_PHY_0); + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_B, RTW89_PHY_0); + rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 1, RF_PATH_A, RTW89_PHY_1); + rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 1, RF_PATH_B, RTW89_PHY_1); + } else if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 0, RF_PATH_A, RTW89_PHY_0); rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 0, RF_PATH_B, RTW89_PHY_1); } else { @@ -1958,7 +1967,6 @@ static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev, static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode, bool pwr_comp) { - const struct rtw89_chan *chan1; u32 reg0, reg1; u8 cck_phy_idx; @@ -2016,8 +2024,10 @@ static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode rtw89_write32_mask(rtwdev, reg0, B_BBWRAP_ELMSR_EN_BE4, 0); rtw89_write32_mask(rtwdev, reg1, B_BBWRAP_ELMSR_EN_BE4, 0); } else if ((mode == MLO_1_PLUS_1_1RF) || (mode == DBCC_LEGACY)) { - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - cck_phy_idx = chan1->band_type == RTW89_BAND_2G ? + struct rtw89_entity_conf conf; + + rtw89_entity_get_conf(rtwdev, &conf); + cck_phy_idx = conf.chans[1]->band_type == RTW89_BAND_2G ? RTW89_PHY_1 : RTW89_PHY_0; rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, @@ -2475,9 +2485,11 @@ static void rtw8922d_set_txpwr(struct rtw89_dev *rtwdev, static void rtw8922d_set_txpwr_ctrl(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { - const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, phy_idx); + struct rtw89_entity_conf conf; - rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); + rtw89_entity_get_conf(rtwdev, &conf); + + rtw8922d_set_txpwr_ref(rtwdev, conf.chans[phy_idx], phy_idx); } static void rtw8922d_ctrl_trx_path(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c index 4e6a8e88a71e..7957f7b2d8e5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -187,27 +187,13 @@ static u8 rtw8922d_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev, static void rtw8922d_chlk_reload(struct rtw89_dev *rtwdev) { - const struct rtw89_chan *chan0, *chan1; + struct rtw89_entity_conf conf; u8 s0_tbl, s1_tbl; - switch (rtwdev->mlo_dbcc_mode) { - default: - case MLO_2_PLUS_0_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = chan0; - break; - case MLO_0_PLUS_2_1RF: - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - chan0 = chan1; - break; - case MLO_1_PLUS_1_1RF: - chan0 = rtw89_mgnt_chan_get(rtwdev, 0); - chan1 = rtw89_mgnt_chan_get(rtwdev, 1); - break; - } + rtw89_entity_get_conf(rtwdev, &conf); - s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan0, 0); - s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan1, 1); + s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0); + s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1); rtw8922d_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); rtw8922d_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); From b465bc7e18a7a2308db3658b1a4f7f55bad0fd3e Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Mon, 20 Apr 2026 11:40:51 +0800 Subject: [PATCH 036/285] wifi: rtw89: 8922d: update RF calibration flow for MLD RF calibration (RFK) will record the results in HW RFK tables. SW can select and configure the target HW RFK table to reuse the RFK result on the target channel. Though the above can work as before, when MLD runs eMLSR mode, a faster switching between links than SW is needed. So, introduce a new working flow for MLD. With the new settings, HW can easily switch RFK results between links during eMLSR mode. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260420034051.17666-17-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 7 ++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 70 +++++++++++++++---- .../net/wireless/realtek/rtw89/rtw8922d_rfk.h | 1 + 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 570d5883761b..1e1125235f0c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8466,6 +8466,8 @@ #define RR_MODOPT_V1 0x10001 #define RR_SW_SEL BIT(19) #define RR_TXG_SEL GENMASK(19, 17) +#define RR_TBL_SEL_IDX1 GENMASK(9, 8) +#define RR_TBL_SEL_IDX0 GENMASK(7, 6) #define RR_MODOPT_M_TXPWR GENMASK(5, 0) #define RR_WLSEL 0x02 #define RR_WLSEL_AG GENMASK(18, 16) @@ -10691,6 +10693,8 @@ #define R_KTBL0B_BE4 0x38204 #define B_KTBL0_IDX0 GENMASK(1, 0) #define B_KTBL0_IDX1 GENMASK(9, 8) +#define B_KTBL0_MLD_IDX0 GENMASK(25, 24) +#define B_KTBL0_MLD_IDX1 GENMASK(27, 26) #define B_KTBL0_RST BIT(31) #define R_KTBL1A_BE4 0x38154 #define R_KTBL1B_BE4 0x38254 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index aa58a0a0d554..ba736b967880 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2332,12 +2332,16 @@ static void rtw8922d_rfk_tssi(struct rtw89_dev *rtwdev, static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif_link->rtwvif); enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, chanctx_idx); enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, RF_AB, chanctx_idx); u32 tx_en; + if (ieee80211_vif_is_mld(vif)) + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, true); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_START); rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); _wait_rx_mode(rtwdev, RF_AB); @@ -2353,6 +2357,9 @@ static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev, rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_STOP); + + if (ieee80211_vif_is_mld(vif)) + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, false); } static void rtw8922d_rfk_band_changed(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c index 7957f7b2d8e5..a465e6631a22 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -105,26 +105,60 @@ static void rtw8922d_set_syn01(struct rtw89_dev *rtwdev, enum _rf_syn_pow syn) } } -static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx) +void rtw8922d_chlk_ktbl_ctl_mld(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool manual) { - bool mlo_linking = false; + if (manual) { + struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; + u8 path = phy_idx; /* numerical equal for target */ + u8 idx = rfk_mcc[path].table_idx; - if (idx > 2) { - rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL0, idx & BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1); + rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL0, idx & BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1); return; } - if (mlo_linking) { - if (kpath & RF_A) { - rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0); - rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0); - } + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x0); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0); +} - if (kpath & RF_B) { - rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0); - rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0); - } +static void rtw8922d_chlk_ktbl_sel_mld(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, u8 idx) +{ + if (phy_idx == RTW89_PHY_0) { + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_MLD_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_MLD_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TBL_SEL_IDX0, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TBL_SEL_IDX0, idx); + } else if (phy_idx == RTW89_PHY_1) { + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_MLD_IDX1, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_MLD_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TBL_SEL_IDX1, idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TBL_SEL_IDX1, idx); + } + rtw8922d_chlk_ktbl_ctl_mld(rtwdev, phy_idx, false); +} + +static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx) +{ + if (idx > 2) { + rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx); return; } @@ -195,8 +229,18 @@ static void rtw8922d_chlk_reload(struct rtw89_dev *rtwdev) s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[0], 0); s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, conf.chans[1], 1); + if (conf.is_mld) + goto mld; + rtw8922d_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); rtw8922d_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); + return; + +mld: + if (test_bit(RTW89_PHY_0, conf.hw_bitmap)) + rtw8922d_chlk_ktbl_sel_mld(rtwdev, RTW89_PHY_0, s0_tbl); + if (test_bit(RTW89_PHY_1, conf.hw_bitmap)) + rtw8922d_chlk_ktbl_sel_mld(rtwdev, RTW89_PHY_1, s1_tbl); } static enum _rf_syn_pow rtw8922d_get_syn_pow(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h index c5bbe0eb972a..11742b4a8f59 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h @@ -18,5 +18,6 @@ void rtw8922d_rfk_mlo_ctrl(struct rtw89_dev *rtwdev); void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8922d_lck_track(struct rtw89_dev *rtwdev); +void rtw8922d_chlk_ktbl_ctl_mld(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, bool manual); #endif From 6e76e9ed273dfb4b3333a5ebbb94958cc5752ab6 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 11:14:34 +0000 Subject: [PATCH 037/285] wifi: rtw88: fix OOB read from firmware RX descriptor exceeding DMA buffer In rtw_pci_rx_napi(), new_len is computed as the sum of pkt_len (14-bit descriptor field, max 16383) and pkt_offset (drv_info_sz + shift, both firmware-controlled). The result can exceed RTK_PCI_RX_BUF_SIZE (11478), causing an out-of-bounds read from the pre-allocated DMA buffer when skb_put_data copies new_len bytes. The USB transport already validates this (rtw_usb_rx_data_put checks against RTW_USB_MAX_RECVBUF_SZ); the PCIe path does not. Add a check that new_len does not exceed the DMA buffer size. Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver") Signed-off-by: Tristan Madani Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260421111434.3389674-1-tristmd@gmail.com --- drivers/net/wireless/realtek/rtw88/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index c56beacbb1b0..c2bf44e880cf 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1077,6 +1077,11 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, * discard the frame if none available */ new_len = pkt_stat.pkt_len + pkt_offset; + if (unlikely(new_len > RTK_PCI_RX_BUF_SIZE)) { + rtw_dbg(rtwdev, RTW_DBG_RX, + "oversized RX packet: %u\n", new_len); + goto next_rp; + } new = dev_alloc_skb(new_len); if (WARN_ONCE(!new, "rx routine starvation\n")) goto next_rp; From 6d88244bb129755acca696f9227200f4a2d106a6 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 11:14:42 +0000 Subject: [PATCH 038/285] wifi: rtw89: add bounds check on firmware mac_id in link lookup The mac_id field in RX descriptors is 8 bits wide (0-255), but assoc_link_on_macid[] has only RTW89_MAX_MAC_ID_NUM (128) entries. While the driver currently assigns mac_id values below 128, the descriptor value comes from firmware and is not validated before use as an array index. Add a defensive bounds check in rtw89_assoc_link_rcu_dereference() to guard against out-of-range firmware values. Fixes: 144c6cd24b35 ("wifi: rtw89: 8922a: configure AP_LINK_PS if FW supports") Signed-off-by: Tristan Madani Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260421111442.3395411-1-tristmd@gmail.com --- drivers/net/wireless/realtek/rtw89/core.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index bf5585c701ad..b290da650c70 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6527,6 +6527,9 @@ static inline void rtw89_assoc_link_clr(struct rtw89_sta_link *rtwsta_link) static inline struct rtw89_sta_link * rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid) { + if (unlikely(macid >= RTW89_MAX_MAC_ID_NUM)) + return NULL; + return rcu_dereference(rtwdev->assoc_link_on_macid[macid]); } From 329e9fff98f85520843e7d75b7033339e5fb2b7e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 24 Apr 2026 15:25:50 +0800 Subject: [PATCH 039/285] wifi: rtw89: pci: no need to wait CLK ready for RTL8922DE The bit is to wait for CLK ready when entering PCIE L1SS. However, a certain host might not enable CLK for a situation, causing device state stuck and trigger PCIE SER. Unset this bit as default like earlier chips. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260424072552.59220-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 2 ++ drivers/net/wireless/realtek/rtw89/pci_be.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index e7da37b9da7d..d0d1e2b99698 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -33,6 +33,8 @@ #define RAC_REG_REV2 0x1B #define BAC_CMU_EN_DLY_MASK GENMASK(15, 12) #define PCIE_DPHY_DLY_25US 0x1 +#define RAC_ANA14 0x14 +#define EIEOS_L1SS_WAIT_CLKRDY BIT(2) #define RAC_ANA19 0x19 #define B_PCIE_BIT_RD_SEL BIT(2) #define RAC_REG_FLD_0 0x1D diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index dfffec1ff3c7..473d491eb3f4 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -83,6 +83,17 @@ static void _patch_pcie_power_wake_be(struct rtw89_dev *rtwdev, bool power_up) rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, BIT_WAKE_CTRL_V1); } +static void _patch_pre_init_be(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + + if (!(rtwdev->chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + return; + + rtw89_write16_clr(rtwdev, R_RAC_DIRECT_OFFSET_BE_LANE0_G2 + + RAC_ANA14 * RAC_MULT, EIEOS_L1SS_WAIT_CLKRDY); +} + static void rtw89_pci_set_io_rcy_be(struct rtw89_dev *rtwdev) { const struct rtw89_pci_info *info = rtwdev->pci_info; @@ -475,6 +486,7 @@ static int rtw89_pci_ops_mac_pre_init_be(struct rtw89_dev *rtwdev) rtw89_pci_set_io_rcy_be(rtwdev); _patch_pcie_power_wake_be(rtwdev, true); + _patch_pre_init_be(rtwdev); rtw89_pci_ctrl_wpdma_pcie_be(rtwdev, false); rtw89_pci_ctrl_trxdma_pcie_be(rtwdev, MAC_AX_PCIE_DISABLE, MAC_AX_PCIE_DISABLE, MAC_AX_PCIE_DISABLE); From 1e53b826d5f8a3ca6d88fa6ed263acf54503214b Mon Sep 17 00:00:00 2001 From: Dian-Syuan Yang Date: Fri, 24 Apr 2026 15:25:51 +0800 Subject: [PATCH 040/285] wifi: rtw89: disable HTC field in AP mode Disable HE QoS HTC field when operating in AP mode to resolve interoperability issues with some stations. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260424072552.59220-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index aeeba52b8c5e..7e058c071cc4 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -947,6 +947,7 @@ __rtw89_core_tx_check_he_qos_htc(struct rtw89_dev *rtwdev, enum btc_pkt_type pkt_type) { struct rtw89_sta_link *rtwsta_link = tx_req->rtwsta_link; + struct ieee80211_vif *vif = tx_req->vif; struct sk_buff *skb = tx_req->skb; struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_link_sta *link_sta; @@ -978,6 +979,9 @@ __rtw89_core_tx_check_he_qos_htc(struct rtw89_dev *rtwdev, if (rtwsta_link && rtwsta_link->ra_report.might_fallback_legacy) return false; + if (vif->type == NL80211_IFTYPE_AP) + return false; + return true; } From d1fba55228685a7a237681be739683eaf698b9bc Mon Sep 17 00:00:00 2001 From: Dian-Syuan Yang Date: Fri, 24 Apr 2026 15:25:52 +0800 Subject: [PATCH 041/285] wifi: rtw89: disable CSI STBC for VHT 160MHz Fix interoperability problem where CSI feedback with STBC enabled at VHT 160MHz BW cannot be properly decoded by certain APs, causing CSI reports to be rejected. This problem is specific to Wi-Fi 7 chips, as Wi-Fi 6 defaults to 20MHz CSI BW. Therefore, disable STBC encoding for CSI transmission in VHT 160MHz mode to ensure CSI feedback is accepted by these APs and maintain smooth throughput. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260424072552.59220-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac_be.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index a0fa8e059739..dd0dbe78a1ed 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -2805,6 +2805,10 @@ static int rtw89_mac_set_csi_para_reg_be(struct rtw89_dev *rtwdev, sound_dim = min(sound_dim, t); } + /* Disable stbc_en in VHT 160MHz to avoid IOT issues with certain APs */ + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) + stbc_en = 0; + nc = min(nc, sound_dim); nr = min(nr, sound_dim); From 83d38df6929118c3f996b9e3351c2d5014073d87 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 25 Apr 2026 22:32:58 +0300 Subject: [PATCH 042/285] wifi: rtlwifi: rtl8821ae: Fix C2H bit location in RX descriptor Bit 28 of double word 2 in the RX descriptor indicates if the packet is a normal 802.11 frame, or a message from the wifi firmware to the driver (Card 2 Host). Commit f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros") mistakenly made the driver look for this bit in double word 1, causing packet loss and Bluetooth coexistence problems. Fixes: f5678bfe1cdc ("rtlwifi: rtl8821ae: Replace local bit manipulation macros") Cc: Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/04da7398-cedb-425a-a810-5772ab10139d@gmail.com --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h index 1155365348f3..d5de09d75f45 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h @@ -291,7 +291,7 @@ static inline int get_rx_desc_paggr(__le32 *__pdesc) static inline int get_rx_status_desc_rpt_sel(__le32 *__pdesc) { - return le32_get_bits(*(__pdesc + 1), BIT(28)); + return le32_get_bits(*(__pdesc + 2), BIT(28)); } static inline int get_rx_desc_rxmcs(__le32 *__pdesc) From ef771eabc79d5f21b63689cca0e0fa5493fa0a8a Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 29 Apr 2026 15:02:48 +0300 Subject: [PATCH 043/285] wifi: rtl8xxxu: Detect the maximum supported channel width Some devices malfunction when connected to a network with 40 MHz channel width, because they don't support that. RTL8188FU, RTL8192FU, and RTL8710BU (RTL8188GU) have a way to signal this (and some other capabilities) to the driver. Get this information from the hardware and advertise 40 MHz support only when the hardware can handle it. We assume the other chips can always handle it. RTL8710BU needs a different way to retrieve this information, which will be implemented some other time. Fixes: dbf9b7bb0edf ("wifi: rtl8xxxu: Enable 40 MHz width by default") Cc: stable@vger.kernel.org Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221394 Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/c57de68e-5d57-4c26-898f-8a284bb25381@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8188f.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192c.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192e.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8192f.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8710b.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8723a.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/8723b.c | 1 + drivers/net/wireless/realtek/rtl8xxxu/core.c | 64 +++++++++++++++++-- drivers/net/wireless/realtek/rtl8xxxu/regs.h | 2 + .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 7 ++ 11 files changed, 76 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c index 766a7a7c7d28..fea5aec9ced9 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c @@ -1866,6 +1866,7 @@ struct rtl8xxxu_fileops rtl8188eu_fops = { .has_tx_report = 1, .init_reg_pkt_life_time = 1, .gen2_thermal_meter = 1, + .hw_feature_report = 0, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c index 3abf14d7044f..5556aad1a7ad 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8188f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8188f.c @@ -1745,6 +1745,7 @@ struct rtl8xxxu_fileops rtl8188fu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 1, .ampdu_max_time = 0x70, .ustime_tsf_edca = 0x28, .max_aggr_num = 0x0c14, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c index 444872131c66..508322a8f310 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c @@ -723,6 +723,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), .supports_ap = 1, + .hw_feature_report = 0, .max_macid_num = 32, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c index 8e123bbfc665..690ace303150 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192e.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192e.c @@ -1751,6 +1751,7 @@ struct rtl8xxxu_fileops rtl8192eu_fops = { .has_s0s1 = 0, .gen2_thermal_meter = 1, .needs_full_init = 1, + .hw_feature_report = 0, .supports_ap = 1, .max_macid_num = 128, .max_sec_cam_num = 64, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c index cd2156b7a57a..5f076da4b550 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192f.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192f.c @@ -2074,6 +2074,7 @@ struct rtl8xxxu_fileops rtl8192fu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 1, .ampdu_max_time = 0x5e, .ustime_tsf_edca = 0x50, .max_aggr_num = 0x1f1f, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c index 11c63c320eae..a87e692bfc81 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8710b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8710b.c @@ -1852,6 +1852,7 @@ struct rtl8xxxu_fileops rtl8710bu_fops = { .init_reg_rxfltmap = 1, .init_reg_pkt_life_time = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 0, /* TODO, it's different */ .ampdu_max_time = 0x5e, /* * The RTL8710BU vendor driver uses 0x50 here and it works fine, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c index 4f4493d0bfc2..18e038b82f49 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c @@ -632,6 +632,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = { .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), + .hw_feature_report = 0, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c index cc2e60b06f64..e314ef991b38 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8723b.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8723b.c @@ -1746,6 +1746,7 @@ struct rtl8xxxu_fileops rtl8723bu_fops = { .gen2_thermal_meter = 1, .needs_full_init = 1, .init_reg_hmtfr = 1, + .hw_feature_report = 0, .ampdu_max_time = 0x5e, .ustime_tsf_edca = 0x50, .max_aggr_num = 0x0c14, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 508137e4a87a..646fe76b086e 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -14,6 +14,7 @@ */ #include +#include #include "regs.h" #include "rtl8xxxu.h" @@ -3915,6 +3916,46 @@ static inline u8 rtl8xxxu_get_macid(struct rtl8xxxu_priv *priv, return sta_info->macid; } +static void rtl8xxxu_request_hw_feature(struct rtl8xxxu_priv *priv) +{ + if (!priv->fops->hw_feature_report) + return; + + rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, C2H_HW_FEATURE_DUMP); +} + +static int rtl8xxxu_dump_hw_feature(struct rtl8xxxu_priv *priv) +{ + static const u8 bw_map[8] = { 0, 0, 160, 5, 10, 20, 40, 80 }; + struct rtl8xxxu_hw_feature *hw_feature = &priv->hw_feature; + u8 feature[13]; + int i, ret; + u8 id, bw; + + if (!priv->fops->hw_feature_report) { + hw_feature->max_bw = 40; + return 0; + } + + ret = read_poll_timeout(rtl8xxxu_read8, id, + id == C2H_HW_FEATURE_REPORT, + 10000, 800000, false, + priv, REG_C2HEVT_MSG_NORMAL); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(feature); i++) + feature[i] = rtl8xxxu_read8(priv, REG_C2HEVT_MSG_NORMAL + 2 + i); + + rtl8xxxu_write8(priv, REG_C2HEVT_MSG_NORMAL, 0); + + bw = u8_get_bits(feature[6], GENMASK(2, 0)); + + hw_feature->max_bw = bw_map[bw]; + + return 0; +} + static int rtl8xxxu_init_device(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv = hw->priv; @@ -3961,6 +4002,8 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) */ rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary); + rtl8xxxu_request_hw_feature(priv); + for (int retry = 5; retry >= 0 ; retry--) { ret = rtl8xxxu_download_firmware(priv); dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret); @@ -3976,6 +4019,12 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (ret) goto exit; + ret = rtl8xxxu_dump_hw_feature(priv); + if (ret) { + dev_err(dev, "failed to dump hw feature\n"); + goto exit; + } + if (fops->phy_init_antenna_selection) fops->phy_init_antenna_selection(priv); @@ -7844,15 +7893,20 @@ static int rtl8xxxu_probe(struct usb_interface *interface, sband->ht_cap.ht_supported = true; sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20; + + if (priv->hw_feature.max_bw >= 40) { + sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } else { + dev_info(&udev->dev, "hardware doesn't support HT40\n"); + } + memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs)); sband->ht_cap.mcs.rx_mask[0] = 0xff; sband->ht_cap.mcs.rx_mask[4] = 0x01; - if (priv->rf_paths > 1) { + if (priv->rf_paths > 1) sband->ht_cap.mcs.rx_mask[1] = 0xff; - sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - } sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/regs.h b/drivers/net/wireless/realtek/rtl8xxxu/regs.h index 0741db8d08bf..eafaef1438b9 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/regs.h @@ -447,6 +447,8 @@ /* 8188EU */ #define REG_32K_CTRL 0x0194 #define REG_C2HEVT_MSG_NORMAL 0x01a0 +#define C2H_HW_FEATURE_REPORT 0x19 +#define C2H_HW_FEATURE_DUMP 0xfd /* 8192EU/8723BU/8812 */ #define REG_C2HEVT_CMD_ID_8723B 0x01ae #define REG_C2HEVT_CLEAR 0x01af diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index 9fb2583ffffc..eeb18eb0e4c0 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -1789,11 +1789,17 @@ struct rtl8xxxu_cfo_tracking { #define RTL8XXXU_BC_MC_MACID1 1 #define RTL8XXXU_MAX_SEC_CAM_NUM 64 +struct rtl8xxxu_hw_feature { + u8 max_bw; +}; + struct rtl8xxxu_priv { struct ieee80211_hw *hw; struct usb_device *udev; struct rtl8xxxu_fileops *fops; + struct rtl8xxxu_hw_feature hw_feature; + spinlock_t tx_urb_lock; struct list_head tx_urb_free_list; int tx_urb_free_count; @@ -2009,6 +2015,7 @@ struct rtl8xxxu_fileops { u8 init_reg_pkt_life_time:1; u8 init_reg_hmtfr:1; u8 supports_concurrent:1; + u8 hw_feature_report:1; u8 ampdu_max_time; u8 ustime_tsf_edca; u16 max_aggr_num; From 7284f5be5d298c901be1fc9fda6a2476f5ffdbaf Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:19 +0800 Subject: [PATCH 044/285] wifi: rtw89: mlo: rearrange MLSR link decision flow The original MLSR link decision refers to RSSI, but it should be based on the premise of an existing link. Otherwise, make a link decision to select a new link from any available band. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7e058c071cc4..03d80d012022 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4746,13 +4746,19 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, { unsigned int sel_link_id = IEEE80211_MLD_MAX_NUM_LINKS; struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + u8 decided_bands = BIT(RTW89_BAND_NUM) - 1; struct rtw89_vif_link *rtwvif_link; const struct rtw89_chan *chan; unsigned long usable_links; unsigned int link_id; - u8 decided_bands; u8 rssi; + usable_links = ieee80211_vif_usable_links(vif); + + rtwvif_link = rtw89_get_designated_link(rtwvif); + if (unlikely(!rtwvif_link)) + goto select; + rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); if (unlikely(!rssi)) return; @@ -4764,12 +4770,6 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, else return; - usable_links = ieee80211_vif_usable_links(vif); - - rtwvif_link = rtw89_get_designated_link(rtwvif); - if (unlikely(!rtwvif_link)) - goto select; - chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); if (decided_bands & BIT(chan->band_type)) return; From 09d369c66373de8708e442a30460ced17f254915 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:20 +0800 Subject: [PATCH 045/285] wifi: rtw89: phy: support per PHY RX statistics Previously, RX statistics such as beacon RSSI and packet counters were shared across all PHYs. To support MLO, extend the statistics to be maintained per PHY. Update the debugfs output for phy_info and beacon_info to include a "[PHY X]" label for better clarity. The output of phy_info: TP TX: 0 [0] Mbps (lv: 0), RX: 0 [0] Mbps (lv: 0) Avg packet length: TX=0, RX=120 TF: 0 [PHY 0] Beacon: 19 (-45 dBm) RX count: Legacy: [0, 0, 0, 0] ... EHT 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] The output of beacon_info: [PHY 0] Beacon: 20 raw rssi: 131 hw rate: 4 length: 437 [Beacon info] interval: 100 dtim: 1 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 26 ++++-- drivers/net/wireless/realtek/rtw89/core.h | 6 +- drivers/net/wireless/realtek/rtw89/debug.c | 88 ++++++++++++------- drivers/net/wireless/realtek/rtw89/fw.c | 6 +- drivers/net/wireless/realtek/rtw89/phy.c | 17 ++-- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 5 +- 6 files changed, 97 insertions(+), 51 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 03d80d012022..81f3ae21dc18 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2736,12 +2736,14 @@ static u16 rtw89_bcn_get_histogram_bound(struct rtw89_dev *rtwdev, u8 target) } static u16 rtw89_bcn_get_rx_time(struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) + struct rtw89_vif_link *rtwvif_link) { #define RTW89_SYMBOL_TIME_2GHZ 192 #define RTW89_SYMBOL_TIME_5GHZ 20 #define RTW89_SYMBOL_TIME_6GHZ 20 - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + struct rtw89_pkt_stat *pkt_stat = &bb->cur_pkt_stat; u16 bitrate, val; if (!rtw89_legacy_rate_to_bitrate(rtwdev, pkt_stat->beacon_rate, &bitrate)) @@ -2772,15 +2774,15 @@ static void rtw89_bcn_calc_timeout(struct rtw89_dev *rtwdev, #define RTW89_BCN_TRACK_EXTEND_TIMEOUT 5 #define RTW89_BCN_TRACK_COVERAGE_TH 0 /* unit: TU */ #define RTW89_BCN_TRACK_STRONG_RSSI 80 - const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; + struct rtw89_pkt_stat *pkt_stat = &bb->cur_pkt_stat; struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; u16 outlier_high_bcn_th = bcn_track->outlier_high_bcn_th; u16 outlier_low_bcn_th = bcn_track->outlier_low_bcn_th; - u8 rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); u16 target_bcn_th = bcn_track->target_bcn_th; + u8 rssi = ewma_rssi_read(&bb->bcn_rssi); u16 low_bcn_th = bcn_track->low_bcn_th; u16 med_bcn_th = bcn_track->med_bcn_th; u16 beacon_int = bcn_track->beacon_int; @@ -2826,7 +2828,7 @@ static void rtw89_bcn_calc_timeout(struct rtw89_dev *rtwdev, bcn_timeout = bcn_stat->drift[target_bcn_th]; out: - bcn_track->bcn_timeout = bcn_timeout + rtw89_bcn_get_rx_time(rtwdev, chan); + bcn_track->bcn_timeout = bcn_timeout + rtw89_bcn_get_rx_time(rtwdev, rtwvif_link); } static void rtw89_bcn_update_timeout(struct rtw89_dev *rtwdev, @@ -2987,7 +2989,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct rtw89_vif_rx_stats_iter_data *iter_data = data; struct rtw89_dev *rtwdev = iter_data->rtwdev; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; struct rtw89_rx_desc_info *desc_info = iter_data->desc_info; struct sk_buff *skb = iter_data->skb; struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); @@ -2997,6 +2998,8 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct ieee80211_bss_conf *bss_conf; struct rtw89_vif_link *rtwvif_link; const u8 *bssid = iter_data->bssid; + struct rtw89_pkt_stat *pkt_stat; + struct rtw89_bb_ctx *bb; const u8 *target_bssid; if (rtwdev->scanning && @@ -3030,6 +3033,9 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, rx_status->link_id = rtwvif_link->link_id; } + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + pkt_stat = &bb->cur_pkt_stat; + if (ieee80211_is_beacon(hdr->frame_control)) { if (vif->type == NL80211_IFTYPE_STATION && !test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) { @@ -3038,7 +3044,7 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, } if (phy_ppdu) { - ewma_rssi_add(&rtwdev->phystat.bcn_rssi, phy_ppdu->rssi_avg); + ewma_rssi_add(&bb->bcn_rssi, phy_ppdu->rssi_avg); if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) rtwvif_link->bcn_bw_idx = phy_ppdu->bw_idx; } @@ -4750,6 +4756,7 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link; const struct rtw89_chan *chan; unsigned long usable_links; + struct rtw89_bb_ctx *bb; unsigned int link_id; u8 rssi; @@ -4759,7 +4766,8 @@ static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, if (unlikely(!rtwvif_link)) goto select; - rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + rssi = ewma_rssi_read(&bb->bcn_rssi); if (unlikely(!rssi)) return; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b290da650c70..f15a0c43ef6d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5345,9 +5345,6 @@ DECLARE_EWMA(thermal, 4, 4); struct rtw89_phy_stat { struct ewma_thermal avg_thermal[RF_PATH_MAX]; u8 last_thermal_max; - struct ewma_rssi bcn_rssi; - struct rtw89_pkt_stat cur_pkt_stat; - struct rtw89_pkt_stat last_pkt_stat; struct rtw89_beacon_stat bcn_stat; }; @@ -6318,6 +6315,9 @@ struct rtw89_dev { struct rtw89_dig_info dig; struct rtw89_phy_ch_info ch_info; struct rtw89_edcca_bak edcca_bak; + struct ewma_rssi bcn_rssi; + struct rtw89_pkt_stat cur_pkt_stat; + struct rtw89_pkt_stat last_pkt_stat; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 2cb6e441b2f8..2d953bec149b 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4051,38 +4051,20 @@ static const struct rtw89_rx_rate_cnt_info { {FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"}, }; -static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, - struct rtw89_debugfs_priv *debugfs_priv, - char *buf, size_t bufsz) +static int rtw89_get_rx_pkt_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) { - struct rtw89_traffic_stats *stats = &rtwdev->stats; - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; const struct rtw89_chip_info *chip = rtwdev->chip; - struct rtw89_debugfs_iter_data iter_data; const struct rtw89_rx_rate_cnt_info *info; - struct rtw89_hal *hal = &rtwdev->hal; + u8 rssi = ewma_rssi_read(&bb->bcn_rssi); char *p = buf, *end = buf + bufsz; enum rtw89_hw_rate first_rate; - u8 rssi; int i; - rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); - - p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", - stats->tx_throughput, stats->tx_throughput_raw, - stats->tx_tfc_lv); - if (hal->thermal_prot_lv) - p += scnprintf(p, end - p, ", duty: %d%%", - 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); - p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", - stats->rx_throughput, stats->rx_throughput_raw, - stats->rx_tfc_lv); - p += scnprintf(p, end - p, "Beacon: %u (%d dBm), TF: %u\n", + p += scnprintf(p, end - p, "Beacon: %u (%d dBm)\n", pkt_stat->beacon_nr, - RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); - p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", - stats->tx_avg_len, - stats->rx_avg_len); + RTW89_RSSI_RAW_TO_DBM(rssi)); p += scnprintf(p, end - p, "RX count:\n"); @@ -4103,6 +4085,39 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, p += scnprintf(p, end - p, "]\n"); } + return p - buf; +} + +static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_traffic_stats *stats = &rtwdev->stats; + struct rtw89_debugfs_iter_data iter_data; + struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + + p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", + stats->tx_throughput, stats->tx_throughput_raw, + stats->tx_tfc_lv); + if (hal->thermal_prot_lv) + p += scnprintf(p, end - p, ", duty: %d%%", + 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); + p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", + stats->rx_throughput, stats->rx_throughput_raw, + stats->rx_tfc_lv); + p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", + stats->tx_avg_len, + stats->rx_avg_len); + p += scnprintf(p, end - p, "TF: %u\n", stats->rx_tf_periodic); + + rtw89_for_each_active_bb(rtwdev, bb) { + p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); + p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); + } + p += scnprintf(p, end - p, "\n"); + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); p += iter_data.written_sz; @@ -4858,12 +4873,26 @@ rtw89_debug_priv_diag_mac_get(struct rtw89_dev *rtwdev, return rtw89_mac_diag_iter_all(rtwdev, buf, bufsz); } +static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr); + p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi)); + p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); + p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len); + + return p - buf; +} + static ssize_t rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, struct rtw89_debugfs_priv *debugfs_priv, char *buf, size_t bufsz) { - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; @@ -4871,17 +4900,16 @@ rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, char *p = buf, *end = buf + bufsz; u16 *drift = bcn_stat->drift; u8 bcn_num = bcn_stat->num; + struct rtw89_bb_ctx *bb; u8 count; u8 i; + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_beacon_info(rtwdev, bb, p, end - p); + p += scnprintf(p, end - p, "[Beacon info]\n"); - p += scnprintf(p, end - p, "count: %u\n", pkt_stat->beacon_nr); p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int); p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim); - p += scnprintf(p, end - p, "raw rssi: %lu\n", - ewma_rssi_read(&rtwdev->phystat.bcn_rssi)); - p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); - p += scnprintf(p, end - p, "length: %u\n", pkt_stat->beacon_len); p += scnprintf(p, end - p, "\n[Distribution]\n"); p += scnprintf(p, end - p, "tbtt\n"); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index b5d2540cf212..2a3662ed733f 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3223,14 +3223,15 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be; - struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; static const u8 bcn_bw_ofst[] = {0, 0, 0, 3, 6, 9, 0, 12}; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_h2c_lps_ml_cmn_info *h2c; struct rtw89_vif_link *rtwvif_link; + struct rtw89_pkt_stat *pkt_stat; const struct rtw89_chan *chan; u8 bw_idx = RTW89_BB_BW_20_40; + struct rtw89_bb_ctx *bb; u32 len = sizeof(*h2c); unsigned int link_id; struct sk_buff *skb; @@ -3261,11 +3262,14 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, path = rtwvif_link->phy_idx == RTW89_PHY_1 ? RF_PATH_B : RF_PATH_A; chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); gain_band = rtw89_subband_to_gain_band_be(chan->subband_type); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); h2c->central_ch[rtwvif_link->phy_idx] = chan->channel; h2c->pri_ch[rtwvif_link->phy_idx] = chan->primary_channel; h2c->band[rtwvif_link->phy_idx] = chan->band_type; h2c->bw[rtwvif_link->phy_idx] = chan->band_width; + + pkt_stat = &bb->cur_pkt_stat; if (pkt_stat->beacon_rate < RTW89_HW_RATE_OFDM6) h2c->bcn_rate_type[rtwvif_link->phy_idx] = 0x1; else diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 95b5ace2a27e..29770cc5ea60 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5814,6 +5814,7 @@ static void rtw89_phy_stat_rssi_update(struct rtw89_dev *rtwdev) static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) { struct rtw89_phy_stat *phystat = &rtwdev->phystat; + struct rtw89_bb_ctx *bb; int i; for (i = 0; i < rtwdev->chip->rf_path_num; i++) @@ -5821,24 +5822,28 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtw89_phy_stat_thermal_update(rtwdev); - memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat)); - memset(&phystat->last_pkt_stat, 0, sizeof(phystat->last_pkt_stat)); + rtw89_for_each_capab_bb(rtwdev, bb) { + memset(&bb->cur_pkt_stat, 0, sizeof(bb->cur_pkt_stat)); + memset(&bb->last_pkt_stat, 0, sizeof(bb->last_pkt_stat)); - ewma_rssi_init(&phystat->bcn_rssi); + ewma_rssi_init(&bb->bcn_rssi); + } rtwdev->hal.thermal_prot_lv = 0; } void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) { - struct rtw89_phy_stat *phystat = &rtwdev->phystat; + struct rtw89_bb_ctx *bb; rtw89_phy_stat_thermal_update(rtwdev); rtw89_phy_thermal_protect(rtwdev); rtw89_phy_stat_rssi_update(rtwdev); - phystat->last_pkt_stat = phystat->cur_pkt_stat; - memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat)); + rtw89_for_each_active_bb(rtwdev, bb) { + bb->last_pkt_stat = bb->cur_pkt_stat; + memset(&bb->cur_pkt_stat, 0, sizeof(bb->cur_pkt_stat)); + } } static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 9cd4c88ee57e..b81eeb59be60 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2158,13 +2158,14 @@ static void rtw8852a_query_ppdu(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct ieee80211_rx_status *status) { - u8 path; + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, phy_ppdu->phy_idx); u8 *rx_power = phy_ppdu->rssi; + u8 path; u8 raw; if (!status->signal) { if (phy_ppdu->to_self) - raw = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); + raw = ewma_rssi_read(&bb->bcn_rssi); else raw = max(rx_power[RF_PATH_A], rx_power[RF_PATH_B]); From 523d012f47e7371b653692cdff1fa2eab58fe3cb Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:21 +0800 Subject: [PATCH 046/285] wifi: rtw89: debug: bb_info entry including TX rate count for WiFi 7 chips Enhance TX performance visibility for WiFi 7 chips by introducing TX rate count tracking. This is critical for debugging and validation. Additionally, introduce a new debugfs bb_info to enable and provide baseband status. Usage of bb_info debugfs: $ echo enable 1 > bb_info // Start logging BB statistics information $ echo mac_id 0 > bb_info // Specify mac_id for TX rate count tracking The output of bb_info: TP TX: 0 [0] Mbps, RX: 0 [0] Mbps Avg packet length: TX=118, RX=136 TF: 0 TX count [0]: Legacy: [0, 0, 0, 0] OFDM: [0, 0, 0, 0, 0, 0, 0, 0] MCS 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] MCS 2SS: [183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [PHY 0] == RSSI/RX Rate Beacon: 19 (-41 dBm) RX count: Legacy: [0, 0, 0, 0] OFDM: [0, 0, 0, 0, 0, 0, 0, 0] HT 0: [0, 0, 0, 0, 0, 0, 0, 0] HT 1: [0, 0, 0, 0, 0, 0, 0, 0] VHT 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0] VHT 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0] HE 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] HE 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] EHT 1SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0][0, 0] EHT 2SS: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185] TX rate [0, 2]: EHT 2SS MCS-0 GI:0.8 FB_G BW:160 (hw_rate=0x420) ==> agg_wait=-1 (1) RX rate [0, 2]: EHT 2SS MCS-13 GI:0.8 BW:160 (hw_rate=0x42d) RSSI: -43 dBm (raw=134, prev=135) [-43, -44] EVM: [38.75, (41.50, 43.00)] SNR: 39 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 + drivers/net/wireless/realtek/rtw89/core.h | 13 ++ drivers/net/wireless/realtek/rtw89/debug.c | 110 +++++++++++++++- drivers/net/wireless/realtek/rtw89/fw.c | 122 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 35 +++++ drivers/net/wireless/realtek/rtw89/mac80211.c | 11 +- drivers/net/wireless/realtek/rtw89/phy.c | 29 +++++ drivers/net/wireless/realtek/rtw89/phy.h | 1 + 8 files changed, 314 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 81f3ae21dc18..d8f83623e54a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6613,6 +6613,8 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw)) rtw89_chip_rfk_channel(rtwdev, target); + rtw89_fw_h2c_tx_history(rtwdev, target->mac_id); + rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; wake_queue: diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f15a0c43ef6d..c4396f0100be 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4511,6 +4511,15 @@ struct rtw89_antdiv_info { bool get_stats; }; +struct rtw89_bb_stat_cfg { + bool enable; + u16 mac_id; +}; + +struct rtw89_phy_info { + struct rtw89_bb_stat_cfg bb_stat_cfg; +}; + enum rtw89_chanctx_state { RTW89_CHANCTX_STATE_MCC_START, RTW89_CHANCTX_STATE_MCC_STOP, @@ -4831,6 +4840,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_SIM_SER_L0L1_BY_HALT_H2C, RTW89_FW_FEATURE_LPS_ML_INFO_V1, RTW89_FW_FEATURE_SER_POST_RECOVER_DMAC, + RTW89_FW_FEATURE_TX_HISTORY_V1, NUM_OF_RTW89_FW_FEATURES, }; @@ -5342,9 +5352,11 @@ struct rtw89_beacon_stat { DECLARE_EWMA(thermal, 4, 4); +#define RTW89_TX_RATE_NR 40 struct rtw89_phy_stat { struct ewma_thermal avg_thermal[RF_PATH_MAX]; u8 last_thermal_max; + u32 tx_rate_cnt[RTW89_TX_RATE_NR]; struct rtw89_beacon_stat bcn_stat; }; @@ -6308,6 +6320,7 @@ struct rtw89_dev { struct rtw89_phy_efuse_gain efuse_gain; struct rtw89_phy_ul_tb_info ul_tb_info; struct rtw89_antdiv_info antdiv; + struct rtw89_phy_info phy_info; struct rtw89_bb_ctx { enum rtw89_phy_idx phy_idx; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 2d953bec149b..b82b13645fb0 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -85,6 +85,7 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv btc_manual; struct rtw89_debugfs_priv fw_log_manual; struct rtw89_debugfs_priv phy_info; + struct rtw89_debugfs_priv bb_info; struct rtw89_debugfs_priv stations; struct rtw89_debugfs_priv disable_dm; struct rtw89_debugfs_priv static_pd_th; @@ -4016,15 +4017,15 @@ static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) } static int -rtw89_debug_append_rx_rate(char *buf, size_t bufsz, struct rtw89_pkt_stat *pkt_stat, - enum rtw89_hw_rate first_rate, int len) +rtw89_debug_append_rate(char *buf, size_t bufsz, const u32 *rate_cnt, + int first_rate, int len) { char *p = buf, *end = buf + bufsz; int i; for (i = 0; i < len; i++) p += scnprintf(p, end - p, "%s%u", i == 0 ? "" : ", ", - pkt_stat->rx_rate_cnt[first_rate + i]); + rate_cnt[first_rate + i]); return p - buf; } @@ -4051,6 +4052,17 @@ static const struct rtw89_rx_rate_cnt_info { {FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"}, }; +static const struct rtw89_tx_rate_cnt_info { + int first_rate; + int len; + const char *rate_mode; +} rtw89_tx_rate_cnt_infos[] = { + {0, 4, "Legacy:"}, + {4, 8, "OFDM:"}, + {12, 14, "MCS 1SS:"}, + {26, 14, "MCS 2SS:"}, +}; + static int rtw89_get_rx_pkt_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { @@ -4075,12 +4087,13 @@ static int rtw89_get_rx_pkt_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx * continue; p += scnprintf(p, end - p, "%10s [", info->rate_mode); - p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, - first_rate, info->len); + p += rtw89_debug_append_rate(p, end - p, pkt_stat->rx_rate_cnt, + first_rate, info->len); if (info->ext) { p += scnprintf(p, end - p, "]["); - p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, - first_rate + info->len, info->ext); + p += rtw89_debug_append_rate(p, end - p, pkt_stat->rx_rate_cnt, + first_rate + info->len, + info->ext); } p += scnprintf(p, end - p, "]\n"); } @@ -4125,6 +4138,87 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, return p - buf; } +static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); + + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); + p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_bb_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + struct rtw89_traffic_stats *stats = &rtwdev->stats; + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_tx_rate_cnt_info *info; + struct rtw89_debugfs_iter_data iter_data; + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + int i; + + p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps, RX: %u [%u] Mbps\n", + stats->tx_throughput, stats->tx_throughput_raw, + stats->rx_throughput, stats->rx_throughput_raw); + p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", + stats->tx_avg_len, + stats->rx_avg_len); + p += scnprintf(p, end - p, "TF: %u\n", stats->rx_tf_periodic); + + if (chip->chip_gen != RTW89_CHIP_AX) { + p += scnprintf(p, end - p, + "TX count [0x%x]:\n", bb_stat->mac_id); + + for (i = 0; i < ARRAY_SIZE(rtw89_tx_rate_cnt_infos); i++) { + info = &rtw89_tx_rate_cnt_infos[i]; + + p += scnprintf(p, end - p, "%10s [", info->rate_mode); + p += rtw89_debug_append_rate(p, end - p, + rtwdev->phystat.tx_rate_cnt, + info->first_rate, info->len); + p += scnprintf(p, end - p, "]\n"); + } + } + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_bb_stat(rtwdev, bb, p, end - p); + p += scnprintf(p, end - p, "\n"); + + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); + p += iter_data.written_sz; + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_bb_info_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + int val; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (sscanf(buf, "enable %d", &val) == 1) + bb_stat->enable = !!val; + else if (sscanf(buf, "mac_id %x", &val) == 1) + rtw89_fw_h2c_tx_history(rtwdev, val); + else + return -EINVAL; + + return count; +} + static int rtw89_dump_addr_cam(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, struct rtw89_addr_cam_entry *addr_cam) @@ -5000,6 +5094,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .btc_manual = rtw89_debug_priv_set(btc_manual), .fw_log_manual = rtw89_debug_priv_set(fw_log_manual, WLOCK), .phy_info = rtw89_debug_priv_get(phy_info), + .bb_info = rtw89_debug_priv_set_and_get(bb_info, RWLOCK), .stations = rtw89_debug_priv_get(stations, RLOCK), .disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK), .static_pd_th = rtw89_debug_priv_set_and_get(static_pd_th, RWLOCK), @@ -5049,6 +5144,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_w(btc_manual); rtw89_debugfs_add_w(fw_log_manual); rtw89_debugfs_add_r(phy_info); + rtw89_debugfs_add_rw(bb_info); rtw89_debugfs_add_r(stations); rtw89_debugfs_add_rw(disable_dm); rtw89_debugfs_add_rw(static_pd_th); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 2a3662ed733f..ff3914a16b81 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -930,6 +930,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __DIS_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, WITH_RFK_PRE_NOTIFY, G), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, RFK_PRE_NOTIFY_MCC_V1), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0), + __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 92, 0, TX_HISTORY_V1), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 97, 0, SIM_SER_L0L1_BY_HALT_H2C), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), }; @@ -5588,6 +5589,127 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi return ret; } +int rtw89_fw_h2c_tx_history(struct rtw89_dev *rtwdev, u16 mac_id) +{ + struct rtw89_bb_stat_cfg *bb_stat = &rtwdev->phy_info.bb_stat_cfg; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_tx_history *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c tx history\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_tx_history *)skb->data; + + h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_RA_TX_HISTORY_W0_MACID) | + le32_encode_bits(0, RTW89_H2C_RA_TX_HISTORY_W0_PER_PPDU); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_TX_HISTORY, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + bb_stat->mac_id = mac_id; + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +int rtw89_fw_h2c_phy_ch_rpt(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_phy_ch_rpt *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c phy ch rpt\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_phy_ch_rpt *)skb->data; + + h2c->w1 = le32_encode_bits(1, RTW89_H2C_RA_PHY_CH_RPT_W1_RPT_TX_COUNT); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_PHY_CH_RPT, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +int rtw89_fw_h2c_drv_ctrl_fw(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_ra_drv_ctrl_fw *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen == RTW89_CHIP_AX) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c drv ctrl fw\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_ra_drv_ctrl_fw *)skb->data; + + h2c->w0 = le32_encode_bits(1, RTW89_H2C_RA_DRV_CTRL_FW_W0_RPT_TX_COUNT); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA, + H2C_FUNC_OUTSRC_RA_DRV_CTRL_FW, 0, 0, len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type) { struct rtw89_btc *btc = &rtwdev->btc; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 6ef53fcd0cce..cde8fd34723b 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -471,6 +471,28 @@ struct rtw89_h2c_ra_v1 { #define RTW89_H2C_RA_V1_W4_RAMASK_UHL16 GENMASK(31, 16) #define RTW89_H2C_RA_V1_W5_RAMASK_UHH16 GENMASK(15, 0) +struct rtw89_h2c_ra_tx_history { + __le32 w0; +} __packed; + +#define RTW89_H2C_RA_TX_HISTORY_W0_MACID GENMASK(15, 0) +#define RTW89_H2C_RA_TX_HISTORY_W0_PER_PPDU GENMASK(23, 16) + +struct rtw89_h2c_ra_phy_ch_rpt { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; +} __packed; + +#define RTW89_H2C_RA_PHY_CH_RPT_W1_RPT_TX_COUNT BIT(10) + +struct rtw89_h2c_ra_drv_ctrl_fw { + __le32 w0; +} __packed; + +#define RTW89_H2C_RA_DRV_CTRL_FW_W0_RPT_TX_COUNT BIT(13) + static inline void RTW89_SET_FWCMD_SEC_IDX(void *cmd, u32 val) { le32p_replace_bits((__le32 *)(cmd) + 0x00, val, GENMASK(7, 0)); @@ -3973,6 +3995,13 @@ struct rtw89_c2h_lps_rpt { */ } __packed; +struct rtw89_c2h_ra_tx_history { + struct rtw89_c2h_hdr hdr; + __le32 ra_tbtt_cnt; + __le32 tx_rate_tot_cnt_hist[RTW89_TX_RATE_NR]; + __le32 tx_cat_cnt[3]; +} __packed; + struct rtw89_c2h_fw_scan_rpt { struct rtw89_c2h_hdr hdr; u8 phy_idx; @@ -4771,6 +4800,9 @@ enum rtw89_mrc_h2c_func { #define H2C_CL_OUTSRC_RA 0x1 #define H2C_FUNC_OUTSRC_RA_MACIDCFG 0x0 +#define H2C_FUNC_OUTSRC_RA_TX_HISTORY 0x9 +#define H2C_FUNC_OUTSRC_RA_PHY_CH_RPT 0xe +#define H2C_FUNC_OUTSRC_RA_DRV_CTRL_FW 0xf #define H2C_CL_OUTSRC_DM 0x2 #define H2C_FUNC_FW_MCC_DIG 0x6 @@ -5349,6 +5381,9 @@ int rtw89_fw_h2c_rssi_offload(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu); int rtw89_fw_h2c_tp_offload(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi); +int rtw89_fw_h2c_phy_ch_rpt(struct rtw89_dev *rtwdev); +int rtw89_fw_h2c_tx_history(struct rtw89_dev *rtwdev, u16 mac_id); +int rtw89_fw_h2c_drv_ctrl_fw(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_init_v7(struct rtw89_dev *rtwdev, u8 type); int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev, u8 type); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 9ee2aa225976..b72f6661fbd1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -721,14 +721,21 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_MLD_VALID_LINKS) { struct rtw89_vif_link *cur = rtw89_get_designated_link(rtwvif); + u16 mac_id; if (RTW89_CHK_FW_FEATURE_GROUP(WITH_RFK_PRE_NOTIFY, &rtwdev->fw)) rtw89_chip_rfk_channel(rtwdev, cur); - if (hweight16(vif->active_links) == 1) + if (hweight16(vif->active_links) == 1) { + mac_id = cur->mac_id; rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; - else + } else { + /* Specify for all MAC ID */ + mac_id = 0xffff; rtwvif->mlo_mode = RTW89_MLO_MODE_EMLSR; + } + + rtw89_fw_h2c_tx_history(rtwdev, mac_id); } } diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 29770cc5ea60..3124a99d5381 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3379,12 +3379,24 @@ rtw89_phy_c2h_ra_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) &ra_data); } +static void +rtw89_phy_c2h_tx_history(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + const struct rtw89_c2h_ra_tx_history *history = (const void *)c2h->data; + u32 *tx_rate_cnt = rtwdev->phystat.tx_rate_cnt; + u32 i; + + for (i = 0; i < RTW89_TX_RATE_NR; i++) + tx_rate_cnt[i] = le32_to_cpu(history->tx_rate_tot_cnt_hist[i]); +} + static void (* const rtw89_phy_c2h_ra_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { [RTW89_PHY_C2H_FUNC_STS_RPT] = rtw89_phy_c2h_ra_rpt, [RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT] = NULL, [RTW89_PHY_C2H_FUNC_TXSTS] = NULL, + [RTW89_PHY_C2H_FUNC_TX_HISTORY] = rtw89_phy_c2h_tx_history, [RTW89_PHY_C2H_FUNC_ACCELERATE_EN] = rtw89_fw_c2h_dummy_handler, }; @@ -5832,6 +5844,22 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtwdev->hal.thermal_prot_lv = 0; } +static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) +{ + if (RTW89_CHK_FW_FEATURE(TX_HISTORY_V1, &rtwdev->fw)) + rtw89_fw_h2c_phy_ch_rpt(rtwdev); + else + rtw89_fw_h2c_drv_ctrl_fw(rtwdev); +} + +static void rtw89_phy_stat_update(struct rtw89_dev *rtwdev) +{ + if (!rtwdev->phy_info.bb_stat_cfg.enable) + return; + + rtw89_phy_trigger_tx_count(rtwdev); +} + void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) { struct rtw89_bb_ctx *bb; @@ -5839,6 +5867,7 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) rtw89_phy_stat_thermal_update(rtwdev); rtw89_phy_thermal_protect(rtwdev); rtw89_phy_stat_rssi_update(rtwdev); + rtw89_phy_stat_update(rtwdev); rtw89_for_each_active_bb(rtwdev, bb) { bb->last_pkt_stat = bb->cur_pkt_stat; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 3f9d306ff1ca..c6761cedc5a5 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -139,6 +139,7 @@ enum rtw89_phy_c2h_ra_func { RTW89_PHY_C2H_FUNC_STS_RPT, RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT, RTW89_PHY_C2H_FUNC_TXSTS, + RTW89_PHY_C2H_FUNC_TX_HISTORY = 0x4, RTW89_PHY_C2H_FUNC_ACCELERATE_EN = 0x7, RTW89_PHY_C2H_FUNC_RA_NUM, From 3b851f36c3f1f6f182eca64cfb3cc7b065ed4039 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:22 +0800 Subject: [PATCH 047/285] wifi: rtw89: debug: add PMAC counter in bb_info PMAC (Pseudo MAC) is a circuit within the baseband that can report various packet-related counters through registers, such as TX ON, TX EN, CCA, FA, CRC, etc. The driver periodically collects per PHY PMAC counters in track_work and exposes them through the bb_info debugfs for easier debugging. The output of PMAC counter: == PMAC TX [CCK_TXEN, CCK_TXON, OFDM_TXEN, OFDM_TXON]: [0, 0, 17, 17] CRC [CCK, OFDM, HT, VHT, HE, EHT, ALL, MPDU] ok: [0, 301, 0, 0, 5, 0, 306, 5] err: [0, 4, 0, 0, 0, 0, 4, 0] CCA [CCK, OFDM]: [0, 353] FA [CCK, OFDM]: [0, 39] CCA spoofing [CCK, OFDM]: [0, 0] CCK SFD: 0, SIG_GG: 0 OFDM Parity: 4, Rate: 2, LSIG_BRK_S: 0, LSIG_BRK_L: 7, SBD: 5 AMPDU miss: 0 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 87 ++++++++ drivers/net/wireless/realtek/rtw89/debug.c | 33 +++ drivers/net/wireless/realtek/rtw89/phy.c | 195 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/reg.h | 127 ++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8852a.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8852b.c | 49 +++++ .../net/wireless/realtek/rtw89/rtw8852bt.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 49 +++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 49 +++++ 11 files changed, 785 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c4396f0100be..7f869a339ee6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4487,6 +4487,54 @@ struct rtw89_edcca_regs { u32 tx_collision_t2r_st_mask; }; +struct rtw89_pmac_regs { + struct rtw89_reg_def cck_txon; + struct rtw89_reg_def cck_txen; + struct rtw89_reg_def cck_cca; + struct rtw89_reg_def cck_sfd_gg; + struct rtw89_reg_def cck_sig_gg; + struct rtw89_reg_def cck_spoofing; + struct rtw89_reg_def cck_brk; + struct rtw89_reg_def brk; + struct rtw89_reg_def brk_option; + struct rtw89_reg_def search_fail; + struct rtw89_reg_def lsig_brk_s_th; + struct rtw89_reg_def lsig_brk_l_th; + struct rtw89_reg_def rxl_err_parity; + struct rtw89_reg_def rxl_err_rate; + struct rtw89_reg_def ofdm_cca; + struct rtw89_reg_def cca_spoofing; + struct rtw89_reg_def ampdu_miss; + struct rtw89_reg_def r1b_rx_rpt_rst; + struct rtw89_reg_def r1b_rr_sel; + struct rtw89_reg_def enable_all_cnt; + struct rtw89_reg_def rst_all_cnt; + u32 cck_crc32; + u32 cck_crc32_ok_mask; + u32 cck_crc32_fail_mask; + u32 ofdm_txon; + u32 ofdm_txon_mask; + u32 ofdm_txen_mask; + u32 l_crc; + u32 l_crc_ok_mask; + u32 l_crc_err_mask; + u32 ht_crc; + u32 ht_crc_ok_mask; + u32 ht_crc_err_mask; + u32 vht_crc; + u32 vht_crc_ok_mask; + u32 vht_crc_err_mask; + u32 he_crc; + u32 he_crc_ok_mask; + u32 he_crc_err_mask; + u32 eht_crc; + u32 eht_crc_ok_mask; + u32 eht_crc_err_mask; + u32 ampdu_crc; + u32 ampdu_crc_ok_mask; + u32 ampdu_crc_err_mask; +}; + struct rtw89_phy_ul_tb_info { bool dyn_tb_tri_en; u8 def_if_bandedge; @@ -4682,6 +4730,7 @@ struct rtw89_chip_info { struct rtw89_sb_regs btc_sb; u32 dma_ch_mask; const struct rtw89_edcca_regs *edcca_regs; + const struct rtw89_pmac_regs *pmac_regs; const struct wiphy_wowlan_support *wowlan_stub; const struct rtw89_xtal_info *xtal_info; unsigned long default_quirks; /* bitmap of rtw89_quirks */ @@ -5505,6 +5554,43 @@ struct rtw89_phy_ch_info { u8 is_noisy; }; +struct rtw89_pmac_stat_info { + u32 cck_phy_txon; + u32 cck_mac_txen; + u32 ofdm_mac_txen; + u32 ofdm_phy_txon; + u32 cnt_ofdm_cca; + u32 cnt_cck_cca; + u32 cnt_cck_spoofing; + u32 cnt_ofdm_spoofing; + u32 cnt_ampdu_miss; + u32 cnt_ampdu_crc_error; + u32 cnt_ampdu_crc_ok; + u32 cnt_cck_crc32_error; + u32 cnt_cck_crc32_ok; + u32 cnt_ofdm_crc32_error; + u32 cnt_ofdm_crc32_ok; + u32 cnt_ht_crc32_error; + u32 cnt_ht_crc32_ok; + u32 cnt_vht_crc32_error; + u32 cnt_vht_crc32_ok; + u32 cnt_he_crc32_ok; + u32 cnt_he_crc32_error; + u32 cnt_eht_crc32_ok; + u32 cnt_eht_crc32_error; + u32 cnt_crc32_error_all; + u32 cnt_crc32_ok_all; + u32 cnt_sfd_gg; + u32 cnt_sig_gg; + u32 cnt_cck_fail; + u32 cnt_ofdm_fail; + u32 cnt_lsig_brk_s_th; + u32 cnt_lsig_brk_l_th; + u32 cnt_parity_fail; + u32 cnt_rate_illegal; + u32 cnt_sb_search_fail; +}; + struct rtw89_agc_gaincode_set { u8 lna_idx; u8 tia_idx; @@ -6331,6 +6417,7 @@ struct rtw89_dev { struct ewma_rssi bcn_rssi; struct rtw89_pkt_stat cur_pkt_stat; struct rtw89_pkt_stat last_pkt_stat; + struct rtw89_pmac_stat_info pmac_stat; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index b82b13645fb0..d8a183bfc6da 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4141,10 +4141,43 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; char *p = buf, *end = buf + bufsz; p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "== PMAC\n"); + p += scnprintf(p, end - p, + "TX [CCK_TXEN, CCK_TXON, OFDM_TXEN, OFDM_TXON]: [%d, %d, %d, %d]\n", + pmac->cck_mac_txen, pmac->cck_phy_txon, + pmac->ofdm_mac_txen, pmac->ofdm_phy_txon); + p += scnprintf(p, end - p, "CRC [CCK, OFDM, HT, VHT, HE, EHT, ALL, MPDU]\n"); + p += scnprintf(p, end - p, " ok: [%d, %d, %d, %d, %d, %d, %d, %d]\n", + pmac->cnt_cck_crc32_ok, pmac->cnt_ofdm_crc32_ok, + pmac->cnt_ht_crc32_ok, pmac->cnt_vht_crc32_ok, + pmac->cnt_he_crc32_ok, pmac->cnt_eht_crc32_ok, + pmac->cnt_crc32_ok_all, pmac->cnt_ampdu_crc_ok); + p += scnprintf(p, end - p, "err: [%d, %d, %d, %d, %d, %d, %d, %d]\n", + pmac->cnt_cck_crc32_error, pmac->cnt_ofdm_crc32_error, + pmac->cnt_ht_crc32_error, pmac->cnt_vht_crc32_error, + pmac->cnt_he_crc32_error, pmac->cnt_eht_crc32_error, + pmac->cnt_crc32_error_all, pmac->cnt_ampdu_crc_error); + p += scnprintf(p, end - p, "CCA [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_cca, pmac->cnt_ofdm_cca); + p += scnprintf(p, end - p, "FA [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_fail, pmac->cnt_ofdm_fail); + + p += scnprintf(p, end - p, "CCA spoofing [CCK, OFDM]: [%d, %d]\n", + pmac->cnt_cck_spoofing, pmac->cnt_ofdm_spoofing); + p += scnprintf(p, end - p, "CCK SFD: %d, SIG_GG: %d\n", + pmac->cnt_sfd_gg, pmac->cnt_sig_gg); + p += scnprintf(p, end - p, + "OFDM Parity: %d, Rate: %d, LSIG_BRK_S: %d, LSIG_BRK_L: %d, SBD: %d\n", + pmac->cnt_parity_fail, pmac->cnt_rate_illegal, + pmac->cnt_lsig_brk_s_th, pmac->cnt_lsig_brk_l_th, + pmac->cnt_sb_search_fail); + p += scnprintf(p, end - p, "AMPDU miss: %d\n\n", pmac->cnt_ampdu_miss); + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 3124a99d5381..80d358d80a38 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5844,6 +5844,196 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtwdev->hal.thermal_prot_lv = 0; } +static void rtw89_phy_pmac_stat_reset(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool cck) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + + if (cck) { + rtw89_phy_write32_clr(rtwdev, regs->r1b_rx_rpt_rst.addr, + regs->r1b_rx_rpt_rst.mask); + rtw89_phy_write32_set(rtwdev, regs->r1b_rx_rpt_rst.addr, + regs->r1b_rx_rpt_rst.mask); + } + + rtw89_phy_write32_idx_set(rtwdev, regs->enable_all_cnt.addr, + regs->enable_all_cnt.mask, bb->phy_idx); + rtw89_phy_write32_idx_set(rtwdev, regs->rst_all_cnt.addr, + regs->rst_all_cnt.mask, bb->phy_idx); + rtw89_phy_write32_idx_clr(rtwdev, regs->rst_all_cnt.addr, + regs->rst_all_cnt.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_cck(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 val; + + pmac->cck_phy_txon = + rtw89_phy_read32_mask(rtwdev, regs->cck_txon.addr, regs->cck_txon.mask); + pmac->cck_mac_txen = + rtw89_phy_read32_mask(rtwdev, regs->cck_txen.addr, regs->cck_txen.mask); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + rtw89_phy_write32_mask(rtwdev, regs->r1b_rr_sel.addr, + regs->r1b_rr_sel.mask, 0x2); + + if (bb->phy_idx == RTW89_PHY_1) + pmac->cnt_cck_cca = + rtw89_phy_read32_mask(rtwdev, regs->cck_cca.addr + 8, + regs->cck_cca.mask); + else + pmac->cnt_cck_cca = + rtw89_phy_read32_mask(rtwdev, regs->cck_cca.addr, + regs->cck_cca.mask); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + rtw89_phy_write32_mask(rtwdev, regs->r1b_rr_sel.addr, + regs->r1b_rr_sel.mask, 0x1); + + if (bb->phy_idx == RTW89_PHY_1) + val = rtw89_phy_read32(rtwdev, regs->cck_crc32 + 8); + else + val = rtw89_phy_read32(rtwdev, regs->cck_crc32); + pmac->cnt_cck_crc32_ok = field_get(regs->cck_crc32_ok_mask, val); + pmac->cnt_cck_crc32_error = field_get(regs->cck_crc32_fail_mask, val); + + pmac->cnt_sfd_gg = + rtw89_phy_read32_idx(rtwdev, regs->cck_sfd_gg.addr, + regs->cck_sfd_gg.mask, bb->phy_idx); + pmac->cnt_sig_gg = + rtw89_phy_read32_idx(rtwdev, regs->cck_sig_gg.addr, + regs->cck_sig_gg.mask, bb->phy_idx); + pmac->cnt_cck_spoofing = + rtw89_phy_read32_idx(rtwdev, regs->cck_spoofing.addr, + regs->cck_spoofing.mask, bb->phy_idx); + + if (chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev)) + pmac->cnt_cck_fail = + pmac->cnt_cck_cca - pmac->cnt_cck_crc32_ok - + pmac->cnt_cck_crc32_error - pmac->cnt_cck_spoofing; + else + pmac->cnt_cck_fail = + rtw89_phy_read32_idx(rtwdev, regs->cck_brk.addr, + regs->cck_brk.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_ofdm(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_pmac_regs *regs = rtwdev->chip->pmac_regs; + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 val; + + val = rtw89_phy_read32_idx(rtwdev, regs->ofdm_txon, MASKDWORD, bb->phy_idx); + pmac->ofdm_phy_txon = field_get(regs->ofdm_txon_mask, val); + pmac->ofdm_mac_txen = field_get(regs->ofdm_txen_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->l_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ofdm_crc32_ok = field_get(regs->l_crc_ok_mask, val); + pmac->cnt_ofdm_crc32_error = field_get(regs->l_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->ht_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ht_crc32_ok = field_get(regs->ht_crc_ok_mask, val); + pmac->cnt_ht_crc32_error = field_get(regs->ht_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->vht_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_vht_crc32_ok = field_get(regs->vht_crc_ok_mask, val); + pmac->cnt_vht_crc32_error = field_get(regs->vht_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->he_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_he_crc32_ok = field_get(regs->he_crc_ok_mask, val); + pmac->cnt_he_crc32_error = field_get(regs->he_crc_err_mask, val); + + if (chip->chip_gen == RTW89_CHIP_BE) { + val = rtw89_phy_read32_idx(rtwdev, regs->eht_crc, + MASKDWORD, bb->phy_idx); + pmac->cnt_eht_crc32_ok = field_get(regs->eht_crc_ok_mask, val); + pmac->cnt_eht_crc32_error = field_get(regs->eht_crc_err_mask, val); + } + + val = rtw89_phy_read32_idx(rtwdev, regs->ampdu_crc, MASKDWORD, bb->phy_idx); + pmac->cnt_ampdu_crc_ok = field_get(regs->ampdu_crc_ok_mask, val); + pmac->cnt_ampdu_crc_error = field_get(regs->ampdu_crc_err_mask, val); + + val = rtw89_phy_read32_idx(rtwdev, regs->brk.addr, regs->brk.mask, bb->phy_idx); + if (chip->chip_id == RTL8852C && + rtw89_phy_read32_idx(rtwdev, regs->brk_option.addr, + regs->brk_option.mask, bb->phy_idx) == 1) { + u32 tmp = pmac->ofdm_phy_txon + pmac->cck_phy_txon; + + val = (val > tmp) ? (val - tmp) : 0; + } + pmac->cnt_ofdm_fail = val; + + pmac->cnt_sb_search_fail = + rtw89_phy_read32_idx(rtwdev, regs->search_fail.addr, + regs->search_fail.mask, bb->phy_idx); + pmac->cnt_lsig_brk_s_th = + rtw89_phy_read32_idx(rtwdev, regs->lsig_brk_s_th.addr, + regs->lsig_brk_s_th.mask, bb->phy_idx); + pmac->cnt_lsig_brk_l_th = + rtw89_phy_read32_idx(rtwdev, regs->lsig_brk_l_th.addr, + regs->lsig_brk_l_th.mask, bb->phy_idx); + pmac->cnt_parity_fail = + rtw89_phy_read32_idx(rtwdev, regs->rxl_err_parity.addr, + regs->rxl_err_parity.mask, bb->phy_idx); + pmac->cnt_rate_illegal = + rtw89_phy_read32_idx(rtwdev, regs->rxl_err_rate.addr, + regs->rxl_err_rate.mask, bb->phy_idx); + pmac->cnt_ofdm_cca = + rtw89_phy_read32_idx(rtwdev, regs->ofdm_cca.addr, + regs->ofdm_cca.mask, bb->phy_idx); + pmac->cnt_ofdm_spoofing = + rtw89_phy_read32_idx(rtwdev, regs->cca_spoofing.addr, + regs->cca_spoofing.mask, bb->phy_idx); + pmac->cnt_ampdu_miss = + rtw89_phy_read32_idx(rtwdev, regs->ampdu_miss.addr, + regs->ampdu_miss.mask, bb->phy_idx); +} + +static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_entity_conf conf; + const struct rtw89_chan *chan; + bool cck; + + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[bb->phy_idx]; + cck = chan->band_type == RTW89_BAND_2G; + + if (cck) + rtw89_phy_pmac_stat_cck(rtwdev, bb); + + rtw89_phy_pmac_stat_ofdm(rtwdev, bb); + + pmac->cnt_crc32_error_all = pmac->cnt_he_crc32_error + + pmac->cnt_vht_crc32_error + + pmac->cnt_ht_crc32_error + + pmac->cnt_ofdm_crc32_error + + pmac->cnt_cck_crc32_error; + + pmac->cnt_crc32_ok_all = pmac->cnt_he_crc32_ok + + pmac->cnt_vht_crc32_ok + + pmac->cnt_ht_crc32_ok + + pmac->cnt_ofdm_crc32_ok + + pmac->cnt_cck_crc32_ok; + + if (chip->chip_gen == RTW89_CHIP_BE) { + pmac->cnt_crc32_error_all += pmac->cnt_eht_crc32_error; + pmac->cnt_crc32_ok_all += pmac->cnt_eht_crc32_ok; + } + + rtw89_phy_pmac_stat_reset(rtwdev, bb, cck); +} + static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) { if (RTW89_CHK_FW_FEATURE(TX_HISTORY_V1, &rtwdev->fw)) @@ -5854,10 +6044,15 @@ static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) static void rtw89_phy_stat_update(struct rtw89_dev *rtwdev) { + struct rtw89_bb_ctx *bb; + if (!rtwdev->phy_info.bb_stat_cfg.enable) return; rtw89_phy_trigger_tx_count(rtwdev); + + rtw89_for_each_active_bb(rtwdev, bb) + rtw89_phy_pmac_stat_update(rtwdev, bb); } void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 1e1125235f0c..c054a402bd20 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8799,6 +8799,12 @@ #define R_P0_ANT_SW 0x0728 #define B_P0_HW_ANTSW_DIS_BY_GNT_BT BIT(12) #define B_P0_TRSW_TX_EXTEND GENMASK(3, 0) +#define R_RST_ALL_CNT 0x0730 +#define R_RST_ALL_CNT_BE4 0x20730 +#define B_RST_ALL_CNT BIT(0) +#define R_ENABLE_ALL_CNT 0x0730 +#define R_ENABLE_ALL_CNT_BE4 0x20730 +#define B_ENABLE_ALL_CNT BIT(1) #define R_MAC_PIN_SEL 0x0734 #define R_MAC_PIN_SEL_BE4 0x20734 #define B_CH_IDX_SEG0 GENMASK(23, 16) @@ -8949,6 +8955,8 @@ #define B_RXHT_MCS_LIMIT GENMASK(9, 8) #define R_RXVHT_MCS_LIMIT 0x0D18 #define B_RXVHT_MCS_LIMIT GENMASK(22, 21) +#define R_BRK_OPT 0x0D44 +#define B_BRK_OPT BIT(31) #define R_P0_EN_SOUND_WO_NDP 0x0D7C #define B_P0_EN_SOUND_WO_NDP BIT(1) #define R_RXHE 0x0D80 @@ -9004,6 +9012,21 @@ #define R_CFO_COMP_SEG0_L 0x1384 #define R_CFO_COMP_SEG0_H 0x1388 #define R_CFO_COMP_SEG0_CTRL 0x138C +#define R_CNT_CCKTXEN 0x1700 +#define R_CNT_CCKTXEN_V1 0x2E00 +#define R_CNT_CCKTXEN_BE4 0x2CF00 +#define B_CNT_CCKTXEN GENMASK(31, 16) +#define R_CNT_CCKTXON 0x1704 +#define R_CNT_CCKTXON_V1 0x2E04 +#define R_CNT_CCKTXON_BE4 0x2CF04 +#define B_CNT_CCKTXON GENMASK(15, 0) +#define R_CNT_CCK_CCA_P0 0x1710 +#define R_CNT_CCK_CCA_P0_V1 0x2E10 +#define B_CNT_CCK_CCA_P0 GENMASK(15, 0) +#define R_CNT_CCK_CRC32_P0 0x1714 +#define R_CNT_CCK_CRC32_P0_V1 0x2E14 +#define B_CNT_CCK_CRC32OK_P0 GENMASK(15, 0) +#define B_CNT_CCK_CRC32FAIL_P0 GENMASK(31, 16) #define R_DBG32_D 0x1730 #define R_EDCCA_RPT_A 0x1738 #define R_EDCCA_RPT_B 0x173c @@ -9019,7 +9042,73 @@ #define B_SWSI_W_BUSY_V1 BIT(24) #define B_SWSI_R_BUSY_V1 BIT(25) #define B_SWSI_R_DATA_DONE_V1 BIT(26) +#define R_CNT_LSIG_BRK_S_TH 0x1A00 +#define R_CNT_LSIG_BRK_S_TH_V1 0x0E00 +#define R_CNT_LSIG_BRK_S_TH_BE4 0x20E00 +#define B_CNT_LSIG_BRK_S_TH GENMASK(31, 16) +#define R_CNT_CCA_SPOOFING 0x1A00 +#define R_CNT_CCA_SPOOFING_V1 0x0E00 +#define R_CNT_CCA_SPOOFING_BE4 0x20E00 +#define B_CNT_CCA_SPOOFING GENMASK(15, 0) +#define R_CNT_LSIG_BRK_L_TH 0x1A04 +#define R_CNT_LSIG_BRK_L_TH_V1 0x0E04 +#define R_CNT_LSIG_BRK_L_TH_BE4 0x20E04 +#define B_CNT_LSIG_BRK_L_TH GENMASK(15, 0) +#define R_CNT_BRK 0x1A08 +#define R_CNT_BRK_V1 0x0E08 +#define R_CNT_BRK_BE4 0x20E08 +#define B_CNT_BRK GENMASK(31, 16) +#define R_CNT_RXL_ERR_PARITY 0x1A0C +#define R_CNT_RXL_ERR_PARITY_V1 0x0E0C +#define R_CNT_RXL_ERR_PARITY_BE4 0x20E0C +#define B_CNT_RXL_ERR_PARITY GENMASK(31, 16) +#define R_CNT_RXL_ERR_RATE 0x1A10 +#define R_CNT_RXL_ERR_RATE_V1 0x0E10 +#define R_CNT_RXL_ERR_RATE_BE4 0x20E10 +#define B_CNT_RXL_ERR_RATE GENMASK(15, 0) +#define R_CNT_SEARCH_FAIL 0x1A20 +#define R_CNT_SEARCH_FAIL_V1 0x0E20 +#define R_CNT_SEARCH_FAIL_BE4 0x20E20 +#define B_CNT_SEARCH_FAIL GENMASK(31, 16) +#define R_CNT_OFDM_CCA 0x1A24 +#define R_CNT_OFDM_CCA_V1 0x0E24 +#define R_CNT_OFDM_CCA_BE4 0x20E24 +#define B_CNT_OFDM_CCA GENMASK(15, 0) #define R_TX_COUNTER 0x1A40 +#define R_CNT_OFDMTXON 0x1A40 +#define R_CNT_OFDMTXON_V1 0x0E40 +#define R_CNT_OFDMTXON_BE4 0x20E40 +#define B_CNT_OFDMTXON GENMASK(15, 0) +#define B_CNT_OFDMTXEN GENMASK(31, 16) +#define R_CNT_HE_CRC 0x1A58 +#define R_CNT_HE_CRC_V1 0x0E58 +#define R_CNT_HE_CRC_BE4 0x20E58 +#define B_CNT_HE_CRC_OK GENMASK(15, 0) +#define B_CNT_HE_CRC_ERR GENMASK(31, 16) +#define R_CNT_VHT_CRC 0x1A5C +#define R_CNT_VHT_CRC_V1 0x0E5C +#define R_CNT_VHT_CRC_BE4 0x20E5C +#define B_CNT_VHT_CRC_OK GENMASK(15, 0) +#define B_CNT_VHT_CRC_ERR GENMASK(31, 16) +#define R_CNT_HT_CRC 0x1A60 +#define R_CNT_HT_CRC_V1 0x0E60 +#define R_CNT_HT_CRC_BE4 0x20E60 +#define B_CNT_HT_CRC_OK GENMASK(15, 0) +#define B_CNT_HT_CRC_ERR GENMASK(31, 16) +#define R_CNT_L_CRC 0x1A64 +#define R_CNT_L_CRC_V1 0x0E64 +#define R_CNT_L_CRC_BE4 0x20E64 +#define B_CNT_L_CRC_OK GENMASK(15, 0) +#define B_CNT_L_CRC_ERR GENMASK(31, 16) +#define R_CNT_AMPDU_MISS 0x1A7C +#define R_CNT_AMPDU_MISS_V1 0x0E7C +#define R_CNT_AMPDU_MISS_BE4 0x20E7C +#define B_CNT_AMPDU_MISS GENMASK(31, 16) +#define R_CNT_AMPDU_RX_CRC32 0x1A80 +#define R_CNT_AMPDU_RX_CRC32_V1 0x0E80 +#define R_CNT_AMPDU_RX_CRC32_BE4 0x20E80 +#define B_CNT_AMPDU_RX_CRC32_OK GENMASK(15, 0) +#define B_CNT_AMPDU_RX_CRC32_ERR GENMASK(31, 16) #define R_NHM_CNT0 0x1A88 #define B_NHM_CNT0_MSK GENMASK(15, 0) #define B_NHM_CNT1_MSK GENMASK(31, 16) @@ -9137,12 +9226,41 @@ #define B_RXCCA_DIS_V1 BIT(0) #define R_RXSC 0x237C #define B_RXSC_EN BIT(0) +#define R_R1B_RR_SEL 0x2388 +#define B_R1B_RR_SEL GENMASK(24, 23) +#define R_R1B_RX_RPT_RST 0x2388 +#define R_R1B_RX_RPT_RST_V1 0x2340 +#define R_R1B_RX_RPT_RST_BE 0x0540 +#define R_R1B_RX_RPT_RST_BE4 0x20540 +#define B_R1B_RX_RPT_RST BIT(15) +#define B_R1B_RX_RPT_RST_V1 BIT(7) +#define R_BRK_CNT 0x239C +#define R_BRK_CNT_V1 0x059C +#define R_BRK_CNT_BE4 0x2059C +#define B_BRK_CNT GENMASK(31, 16) #define R_RX_RPL_OFST 0x23AC #define B_RX_RPL_OFST_CCK_MASK GENMASK(6, 0) #define R_RXSCOBC 0x23B0 #define B_RXSCOBC_TH GENMASK(18, 0) #define R_RXSCOCCK 0x23B4 #define B_RXSCOCCK_TH GENMASK(18, 0) +#define R_SFD_GG_CNT 0x23E0 +#define R_SFD_GG_CNT_V1 0x23A0 +#define R_SFD_GG_CNT_V2 0x05A0 +#define R_SFD_GG_CNT_BE4 0x205A0 +#define B_SFD_GG_CNT GENMASK(15, 0) +#define R_SIG_GG_CNT 0x23E8 +#define R_SIG_GG_CNT_V1 0x23AC +#define R_SIG_GG_CNT_V2 0x05AC +#define R_SIG_GG_CNT_BE4 0x205AC +#define B_SIG_GG_CNT GENMASK(15, 0) +#define B_SIG_GG_CNT_V1 GENMASK(15, 8) +#define R_SPOOF_CNT 0x23EC +#define R_SPOOF_CNT_V1 0x23A8 +#define R_SPOOF_CNT_V2 0x05A8 +#define R_SPOOF_CNT_BE4 0x205A8 +#define B_SPOOF_CNT GENMASK(7, 0) +#define B_SPOOF_CNT_V1 GENMASK(31, 16) #define R_P80_AT_HIGH_FREQ_RU_ALLOC 0x2410 #define B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY1 BIT(14) #define B_P80_AT_HIGH_FREQ_RU_ALLOC_PHY0 BIT(13) @@ -9179,6 +9297,10 @@ #define R_EDCCA_RPT_B_BE4_C1 0x2FE34 #define R_EDCCA_RPT_P1_A_BE 0x2E40 #define R_EDCCA_RPT_P1_B_BE 0x2E44 +#define R_CNT_EHT_CRC 0x2F00 +#define R_CNT_EHT_CRC_BE4 0x22F00 +#define B_CNT_EHT_CRC_OK GENMASK(15, 0) +#define B_CNT_EHT_CRC_ERR GENMASK(31, 16) #define R_S1_HW_SI_DIS 0x3200 #define B_S1_HW_SI_DIS_W_R_TRIG GENMASK(30, 28) #define R_P1_RXCK 0x32A0 @@ -10547,6 +10669,11 @@ #define R_IFS_T3_HIS_BE4 0x20F54 #define B_IFS_T3_HIS_BE4 GENMASK(15, 0) #define B_IFS_T4_HIS_BE4 GENMASK(31, 16) +#define R_CNT_CCK_CCA_BE4 0x20FE8 +#define B_CNT_CCK_CCA_BE4 GENMASK(15, 0) +#define R_CNT_CCK_CRC32_BE4 0x20FEC +#define B_CNT_CCK_CRC32OK_BE4 GENMASK(15, 0) +#define B_CNT_CCK_CRC32FAIL_BE4 GENMASK(31, 16) #define R_TX_ERROR_SEL_BE4 0x21254 #define B_TX_ERROR_PSDU_BE4 BIT(11) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index d9a144b4bf58..e047e716d9e3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -307,6 +307,54 @@ static const struct rtw89_edcca_regs rtw8851b_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8851b_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8851b_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -2722,6 +2770,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8851b_edcca_regs, + .pmac_regs = &rtw8851b_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8851b, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index b81eeb59be60..686e489d42f2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -621,6 +621,54 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852a_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static void rtw8852a_efuse_parsing_tssi(struct rtw89_dev *rtwdev, struct rtw8852a_efuse *map) { @@ -2459,6 +2507,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .btc_sb = {{{R_AX_SCOREBOARD, R_AX_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8852a_edcca_regs, + .pmac_regs = &rtw8852a_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852a, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 13c942127225..6ab99f72fda7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -259,6 +259,54 @@ static const struct rtw89_edcca_regs rtw8852b_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852b_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -1056,6 +1104,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8852b_edcca_regs, + .pmac_regs = &rtw8852b_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852b, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 3fd5990a8bc4..83de26273100 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -202,6 +202,54 @@ static const struct rtw89_edcca_regs rtw8852bt_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852bt_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT, B_SIG_GG_CNT}, + .cck_spoofing = {R_SPOOF_CNT, B_SPOOF_CNT}, + .cck_brk = {}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST, B_R1B_RX_RPT_RST}, + .r1b_rr_sel = {R_R1B_RR_SEL, B_R1B_RR_SEL}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_btc_rf_trx_para rtw89_btc_8852bt_rf_ul[] = { {255, 0, 0, 7}, /* 0 -> original */ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ @@ -893,6 +941,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { BIT(RTW89_DMA_ACH6) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B1MG) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8852bt_edcca_regs, + .pmac_regs = &rtw8852bt_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852bt, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9ef469c1080e..cc278587b532 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -270,6 +270,54 @@ static const struct rtw89_edcca_regs rtw8852c_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; +static const struct rtw89_pmac_regs rtw8852c_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT_V1, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_V1, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_V1, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT, B_BRK_CNT}, + .brk = {R_CNT_BRK, B_CNT_BRK}, + .brk_option = {R_BRK_OPT, B_BRK_OPT}, + .search_fail = {R_CNT_SEARCH_FAIL, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_V1, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = 0, + .eht_crc_ok_mask = 0, + .eht_crc_err_mask = 0, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static void rtw8852c_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en, enum rtw89_phy_idx phy_idx); @@ -3252,6 +3300,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .btc_sb = {{{R_AX_SCOREBOARD, R_AX_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8852c_edcca_regs, + .pmac_regs = &rtw8852c_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8852c, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 3c453b93c52e..e6f15ee2a86b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -383,6 +383,54 @@ static const struct rtw89_edcca_regs rtw8922a_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_BE_M, }; +static const struct rtw89_pmac_regs rtw8922a_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON_V1, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN_V1, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_P0_V1, B_CNT_CCK_CCA_P0}, + .cck_sfd_gg = {R_SFD_GG_CNT_V2, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_V2, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_V2, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT_V1, B_BRK_CNT}, + .brk = {R_CNT_BRK_V1, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL_V1, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH_V1, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH_V1, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY_V1, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE_V1, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA_V1, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING_V1, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS_V1, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_BE, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_P0_V1, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_P0, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_P0, + .ofdm_txon = R_CNT_OFDMTXON_V1, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC_V1, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC_V1, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC_V1, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC_V1, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = R_CNT_EHT_CRC, + .eht_crc_ok_mask = B_CNT_EHT_CRC_OK, + .eht_crc_err_mask = B_CNT_EHT_CRC_ERR, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32_V1, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_efuse_block_cfg rtw8922a_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_SYS] = {.offset = 0x00000, .size = 0x310}, [RTW89_EFUSE_BLOCK_RF] = {.offset = 0x10000, .size = 0x240}, @@ -3245,6 +3293,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .btc_sb = {{{R_BE_SCOREBOARD, R_BE_SCOREBOARD},}}, .dma_ch_mask = 0, .edcca_regs = &rtw8922a_edcca_regs, + .pmac_regs = &rtw8922a_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8922a, #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index ba736b967880..b8b75fca0a1b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -227,6 +227,54 @@ static const struct rtw89_edcca_regs rtw8922d_edcca_regs = { .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_BE_M, }; +static const struct rtw89_pmac_regs rtw8922d_pmac_regs = { + .cck_txon = {R_CNT_CCKTXON_BE4, B_CNT_CCKTXON}, + .cck_txen = {R_CNT_CCKTXEN_BE4, B_CNT_CCKTXEN}, + .cck_cca = {R_CNT_CCK_CCA_BE4, B_CNT_CCK_CCA_BE4}, + .cck_sfd_gg = {R_SFD_GG_CNT_BE4, B_SFD_GG_CNT}, + .cck_sig_gg = {R_SIG_GG_CNT_BE4, B_SIG_GG_CNT_V1}, + .cck_spoofing = {R_SPOOF_CNT_BE4, B_SPOOF_CNT_V1}, + .cck_brk = {R_BRK_CNT_BE4, B_BRK_CNT}, + .brk = {R_CNT_BRK_BE4, B_CNT_BRK}, + .brk_option = {}, + .search_fail = {R_CNT_SEARCH_FAIL_BE4, B_CNT_SEARCH_FAIL}, + .lsig_brk_s_th = {R_CNT_LSIG_BRK_S_TH_BE4, B_CNT_LSIG_BRK_S_TH}, + .lsig_brk_l_th = {R_CNT_LSIG_BRK_L_TH_BE4, B_CNT_LSIG_BRK_L_TH}, + .rxl_err_parity = {R_CNT_RXL_ERR_PARITY_BE4, B_CNT_RXL_ERR_PARITY}, + .rxl_err_rate = {R_CNT_RXL_ERR_RATE_BE4, B_CNT_RXL_ERR_RATE}, + .ofdm_cca = {R_CNT_OFDM_CCA_BE4, B_CNT_OFDM_CCA}, + .cca_spoofing = {R_CNT_CCA_SPOOFING_BE4, B_CNT_CCA_SPOOFING}, + .ampdu_miss = {R_CNT_AMPDU_MISS_BE4, B_CNT_AMPDU_MISS}, + .r1b_rx_rpt_rst = {R_R1B_RX_RPT_RST_BE4, B_R1B_RX_RPT_RST_V1}, + .r1b_rr_sel = {}, + .enable_all_cnt = {R_ENABLE_ALL_CNT_BE4, B_ENABLE_ALL_CNT}, + .rst_all_cnt = {R_RST_ALL_CNT_BE4, B_RST_ALL_CNT}, + .cck_crc32 = R_CNT_CCK_CRC32_BE4, + .cck_crc32_ok_mask = B_CNT_CCK_CRC32OK_BE4, + .cck_crc32_fail_mask = B_CNT_CCK_CRC32FAIL_BE4, + .ofdm_txon = R_CNT_OFDMTXON_BE4, + .ofdm_txon_mask = B_CNT_OFDMTXON, + .ofdm_txen_mask = B_CNT_OFDMTXEN, + .l_crc = R_CNT_L_CRC_BE4, + .l_crc_ok_mask = B_CNT_L_CRC_OK, + .l_crc_err_mask = B_CNT_L_CRC_ERR, + .ht_crc = R_CNT_HT_CRC_BE4, + .ht_crc_ok_mask = B_CNT_HT_CRC_OK, + .ht_crc_err_mask = B_CNT_HT_CRC_ERR, + .vht_crc = R_CNT_VHT_CRC_BE4, + .vht_crc_ok_mask = B_CNT_VHT_CRC_OK, + .vht_crc_err_mask = B_CNT_VHT_CRC_ERR, + .he_crc = R_CNT_HE_CRC_BE4, + .he_crc_ok_mask = B_CNT_HE_CRC_OK, + .he_crc_err_mask = B_CNT_HE_CRC_ERR, + .eht_crc = R_CNT_EHT_CRC_BE4, + .eht_crc_ok_mask = B_CNT_EHT_CRC_OK, + .eht_crc_err_mask = B_CNT_EHT_CRC_ERR, + .ampdu_crc = R_CNT_AMPDU_RX_CRC32_BE4, + .ampdu_crc_ok_mask = B_CNT_AMPDU_RX_CRC32_OK, + .ampdu_crc_err_mask = B_CNT_AMPDU_RX_CRC32_ERR, +}; + static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_SYS] = {.offset = 0x00000, .size = 0x310}, [RTW89_EFUSE_BLOCK_RF] = {.offset = 0x10000, .size = 0x240}, @@ -3085,6 +3133,7 @@ const struct rtw89_chip_info rtw8922d_chip_info = { BIT(RTW89_DMA_ACH5) | BIT(RTW89_DMA_ACH7) | BIT(RTW89_DMA_B0HI) | BIT(RTW89_DMA_B1HI), .edcca_regs = &rtw8922d_edcca_regs, + .pmac_regs = &rtw8922d_pmac_regs, #ifdef CONFIG_PM .wowlan_stub = &rtw_wowlan_stub_8922d, #endif From 419ed7f4a0532e6211f1f73b311859500a63e141 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:23 +0800 Subject: [PATCH 048/285] wifi: rtw89: debug: extend bb_info with TX status and PER Enhance bb_info debugfs by adding TX status information to aid debugging and performance analysis. A snapshot of TX-related registers, including PPDU type and subtype, bandwidth, TX power, STBC, etc. The information is collected per PHY during track_work and displayed via bb_info debugfs. TX status output: == TX General EHT_MU_SU DATA BW: 160, TX_SC: 0, TX_PATH_EN: 3, PATH_MAP: 0xe4 TXPWR TMAC: 11, P0: 11, P1: 11 dBm MCS: 9, STBC: 0 Info: [0x0000000b, 0xc1702c30, 0x0ff1e430, 0xc0000000, 0x99000009, 0x40000000] Common ctrl: [0x00000150, 0x40190140] In addition, include the retry ratio from RA reports and display it as PER (packet error rate) in station debug information. PER output: TX rate [0, 0]: HE 2SS MCS-4 GI:0.8 BW:20 (hw_rate=0x324) PER:23 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 24 +++ drivers/net/wireless/realtek/rtw89/debug.c | 206 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.c | 93 ++++++++- drivers/net/wireless/realtek/rtw89/phy.h | 23 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 46 +++++ drivers/net/wireless/realtek/rtw89/reg.h | 29 +++ 6 files changed, 420 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 7f869a339ee6..e8b03716d41e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3488,6 +3488,7 @@ struct rtw89_ra_report { struct rate_info txrate; u32 bit_rate; u16 hw_rate; + u8 retry_ratio; bool might_fallback_legacy; }; @@ -4175,6 +4176,12 @@ struct rtw89_reg5_def { u32 data; }; +#define RTW89_REGS_DEF(x) {x, ARRAY_SIZE(x)} +struct rtw89_regs_def { + const u32 *regs; + u32 reg_nr; +}; + struct rtw89_reg_imr { u32 addr; u32 clr; @@ -5591,6 +5598,22 @@ struct rtw89_pmac_stat_info { u32 cnt_sb_search_fail; }; +struct rtw89_tx_stat_info { + u32 info[6]; + u32 common_ctrl[2]; + u32 txpwr[2]; + u8 type; + u8 subtype; + u8 txcmd; + u8 txsc; + u8 bw; + u16 tmac_txpwr; + u8 tx_path_en; + u8 path_map; + u8 max_mcs; + bool stbc; +}; + struct rtw89_agc_gaincode_set { u8 lna_idx; u8 tia_idx; @@ -6418,6 +6441,7 @@ struct rtw89_dev { struct rtw89_pkt_stat cur_pkt_stat; struct rtw89_pkt_stat last_pkt_stat; struct rtw89_pmac_stat_info pmac_stat; + struct rtw89_tx_stat_info tx_stat; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index d8a183bfc6da..d9a5bbae11f2 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3926,6 +3926,7 @@ static int rtw89_sta_link_info_get_iter(struct rtw89_dev *rtwdev, rtw89_rate_info_bw_to_mhz(rate->bw)); p += scnprintf(p, end - p, " (hw_rate=0x%x)", rtwsta_link->ra_report.hw_rate); + p += scnprintf(p, end - p, " PER:%d", rtwsta_link->ra_report.retry_ratio); p += scnprintf(p, end - p, " ==> agg_wait=%d (%d)\n", rtwsta_link->max_agg_wait, max_rc_amsdu_len); @@ -4138,11 +4139,179 @@ static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, return p - buf; } +static const char *const lcck[] = {"L_CCK"}; +static const char *const scck[] = {"S_CCK"}; +static const char *const ht_gf[] = {"HT_GF"}; +static const char *const vht_mu[] = {"VHT_MU"}; +static const char *const he_er_su[] = {"HE_ER_SU"}; +static const char *const eht_tb[] = {"EHT_TB"}; +static const char *const legacy_ax[] = {"LEGACY"}; +static const char *const ht_ax[] = {"HT"}; +static const char *const vht_su_ax[] = {"VHT_SU"}; +static const char *const he_su_ax[] = {"HE_SU"}; +static const char *const he_mu_ax[] = {"HE_MU"}; +static const char *const he_tb_ax[] = {"HE_TB"}; +static const char *const legacy_be[] = { + "LEGACY", "LEGACY_DUP", "LEGACY_DUP_PUNC" +}; + +static const char *const ht_be[] = { + "HT_MF", "HT_SND_NDP" +}; + +static const char *const vht_su_be[] = { + "VHT_SU", "VHT_SND_NDP" +}; + +static const char *const he_su_be[] = { + "HE_SU", "HE_SND_NDP", "HE_SND_NDP_PUNC", "HE_RANG_NDP" +}; + +static const char *const he_mu_be[] = { + "HE_MU_RU", "HE_MU_MU", "HE_MU_RU_PUNC" +}; + +static const char *const he_tb_be[] = { + "HE_TB", "HE_TB_FB_NDP", "HE_MU_RANG_NDP" +}; + +static const char *const eht_mu[] = { + "EHT_MU_SU", "EHT_MU_ER", "EHT_MU_RU", "EHT_MU_MU", + "EHT_MU_SND_NDP", "EHT_MU_SU_PUNC", "EHT_MU_RU_PUNC", + "EHT_SND_NDP_PUNC", "EHT_MU_MU_PUNC" +}; + +#define PPDU_SAME(ppdu) \ + {.str = {ppdu, ppdu}, \ + .cnt = {ARRAY_SIZE(ppdu), ARRAY_SIZE(ppdu)} } +#define PPDU_VARIANT(ppdu) \ + {.str = {ppdu##_ax, ppdu##_be}, \ + .cnt = {ARRAY_SIZE(ppdu##_ax), ARRAY_SIZE(ppdu##_be)} } +#define PPDU_GEV1(ppdu) \ + {.str = {NULL, ppdu}, \ + .cnt = {0, ARRAY_SIZE(ppdu)} } + +static const struct rtw89_ppdu_info { + const char *const *str[RTW89_CHIP_GEN_NUM]; + u8 cnt[RTW89_CHIP_GEN_NUM]; +} rtw89_ppdu_infos[] = { + [0] = PPDU_SAME(lcck), + [1] = PPDU_SAME(scck), + [2] = PPDU_VARIANT(legacy), + [3] = PPDU_VARIANT(ht), + [4] = PPDU_SAME(ht_gf), + [5] = PPDU_VARIANT(vht_su), + [6] = PPDU_SAME(vht_mu), + [7] = PPDU_VARIANT(he_su), + [8] = PPDU_SAME(he_er_su), + [9] = PPDU_VARIANT(he_mu), + [10] = PPDU_VARIANT(he_tb), + [11] = PPDU_GEV1(eht_mu), + [12] = PPDU_GEV1(eht_tb), +}; + +#define TXCMD_SAME(txcmd) {txcmd, txcmd} +#define TXCMD_DIFF(txcmd, txcmd_v1) {txcmd, txcmd_v1} +#define TXCMD_GEV1(txcmd) {"RSVD", txcmd} + +static const struct rtw89_txcmd_info { + const char *str[RTW89_CHIP_GEN_NUM]; +} rtw89_txcmd_infos[] = { + [0] = {TXCMD_SAME("DATA")}, + [1] = {TXCMD_SAME("BCN")}, + [2] = {TXCMD_SAME("HT_NDPA")}, + [3] = {TXCMD_SAME("VHT_NDPA")}, + [4] = {TXCMD_SAME("HE_NDPA")}, + [5] = {TXCMD_GEV1("EHT_NDPA")}, + [6] = {TXCMD_GEV1("11MC_FTM")}, + [7] = {TXCMD_GEV1("11MC_FTM_ACK")}, + [8] = {TXCMD_SAME("RTS")}, + [9] = {TXCMD_SAME("CTS2S")}, + [10] = {TXCMD_SAME("CF_END")}, + [11] = {TXCMD_SAME("CMP_BAR")}, + [12] = {TXCMD_SAME("BFRP")}, + [13] = {TXCMD_SAME("NDP")}, + [14] = {TXCMD_SAME("QoS_NULL")}, + [15] = {TXCMD_GEV1("CTS_2_MURTS")}, + [16] = {TXCMD_SAME("ACK")}, + [17] = {TXCMD_SAME("CTS")}, + [18] = {TXCMD_SAME("CMP_BA")}, + [19] = {TXCMD_SAME("MSTA_BA")}, + [20] = {TXCMD_SAME("HT_CSI")}, + [21] = {TXCMD_SAME("VHT_CSI")}, + [22] = {TXCMD_SAME("HE_CSI")}, + [23] = {TXCMD_GEV1("EHT_CSI")}, + [24] = {TXCMD_GEV1("NTB_I2R_NDPA")}, + [25] = {TXCMD_GEV1("NTB_I2R_NDP")}, + [26] = {TXCMD_GEV1("NTB_I2R_LMR")}, + [27] = {TXCMD_GEV1("NTB_I2R_NDP")}, + [28] = {TXCMD_GEV1("NTB_I2R_LMR")}, + [29] = {TXCMD_GEV1("NTB_R2I_RANG_NDPA")}, + [30] = {TXCMD_GEV1("NTB_R2I_NDP")}, + [31] = {TXCMD_DIFF("TB_PPDU", "NTB_R2I_LMR")}, + [32] = {TXCMD_SAME("TRIG_BASIC")}, + [33] = {TXCMD_SAME("TRIG_BFRP")}, + [34] = {TXCMD_SAME("TRIG_MUBAR")}, + [35] = {TXCMD_SAME("TRIG_MURTS")}, + [36] = {TXCMD_SAME("TRIG_BSRP")}, + [37] = {TXCMD_SAME("TRIG_BQRP")}, + [38] = {TXCMD_SAME("TRIG_NFRP")}, + [39] = {TXCMD_GEV1("TRIG_BASIC_DATA")}, + [40] = {TXCMD_GEV1("TRIG_RANG_POLL")}, + [41] = {TXCMD_GEV1("TRIG_RANG_SNR")}, + [42] = {TXCMD_GEV1("TRIG_RANG_LMR")}, + [48] = {TXCMD_DIFF("TRIG_BASIC_DATA", "TRIG_TB_CSI")}, + [49] = {TXCMD_GEV1("TRIG_TB_CBA")}, + [50] = {TXCMD_GEV1("TRIG_TB_MBA")}, + [51] = {TXCMD_GEV1("TRIG_TB_BSR")}, + [52] = {TXCMD_GEV1("TRIG_TB_BQR")}, + [53] = {TXCMD_GEV1("TRIG_TB_ACK")}, + [54] = {TXCMD_GEV1("TRIG_TB_PPDU")}, + [55] = {TXCMD_GEV1("TRIG_TB_I2R_CTS2S")}, + [56] = {TXCMD_GEV1("TRIG_TB_I2R_NDP")}, + [57] = {TXCMD_GEV1("TRIG_TB_I2R_LMR")}, +}; + +static const char *rtw89_ppdu_str(struct rtw89_dev *rtwdev, u8 type, u8 subtype) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_ppdu_info *ppdu_info; + + if (type > ARRAY_SIZE(rtw89_ppdu_infos)) + return "RSVD"; + + ppdu_info = &rtw89_ppdu_infos[type]; + + if (!ppdu_info->str[chip->chip_gen] || + subtype >= ppdu_info->cnt[chip->chip_gen]) + return "RSVD"; + + return ppdu_info->str[chip->chip_gen][subtype]; +} + +static const char *rtw89_txcmd_str(struct rtw89_dev *rtwdev, u8 txcmd) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (txcmd < ARRAY_SIZE(rtw89_txcmd_infos)) + return rtw89_txcmd_infos[txcmd].str[chip->chip_gen] ?: "RSVD"; + + return "RSVD"; +} + static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const struct rtw89_physts_regs *physts = phy->physts; struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; + struct rtw89_tx_stat_info *tx_stat = &bb->tx_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; char *p = buf, *end = buf + bufsz; + u8 factor = chip->txpwr_factor_rf; + u32 reg_nr; + s32 val; + int i; p += scnprintf(p, end - p, "\n[PHY %u]\n", bb->phy_idx); @@ -4178,6 +4347,43 @@ static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, pmac->cnt_sb_search_fail); p += scnprintf(p, end - p, "AMPDU miss: %d\n\n", pmac->cnt_ampdu_miss); + p += scnprintf(p, end - p, "== TX General\n"); + p += scnprintf(p, end - p, "%s %s\n", + rtw89_ppdu_str(rtwdev, tx_stat->type, tx_stat->subtype), + rtw89_txcmd_str(rtwdev, tx_stat->txcmd)); + + p += scnprintf(p, end - p, "BW: %d, TX_SC: %d, TX_PATH_EN: %d, PATH_MAP: 0x%x\n", + 20 << tx_stat->bw, tx_stat->txsc, + tx_stat->tx_path_en, tx_stat->path_map); + + val = sign_extend32(tx_stat->tmac_txpwr, 8); + p += scnprintf(p, end - p, "TXPWR TMAC: %d,", val >> factor); + + reg_nr = min(chip->rf_path_num, ARRAY_SIZE(tx_stat->txpwr)); + for (i = 0; i < reg_nr; i++) { + val = sign_extend32(tx_stat->txpwr[i], 8); + p += scnprintf(p, end - p, " P%d: %d%s", + i, val >> factor, (i < reg_nr - 1) ? "," : ""); + } + p += scnprintf(p, end - p, " dBm\n"); + + p += scnprintf(p, end - p, "MCS: %d, STBC: %d\n", + tx_stat->max_mcs, tx_stat->stbc); + + p += scnprintf(p, end - p, "Info: ["); + reg_nr = min(physts->tx_info.reg_nr, ARRAY_SIZE(tx_stat->info)); + for (i = 0; i < reg_nr; i++) + p += scnprintf(p, end - p, "0x%08x%s", + tx_stat->info[i], (i < reg_nr - 1) ? ", " : ""); + p += scnprintf(p, end - p, "]\n"); + + p += scnprintf(p, end - p, "Common ctrl: ["); + reg_nr = min(physts->tx_common_ctrl.reg_nr, ARRAY_SIZE(tx_stat->common_ctrl)); + for (i = 0; i < reg_nr; i++) + p += scnprintf(p, end - p, "0x%08x%s", + tx_stat->common_ctrl[i], (i < reg_nr - 1) ? ", " : ""); + p += scnprintf(p, end - p, "]\n\n"); + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 80d358d80a38..cf7382e36b75 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3239,6 +3239,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, bool format_v1 = chip->chip_gen == RTW89_CHIP_BE; u8 mode, rate, bw, giltf, mac_id; u16 legacy_bitrate, amsdu_len; + u8 retry_ratio = 0; bool valid; u8 mcs = 0; u8 t; @@ -3259,6 +3260,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, bw |= u8_encode_bits(t, BIT(2)); t = le32_get_bits(c2h->w3, RTW89_C2H_RA_RPT_W3_MD_SEL_B2); mode |= u8_encode_bits(t, BIT(2)); + retry_ratio = le32_get_bits(c2h->w2, RTW89_C2H_RA_RPT_W2_RETRY_RATIO); } if (mode == RTW89_RA_RPT_MODE_LEGACY) { @@ -3334,6 +3336,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, u16_encode_bits(rate, RTW89_HW_RATE_V1_MASK_VAL) : u16_encode_bits(mode, RTW89_HW_RATE_MASK_MOD) | u16_encode_bits(rate, RTW89_HW_RATE_MASK_VAL); + ra_report->retry_ratio = retry_ratio; ra_report->might_fallback_legacy = mcs <= 2; amsdu_len = get_max_amsdu_len(rtwdev, ra_report); @@ -6034,6 +6037,71 @@ static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, rtw89_phy_pmac_stat_reset(rtwdev, bb, cck); } +static void rtw89_phy_tx_stat_update(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const struct rtw89_physts_regs *physts = phy->physts; + struct rtw89_tx_stat_info *tx_stat = &bb->tx_stat; + const struct rtw89_chip_info *chip = rtwdev->chip; + u32 reg_nr; + u32 val; + u32 i; + + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF && + bb->phy_idx != RTW89_PHY_0) + val = 1; + else + val = 0; + + rtw89_phy_write32_mask(rtwdev, physts->mac_phy_intf_sel.addr, + physts->mac_phy_intf_sel.mask, val); + + reg_nr = min(physts->tx_info.reg_nr, ARRAY_SIZE(tx_stat->info)); + for (i = 0; i < reg_nr; i++) + tx_stat->info[i] = rtw89_phy_read32(rtwdev, physts->tx_info.regs[i]); + + reg_nr = min(physts->tx_common_ctrl.reg_nr, ARRAY_SIZE(tx_stat->common_ctrl)); + for (i = 0; i < reg_nr; i++) + tx_stat->common_ctrl[i] = + rtw89_phy_read32(rtwdev, physts->tx_common_ctrl.regs[i]); + + reg_nr = min(chip->rf_path_num, ARRAY_SIZE(tx_stat->txpwr)); + for (i = 0; i < reg_nr; i++) + tx_stat->txpwr[i] = rtw89_phy_read32_mask(rtwdev, physts->txpwr[i].addr, + physts->txpwr[i].mask); + + tx_stat->type = u32_get_bits(tx_stat->info[0], TX_STATUS_TYPE); + + if (chip->chip_gen == RTW89_CHIP_AX) { + tx_stat->tx_path_en = u32_get_bits(tx_stat->info[0], TX_STATUS_TX_PATH_EN); + tx_stat->path_map = u32_get_bits(tx_stat->info[0], TX_STATUS_PATH_MAP); + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD); + tx_stat->txsc = u32_get_bits(tx_stat->info[1], TX_STATUS_TXSC); + tx_stat->bw = u32_get_bits(tx_stat->info[1], TX_STATUS_BW); + tx_stat->tmac_txpwr = u32_get_bits(tx_stat->info[1], TX_STATUS_TMAC_TXPWR); + + if (!(chip->chip_id == RTL8852A || rtw89_is_rtl885xb(rtwdev))) + tx_stat->max_mcs = u32_get_bits(tx_stat->info[3], TX_STATUS_MAX_MCS); + } else { + tx_stat->type = u32_get_bits(tx_stat->info[0], TX_STATUS_TYPE); + tx_stat->subtype = u32_get_bits(tx_stat->info[0], TX_STATUS_SUBTYPE); + + if (chip->chip_id == RTL8922A) + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD_V1); + else + tx_stat->txcmd = u32_get_bits(tx_stat->info[0], TX_STATUS_TXCMD_V2); + + tx_stat->txsc = u32_get_bits(tx_stat->info[0], TX_STATUS_TXSC_V1); + tx_stat->bw = u32_get_bits(tx_stat->info[1], TX_STATUS_BW_V1); + tx_stat->tmac_txpwr = u32_get_bits(tx_stat->info[1], TX_STATUS_TMAC_TXPWR_V1); + tx_stat->tx_path_en = u32_get_bits(tx_stat->info[2], TX_STATUS_TX_PATH_EN_V1); + tx_stat->path_map = u32_get_bits(tx_stat->info[2], TX_STATUS_PATH_MAP_V1); + tx_stat->max_mcs = u32_get_bits(tx_stat->info[4], TX_STATUS_MAX_MCS_V1); + } + + tx_stat->stbc = !!(tx_stat->common_ctrl[0] & TX_STATUS_STBC); +} + static void rtw89_phy_trigger_tx_count(struct rtw89_dev *rtwdev) { if (RTW89_CHK_FW_FEATURE(TX_HISTORY_V1, &rtwdev->fw)) @@ -6051,8 +6119,10 @@ static void rtw89_phy_stat_update(struct rtw89_dev *rtwdev) rtw89_phy_trigger_tx_count(rtwdev); - rtw89_for_each_active_bb(rtwdev, bb) + rtw89_for_each_active_bb(rtwdev, bb) { rtw89_phy_pmac_stat_update(rtwdev, bb); + rtw89_phy_tx_stat_update(rtwdev, bb); + } } void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) @@ -8702,10 +8772,31 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_ax = { .nhm_pwr_method_msk = B_NHM_PWDB_METHOD_MSK, }; +static const u32 rtw89_txinfo_reg_ax[] = { + R_TX_INFO_0_0_COMB, + R_TX_INFO_0_1_COMB, + R_TX_INFO_1_0_COMB, + R_TX_INFO_1_1_COMB +}; + +static const u32 rtw89_tx_common_ctrl_reg_ax[] = { + R_TX_COMMON_CTRL_0_0_COMB, + R_TX_COMMON_CTRL_0_1_COMB +}; + +static const struct rtw89_reg_def rtw89_txpwr_ax[] = { + {.addr = R_PATH0_TXPWR, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR, .mask = B_PATH1_TXPWR} +}; + static const struct rtw89_physts_regs rtw89_physts_regs_ax = { .setting_addr = R_PLCP_HISTOGRAM, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_ax, + .tx_info = RTW89_REGS_DEF(rtw89_txinfo_reg_ax), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_ax), }; static const struct rtw89_cfo_regs rtw89_cfo_regs_ax = { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index c6761cedc5a5..830cabefca39 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -57,6 +57,25 @@ #define RA_MASK_EHT_4SS_MCS0_11 GENMASK_ULL(62, 60) #define RA_MASK_EHT_RATES GENMASK_ULL(62, 12) +#define TX_STATUS_TYPE GENMASK(3, 0) +#define TX_STATUS_SUBTYPE GENMASK(7, 4) +#define TX_STATUS_TXCMD GENMASK(29, 24) +#define TX_STATUS_TXCMD_V1 GENMASK(13, 8) +#define TX_STATUS_TXCMD_V2 GENMASK(15, 8) +#define TX_STATUS_TXSC GENMASK(7, 4) +#define TX_STATUS_TXSC_V1 GENMASK(27, 24) +#define TX_STATUS_BW GENMASK(17, 16) +#define TX_STATUS_BW_V1 GENMASK(6, 4) +#define TX_STATUS_TMAC_TXPWR GENMASK(26, 18) +#define TX_STATUS_TMAC_TXPWR_V1 GENMASK(16, 8) +#define TX_STATUS_TX_PATH_EN GENMASK(15, 12) +#define TX_STATUS_TX_PATH_EN_V1 GENMASK(7, 4) +#define TX_STATUS_PATH_MAP GENMASK(23, 16) +#define TX_STATUS_PATH_MAP_V1 GENMASK(15, 8) +#define TX_STATUS_MAX_MCS GENMASK(7, 4) +#define TX_STATUS_MAX_MCS_V1 GENMASK(3, 0) +#define TX_STATUS_STBC BIT(0) + #define CFO_TRK_ENABLE_TH (2 << 2) #define CFO_TRK_STOP_TH_4 (30 << 2) #define CFO_TRK_STOP_TH_3 (20 << 2) @@ -458,6 +477,10 @@ struct rtw89_physts_regs { u32 setting_addr; u32 dis_trigger_fail_mask; u32 dis_trigger_brk_mask; + struct rtw89_reg_def mac_phy_intf_sel; + const struct rtw89_reg_def *txpwr; + struct rtw89_regs_def tx_info; + struct rtw89_regs_def tx_common_ctrl; }; struct rtw89_cfo_regs { diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 25f1b068daa2..9caddd19384c 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -135,16 +135,62 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_be_v1 = { .ifs_total_mask = B_IFS_TOTAL_BE4, }; +static const u32 rtw89_tx_info_reg_be[] = { + R_TX_INFO_0_0_COMB_V1, + R_TX_INFO_0_1_COMB_V1, + R_TX_INFO_1_0_COMB_V1, + R_TX_INFO_1_1_COMB_V1, + R_TX_INFO_2_0_COMB_V1, + R_TX_INFO_2_1_COMB_V1 +}; + +static const u32 rtw89_tx_common_ctrl_reg_be[] = { + R_TX_COMMON_CTRL_0_0_COMB_V1, + R_TX_COMMON_CTRL_0_1_COMB_V1 +}; + +static const struct rtw89_reg_def rtw89_txpwr_be[] = { + {.addr = R_PATH0_TXPWR_V1, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR_V1, .mask = B_PATH1_TXPWR} +}; + static const struct rtw89_physts_regs rtw89_physts_regs_be = { .setting_addr = R_PLCP_HISTOGRAM, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_be, + .tx_info = RTW89_REGS_DEF(rtw89_tx_info_reg_be), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_be), +}; + +static const u32 rtw89_tx_info_reg_be_v1[] = { + R_TX_INFO_0_0_COMB_BE4, + R_TX_INFO_0_1_COMB_BE4, + R_TX_INFO_1_0_COMB_BE4, + R_TX_INFO_1_1_COMB_BE4, + R_TX_INFO_2_0_COMB_BE4, + R_TX_INFO_2_1_COMB_BE4 +}; + +static const u32 rtw89_tx_common_ctrl_reg_be_v1[] = { + R_TX_COMMON_CTRL_0_0_COMB_BE4, + R_TX_COMMON_CTRL_0_1_COMB_BE4 +}; + +static const struct rtw89_reg_def rtw89_txpwr_be_v1[] = { + {.addr = R_PATH0_TXPWR_BE4, .mask = B_PATH0_TXPWR}, + {.addr = R_PATH1_TXPWR_BE4, .mask = B_PATH1_TXPWR} }; static const struct rtw89_physts_regs rtw89_physts_regs_be_v1 = { .setting_addr = R_PLCP_HISTOGRAM_BE_V1, .dis_trigger_fail_mask = B_STS_DIS_TRIG_BY_FAIL, .dis_trigger_brk_mask = B_STS_DIS_TRIG_BY_BRK, + .mac_phy_intf_sel = {R_INTF_R_INTF_RPT_SEL_BE4, B_INTF_R_INTF_RPT_SEL}, + .txpwr = rtw89_txpwr_be_v1, + .tx_info = RTW89_REGS_DEF(rtw89_tx_info_reg_be_v1), + .tx_common_ctrl = RTW89_REGS_DEF(rtw89_tx_common_ctrl_reg_be_v1), }; static const struct rtw89_cfo_regs rtw89_cfo_regs_be = { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index c054a402bd20..f5151e1ad58d 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8741,6 +8741,9 @@ #define B_DFS_FFT_EN BIT(0) #define R_CHINFO_DATA 0x00C0 #define B_CHINFO_DATA_BITMAP GENMASK(22, 0) +#define R_INTF_R_INTF_RPT_SEL 0x0200 +#define R_INTF_R_INTF_RPT_SEL_BE4 0x20200 +#define B_INTF_R_INTF_RPT_SEL BIT(12) #define R_ANAPAR_PW15 0x030C #define B_ANAPAR_PW15 GENMASK(31, 24) #define B_ANAPAR_PW15_H GENMASK(27, 24) @@ -9042,6 +9045,20 @@ #define B_SWSI_W_BUSY_V1 BIT(24) #define B_SWSI_R_BUSY_V1 BIT(25) #define B_SWSI_R_DATA_DONE_V1 BIT(26) +#define R_TX_INFO_0_0_COMB 0x1800 +#define R_TX_INFO_0_0_COMB_V1 0x3E00 +#define R_TX_INFO_0_1_COMB 0x1804 +#define R_TX_INFO_0_1_COMB_V1 0x3E04 +#define R_TX_INFO_1_0_COMB 0x1808 +#define R_TX_INFO_1_0_COMB_V1 0x3E08 +#define R_TX_INFO_1_1_COMB 0x180C +#define R_TX_INFO_1_1_COMB_V1 0x3E0C +#define R_TX_INFO_2_0_COMB_V1 0x3E10 +#define R_TX_INFO_2_1_COMB_V1 0x3E14 +#define R_TX_COMMON_CTRL_0_0_COMB 0x1810 +#define R_TX_COMMON_CTRL_0_0_COMB_V1 0x3E20 +#define R_TX_COMMON_CTRL_0_1_COMB 0x1814 +#define R_TX_COMMON_CTRL_0_1_COMB_V1 0x3E24 #define R_CNT_LSIG_BRK_S_TH 0x1A00 #define R_CNT_LSIG_BRK_S_TH_V1 0x0E00 #define R_CNT_LSIG_BRK_S_TH_BE4 0x20E00 @@ -9184,6 +9201,8 @@ #define B_TXAGC_BB GENMASK(31, 24) #define B_TXAGC_RF GENMASK(5, 0) #define R_PATH0_TXPWR 0x1C78 +#define R_PATH0_TXPWR_V1 0xEE0C +#define R_PATH0_TXPWR_BE4 0x2F90C #define B_PATH0_TXPWR GENMASK(8, 0) #define R_S0_ADDCK 0x1E00 #define B_S0_ADDCK_I GENMASK(9, 0) @@ -9328,6 +9347,8 @@ #define B_TXAGC_BB_S1_OFT GENMASK(31, 16) #define B_TXAGC_BB_S1 GENMASK(31, 24) #define R_PATH1_TXPWR 0x3C78 +#define R_PATH1_TXPWR_V1 0xEF0C +#define R_PATH1_TXPWR_BE4 0x2FA0C #define B_PATH1_TXPWR GENMASK(8, 0) #define R_S1_ADDCK 0x3E00 #define B_S1_ADDCK_I GENMASK(9, 0) @@ -10813,6 +10834,14 @@ #define B_SW_SI_R_BUSY_BE4 BIT(25) #define B_SW_SI_READ_DATA_DONE_BE4 BIT(26) +#define R_TX_INFO_0_0_COMB_BE4 0x2DF00 +#define R_TX_INFO_0_1_COMB_BE4 0x2DF04 +#define R_TX_INFO_1_0_COMB_BE4 0x2DF08 +#define R_TX_INFO_1_1_COMB_BE4 0x2DF0C +#define R_TX_INFO_2_0_COMB_BE4 0x2DF10 +#define R_TX_INFO_2_1_COMB_BE4 0x2DF14 +#define R_TX_COMMON_CTRL_0_0_COMB_BE4 0x2DF20 +#define R_TX_COMMON_CTRL_0_1_COMB_BE4 0x2DF24 #define R_RX_PATH0_TBL0_BE4 0x2E028 #define R_RX_PATH1_TBL0_BE4 0x2E128 From c77bb66e244e7b6cb2943234274fef278da19745 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:24 +0800 Subject: [PATCH 049/285] wifi: rtw89: debug: add RX statistics in bb_info Expand RX packet statistics including coding type, spatial diversity, and beamforming. These statistics are accumulated per PHY and displayed in bb_info debugfs. RX statistics output: == RX General LDPC: 190, BCC: 0, STBC: 0, SU_NON_BF: 0, SU_BF: 190, MU: 0 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 26 +++++++++++++++++----- drivers/net/wireless/realtek/rtw89/core.h | 10 +++++++++ drivers/net/wireless/realtek/rtw89/debug.c | 8 +++++++ drivers/net/wireless/realtek/rtw89/txrx.h | 2 ++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index d8f83623e54a..b85728ceb63c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2097,11 +2097,8 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u32 t; phy_ppdu->chan_idx = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_CH_IDX); - - if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) { - phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC); - phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC); - } + phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC); + phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC); if (!phy_ppdu->hdr_2_en) phy_ppdu->rx_path_en = @@ -2114,9 +2111,11 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, return; phy_ppdu->rpl_avg = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_RSSI_AVG_FD); + phy_ppdu->su = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_SU); phy_ppdu->ofdm.avg_snr = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_AVG_SNR); phy_ppdu->ofdm.evm_max = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MAX); phy_ppdu->ofdm.evm_min = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MIN); + phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF); phy_ppdu->ofdm.has = true; /* sign conversion for S(12,2) */ @@ -3062,6 +3061,23 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, if (desc_info->data_rate < RTW89_HW_RATE_NR) pkt_stat->rx_rate_cnt[desc_info->data_rate]++; + if (phy_ppdu && phy_ppdu->ofdm.has) { + if (phy_ppdu->ldpc) + pkt_stat->rx.ldpc++; + else + pkt_stat->rx.bcc++; + + if (phy_ppdu->stbc) + pkt_stat->rx.stbc++; + + if (!phy_ppdu->su) + pkt_stat->rx.mu++; + else if (phy_ppdu->bf) + pkt_stat->rx.su_bf++; + else + pkt_stat->rx.su_non_bf++; + } + rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, false, false); out: diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index e8b03716d41e..c0e176c4c3a6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -855,8 +855,10 @@ struct rtw89_rx_phy_ppdu { } ofdm; bool has_data; bool has_bcn; + bool su; bool ldpc; bool stbc; + bool bf; bool to_self; bool valid; bool hdr_2_en; @@ -5378,6 +5380,14 @@ struct rtw89_pkt_stat { u8 beacon_rate; u32 beacon_len; u32 rx_rate_cnt[RTW89_HW_RATE_NR]; + struct { + u32 ldpc; + u32 bcc; + u32 stbc; + u32 su_bf; + u32 su_non_bf; + u32 mu; + } rx; }; #define RTW89_BCN_TRACK_STAT_NR 32 diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index d9a5bbae11f2..103948aee5cb 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4303,6 +4303,7 @@ static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; const struct rtw89_physts_regs *physts = phy->physts; struct rtw89_pmac_stat_info *pmac = &bb->pmac_stat; struct rtw89_tx_stat_info *tx_stat = &bb->tx_stat; @@ -4384,6 +4385,13 @@ static int rtw89_get_bb_stat(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, tx_stat->common_ctrl[i], (i < reg_nr - 1) ? ", " : ""); p += scnprintf(p, end - p, "]\n\n"); + p += scnprintf(p, end - p, "== RX General\n"); + p += scnprintf(p, end - p, + "LDPC: %d, BCC: %d, STBC: %d, SU_NON_BF: %d, SU_BF: %d, MU: %d\n\n", + pkt_stat->rx.ldpc, pkt_stat->rx.bcc, + pkt_stat->rx.stbc, pkt_stat->rx.su_non_bf, + pkt_stat->rx.su_bf, pkt_stat->rx.mu); + p += scnprintf(p, end - p, "== RSSI/RX Rate\n"); p += rtw89_get_rx_pkt_stat(rtwdev, bb, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index b69a2529aefc..125ba2a9f145 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -634,8 +634,10 @@ struct rtw89_phy_sts_ie01 { #define RTW89_PHY_STS_IE01_W2_AVG_SNR GENMASK(5, 0) #define RTW89_PHY_STS_IE01_W2_EVM_MAX GENMASK(15, 8) #define RTW89_PHY_STS_IE01_W2_EVM_MIN GENMASK(23, 16) +#define RTW89_PHY_STS_IE01_W2_SU BIT(27) #define RTW89_PHY_STS_IE01_W2_LDPC BIT(28) #define RTW89_PHY_STS_IE01_W2_STBC BIT(30) +#define RTW89_PHY_STS_IE01_W3_BF BIT(8) struct rtw89_phy_sts_ie01_v2 { __le32 w0; From c1ed02655f9134d6af6a01a58b734329c2f4f22c Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Wed, 29 Apr 2026 21:26:25 +0800 Subject: [PATCH 050/285] wifi: rtw89: debug: add BB diagnose Add BB diagnostic to track potential abnormal conditions. Currently, five diagnostic metrics are monitored: 1) Hang detection monitors consecutive absence of TX and RX activity. 2) PD maximum triggers when PD stays at its maximum threshold for a period. 3) No RX occurs when no CCA activity is detected over multiple consecutive cycles. 4) High FA indicates a high false alarm ratio, reflecting severe environmental interference. 5) EDCCA alerts when high EDCCA ratio, signaling a potential TX hang. These metrics are exposed via debugfs diag_bb. Output: [PHY 0] Diag bitmap = 0x0 Event{Hang, PD MAX, No RX, High FA, High EDCCA Ratio} = {0, 0, 0, 0, 0} consecutive_no_tx_cnt=0, consecutive_no_rx_cnt=0 Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260429132625.1659182-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 23 ++++ drivers/net/wireless/realtek/rtw89/debug.c | 43 ++++++ drivers/net/wireless/realtek/rtw89/phy.c | 144 +++++++++++++++++++- drivers/net/wireless/realtek/rtw89/phy.h | 4 + drivers/net/wireless/realtek/rtw89/phy_be.c | 8 ++ drivers/net/wireless/realtek/rtw89/reg.h | 8 ++ 6 files changed, 227 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c0e176c4c3a6..bccfee7535a7 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5578,6 +5578,7 @@ struct rtw89_pmac_stat_info { u32 ofdm_phy_txon; u32 cnt_ofdm_cca; u32 cnt_cck_cca; + u32 cnt_cca_all; u32 cnt_cck_spoofing; u32 cnt_ofdm_spoofing; u32 cnt_ampdu_miss; @@ -5601,6 +5602,7 @@ struct rtw89_pmac_stat_info { u32 cnt_sig_gg; u32 cnt_cck_fail; u32 cnt_ofdm_fail; + u32 cnt_fail_all; u32 cnt_lsig_brk_s_th; u32 cnt_lsig_brk_l_th; u32 cnt_parity_fail; @@ -5624,6 +5626,23 @@ struct rtw89_tx_stat_info { bool stbc; }; +enum rtw89_diag_bb_type { + RTW89_DIAG_BB_HANG, + RTW89_DIAG_BB_PD, + RTW89_DIAG_BB_NO_RX, + RTW89_DIAG_BB_FA, + RTW89_DIAG_BB_EDCCA, + + RTW89_DIAG_BB_NR, +}; + +struct rtw89_diag_bb { + u32 diag_bb_bitmap; /* bitmap of enum rtw89_diag_bb_type */ + u32 diag_bb_cnt[RTW89_DIAG_BB_NR]; + u16 consecutive_no_tx_cnt; + u16 consecutive_no_rx_cnt; +}; + struct rtw89_agc_gaincode_set { u8 lna_idx; u8 tia_idx; @@ -5639,6 +5658,7 @@ struct rtw89_dig_info { struct rtw89_agc_gaincode_set cur_gaincode; bool force_gaincode_idx_en; struct rtw89_agc_gaincode_set force_gaincode; + enum rtw89_dig_noisy_level noisy_lv; u8 igi_rssi_th[IGI_RSSI_TH_NUM]; u16 fa_th[FA_TH_NUM]; u8 igi_rssi; @@ -5648,6 +5668,7 @@ struct rtw89_dig_info { u8 dyn_igi_min; bool dyn_pd_th_en; u8 dyn_pd_th_max; + u8 dyn_pd_max_cnt; u8 pd_low_th_ofst; u8 ib_pbk; s8 ib_pkpwr; @@ -5912,6 +5933,7 @@ struct rtw89_env_monitor_info { u8 nhm_th[RTW89_NHM_RPT_NUM]; struct rtw89_nhm_report *nhm_his[RTW89_BAND_NUM]; struct list_head nhm_rpt_list; + u8 edcca_clm_ratio; }; enum rtw89_ser_rcvy_step { @@ -6452,6 +6474,7 @@ struct rtw89_dev { struct rtw89_pkt_stat last_pkt_stat; struct rtw89_pmac_stat_info pmac_stat; struct rtw89_tx_stat_info tx_stat; + struct rtw89_diag_bb diag; } bbs[RTW89_PHY_NUM]; struct wiphy_delayed_work track_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 103948aee5cb..597052261974 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -92,6 +92,7 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv mlo_mode; struct rtw89_debugfs_priv beacon_info; struct rtw89_debugfs_priv diag_mac; + struct rtw89_debugfs_priv diag_bb; }; struct rtw89_debugfs_iter_data { @@ -5214,6 +5215,46 @@ rtw89_debug_priv_diag_mac_get(struct rtw89_dev *rtwdev, return rtw89_mac_diag_iter_all(rtwdev, buf, bufsz); } +static int rtw89_get_diag_bb(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + struct rtw89_diag_bb *diag = &bb->diag; + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "Diag bitmap = 0x%x\n", diag->diag_bb_bitmap); + p += scnprintf(p, end - p, + "Event{Hang, PD MAX, No RX, High FA, High EDCCA Ratio} = "); + p += scnprintf(p, end - p, "{%d, %d, %d, %d, %d}\n", + diag->diag_bb_cnt[RTW89_DIAG_BB_HANG], + diag->diag_bb_cnt[RTW89_DIAG_BB_PD], + diag->diag_bb_cnt[RTW89_DIAG_BB_NO_RX], + diag->diag_bb_cnt[RTW89_DIAG_BB_FA], + diag->diag_bb_cnt[RTW89_DIAG_BB_EDCCA]); + p += scnprintf(p, end - p, + "consecutive_no_tx_cnt=%d, consecutive_no_rx_cnt=%d\n\n", + diag->consecutive_no_tx_cnt, + diag->consecutive_no_rx_cnt); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + char *p = buf, *end = buf + bufsz; + struct rtw89_bb_ctx *bb; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_diag_bb(rtwdev, bb, p, end - p); + + return p - buf; +} + static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, char *buf, size_t bufsz) { @@ -5348,6 +5389,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .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), + .diag_bb = rtw89_debug_priv_get(diag_bb, RSIZE_8K, RLOCK), }; #define rtw89_debugfs_add(name, mode, fopname, parent) \ @@ -5398,6 +5440,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_rw(mlo_mode); rtw89_debugfs_add_r(beacon_info); rtw89_debugfs_add_r(diag_mac); + rtw89_debugfs_add_r(diag_bb); } void rtw89_debugfs_init(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index cf7382e36b75..f98a77f9fc1a 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -5847,6 +5847,50 @@ static void rtw89_phy_stat_init(struct rtw89_dev *rtwdev) rtwdev->hal.thermal_prot_lv = 0; } +static void rtw89_phy_pmac_diag(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +#define RTW89_DIAG_BB_FA_RATIO_TH 80 +#define RTW89_DIAG_BB_NO_RX_TH 5 +#define RTW89_DIAG_BB_HANG_TH 30 + struct rtw89_pmac_stat_info *pmac_stat = &bb->pmac_stat; + bool is_linked = rtwdev->total_sta_assoc > 0; + struct rtw89_diag_bb *diag = &bb->diag; + u8 fa_ratio = 0; + + if (!(pmac_stat->cck_mac_txen || pmac_stat->ofdm_mac_txen)) + diag->consecutive_no_tx_cnt++; + else + diag->consecutive_no_tx_cnt = 0; + + if (!pmac_stat->cnt_cca_all) + diag->consecutive_no_rx_cnt++; + else + diag->consecutive_no_rx_cnt = 0; + + if (pmac_stat->cnt_cca_all) + fa_ratio = pmac_stat->cnt_fail_all * PERCENT / pmac_stat->cnt_cca_all; + + diag->diag_bb_bitmap &= ~(BIT(RTW89_DIAG_BB_NO_RX) | + BIT(RTW89_DIAG_BB_HANG) | + BIT(RTW89_DIAG_BB_FA)); + + if (diag->consecutive_no_rx_cnt >= RTW89_DIAG_BB_NO_RX_TH && is_linked) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_NO_RX); + diag->diag_bb_cnt[RTW89_DIAG_BB_NO_RX]++; + } + + if (fa_ratio >= RTW89_DIAG_BB_FA_RATIO_TH && is_linked) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_FA); + diag->diag_bb_cnt[RTW89_DIAG_BB_FA]++; + } + + if (diag->consecutive_no_tx_cnt > RTW89_DIAG_BB_HANG_TH && + diag->consecutive_no_rx_cnt > RTW89_DIAG_BB_HANG_TH) { + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_HANG); + diag->diag_bb_cnt[RTW89_DIAG_BB_HANG]++; + } +} + static void rtw89_phy_pmac_stat_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, bool cck) { @@ -6017,6 +6061,14 @@ static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, rtw89_phy_pmac_stat_ofdm(rtwdev, bb); + pmac->cnt_fail_all = pmac->cnt_ofdm_fail; + pmac->cnt_cca_all = pmac->cnt_ofdm_cca; + + if (cck) { + pmac->cnt_fail_all += pmac->cnt_cck_fail; + pmac->cnt_cca_all += pmac->cnt_cck_cca; + } + pmac->cnt_crc32_error_all = pmac->cnt_he_crc32_error + pmac->cnt_vht_crc32_error + pmac->cnt_ht_crc32_error + @@ -6034,6 +6086,7 @@ static void rtw89_phy_pmac_stat_update(struct rtw89_dev *rtwdev, pmac->cnt_crc32_ok_all += pmac->cnt_eht_crc32_ok; } + rtw89_phy_pmac_diag(rtwdev, bb); rtw89_phy_pmac_stat_reset(rtwdev, bb, cck); } @@ -6722,6 +6775,29 @@ static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev, return true; } +static bool rtw89_phy_edcca_clm_get_result(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + struct rtw89_env_monitor_info *env = &bb->env_monitor; + const struct rtw89_ccx_regs *ccx = phy->ccx; + u32 val; + + val = rtw89_phy_read32_idx(rtwdev, ccx->edcca_clm_rdy, + ccx->edcca_clm_rdy_mask, bb->phy_idx); + if (!val) { + rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, + "Get EDCCA_CLM report Fail\n"); + return false; + } + + val = rtw89_phy_read32_idx(rtwdev, ccx->edcca_clm_cnt, + ccx->edcca_clm_cnt_mask, bb->phy_idx); + env->edcca_clm_ratio = rtw89_phy_ccx_get_report(rtwdev, bb, val, PERCENT); + + return true; +} + static void rtw89_phy_nhm_th_update(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -6889,6 +6965,9 @@ static void __rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev, if (rtw89_phy_ifs_clm_get_result(rtwdev, bb)) env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_IFS_CLM; + if (rtw89_phy_edcca_clm_get_result(rtwdev, bb)) + env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_EDCCA_CLM; + rtw89_phy_ccx_racing_release(rtwdev, bb); para.mntr_time = 1900; para.rac_lv = RTW89_RAC_LV_1; @@ -7195,6 +7274,7 @@ static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev, dig->dyn_igi_max = igi_max_performance_mode; dig->dyn_igi_min = dynamic_igi_min; dig->dyn_pd_th_max = dynamic_pd_threshold_max; + dig->dyn_pd_max_cnt = 0; dig->pd_low_th_ofst = pd_low_th_offset; dig->is_linked_pre = false; } @@ -7318,6 +7398,7 @@ static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev, igi_offset = min_t(u8, igi_offset, IGI_OFFSET_MAX); dig->fa_rssi_ofst = igi_offset; + dig->noisy_lv = noisy_lv; rtw89_debug(rtwdev, RTW89_DBG_DIG, "fa_th: [+6 (%d) +4 (%d) +2 (%d) 0 (%d) -2 ]\n", @@ -7548,10 +7629,33 @@ void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) rtw89_phy_dig_update_para(rtwdev, bb); } +static void rtw89_phy_dig_diag(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool is_linked) +{ +#define RTW89_DIAG_BB_PD_MAX_PERIOD 5 + struct rtw89_diag_bb *diag = &bb->diag; + struct rtw89_dig_info *dig = &bb->dig; + + diag->diag_bb_bitmap &= ~BIT(RTW89_DIAG_BB_PD); + + if (dig->dyn_igi_max != dig->igi_fa_rssi || + dig->noisy_lv < RTW89_DIG_NOISY_LEVEL_MAX) { + dig->dyn_pd_max_cnt = 0; + return; + } + + if (++dig->dyn_pd_max_cnt < RTW89_DIAG_BB_PD_MAX_PERIOD || !is_linked) + return; + + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_PD); + diag->diag_bb_cnt[RTW89_DIAG_BB_PD]++; +} + #define IGI_RSSI_MIN 10 #define ABS_IGI_MIN 0xc static -void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool is_linked) { struct rtw89_dig_info *dig = &bb->dig; u8 igi_min; @@ -7569,6 +7673,8 @@ void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb } else { dig->igi_fa_rssi = dig->dyn_igi_max; } + + rtw89_phy_dig_diag(rtwdev, bb, is_linked); } struct rtw89_phy_iter_mcc_dig { @@ -7597,7 +7703,7 @@ static void rtw89_phy_set_mcc_dig(struct rtw89_dev *rtwdev, } chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - rtw89_phy_cal_igi_fa_rssi(rtwdev, bb); + rtw89_phy_cal_igi_fa_rssi(rtwdev, bb, is_linked); pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, dig->igi_fa_rssi, is_linked, chan); rtw89_fw_h2c_mcc_dig(rtwdev, rtwvif_link->chanctx_idx, @@ -7742,7 +7848,7 @@ static void __rtw89_phy_dig(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) } dig->is_linked_pre = is_linked; - rtw89_phy_cal_igi_fa_rssi(rtwdev, bb); + rtw89_phy_cal_igi_fa_rssi(rtwdev, bb, is_linked); rtw89_debug(rtwdev, RTW89_DBG_DIG, "rssi=%03d, dyn_joint(max,min)=(%d,%d), final_rssi=%d\n", @@ -7997,6 +8103,14 @@ static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev) __rtw89_phy_env_monitor_init(rtwdev, bb); } +static void rtw89_phy_diag_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + memset(&bb->diag, 0, sizeof(bb->diag)); +} + static void __rtw89_phy_edcca_init(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -8037,6 +8151,7 @@ void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) rtw89_chip_bb_sethw(rtwdev); + rtw89_phy_diag_init(rtwdev); rtw89_phy_env_monitor_init(rtwdev); rtw89_phy_nhm_setting_init(rtwdev); rtw89_physts_parsing_init(rtwdev); @@ -8613,6 +8728,24 @@ void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb edcca_regs->ppdu_mask, th, bb->phy_idx); } +static void rtw89_phy_edcca_diag(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +#define RTW89_DIAG_BB_EDCCA_RATIO_TH 50 +#define RTW89_DIAG_BB_EDCCA_TX_TH 10 + struct rtw89_env_monitor_info *env = &bb->env_monitor; + struct rtw89_traffic_stats *stats = &rtwdev->stats; + struct rtw89_diag_bb *diag = &bb->diag; + + diag->diag_bb_bitmap &= ~BIT(RTW89_DIAG_BB_EDCCA); + + if (stats->tx_throughput <= RTW89_DIAG_BB_EDCCA_TX_TH || + env->edcca_clm_ratio <= RTW89_DIAG_BB_EDCCA_RATIO_TH) + return; + + diag->diag_bb_bitmap |= BIT(RTW89_DIAG_BB_EDCCA); + diag->diag_bb_cnt[RTW89_DIAG_BB_EDCCA]++; +} + static void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { @@ -8620,6 +8753,7 @@ void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) rtw89_phy_edcca_thre_calc(rtwdev, bb); rtw89_phy_edcca_log(rtwdev, bb); + rtw89_phy_edcca_diag(rtwdev, bb); } void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev) @@ -8770,6 +8904,10 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_ax = { .nhm_en_mask = B_NHM_EN_MSK, .nhm_method = R_NHM_TH9, .nhm_pwr_method_msk = B_NHM_PWDB_METHOD_MSK, + .edcca_clm_rdy = R_CLM_EDCCA_RDY, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, }; static const u32 rtw89_txinfo_reg_ax[] = { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 830cabefca39..a6e685654037 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -471,6 +471,10 @@ struct rtw89_ccx_regs { u32 nhm_en_mask; u32 nhm_method; u32 nhm_pwr_method_msk; + u32 edcca_clm_rdy; + u32 edcca_clm_rdy_mask; + u32 edcca_clm_cnt; + u32 edcca_clm_cnt_mask; }; struct rtw89_physts_regs { diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 9caddd19384c..5cd298a2c91b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -74,6 +74,10 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_be = { .nhm_en_mask = B_NHM_EN_MSK, .nhm_method = R_NHM_TH9, .nhm_pwr_method_msk = B_NHM_PWDB_METHOD_MSK, + .edcca_clm_rdy = R_CLM_EDCCA_RDY_V1, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT_V1, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, }; static const struct rtw89_ccx_regs rtw89_ccx_regs_be_v1 = { @@ -133,6 +137,10 @@ static const struct rtw89_ccx_regs rtw89_ccx_regs_be_v1 = { .ifs_total_addr = R_IFS_TOTAL_BE4, .ifs_cnt_done_mask = B_IFS_CNT_DONE_BE4, .ifs_total_mask = B_IFS_TOTAL_BE4, + .edcca_clm_rdy = R_CLM_EDCCA_RDY_BE4, + .edcca_clm_rdy_mask = B_CLM_EDCCA_RDY, + .edcca_clm_cnt = R_CLM_EDCCA_RESULT_BE4, + .edcca_clm_cnt_mask = B_CLM_EDCCA_RESULT, }; static const u32 rtw89_tx_info_reg_be[] = { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index f5151e1ad58d..716747c3a10e 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -9146,6 +9146,14 @@ #define B_NHM_CNT11_MSK GENMASK(31, 16) #define R_NHM_AX 0x1AA4 #define B_NHM_READY_MSK BIT(16) +#define R_CLM_EDCCA_RESULT 0x1AC8 +#define R_CLM_EDCCA_RESULT_V1 0x0EC8 +#define R_CLM_EDCCA_RESULT_BE4 0x20EC8 +#define B_CLM_EDCCA_RESULT GENMASK(15, 0) +#define R_CLM_EDCCA_RDY 0x1AC8 +#define R_CLM_EDCCA_RDY_V1 0x0EC8 +#define R_CLM_EDCCA_RDY_BE4 0x20EC8 +#define B_CLM_EDCCA_RDY BIT(16) #define R_IFS_CLM_TX_CNT 0x1ACC #define R_IFS_CLM_TX_CNT_V1 0x0ECC #define R_IFS_CLM_TX_CNT_BE4 0x20ECC From 756dc69e5639cd339be3d501ad068b01a2b366de Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:47 +0800 Subject: [PATCH 051/285] wifi: rtw89: add AMPDU to radiotap The RX desc can report current frame is in AMPDU, but no way point out if it is a last one in AMPDU. Update AMPDU reference only. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 8 ++++++++ drivers/net/wireless/realtek/rtw89/core.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index b85728ceb63c..240502b25b2e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3568,6 +3568,7 @@ void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, AX_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, AX_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, AX_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, AX_RXD_AMPDU); shift_len = desc_info->shift << 1; /* 2-byte unit */ drv_info_len = desc_info->drv_info_size << 3; /* 8-byte unit */ @@ -3624,6 +3625,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, BE_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, BE_RXD_AMPDU); desc_info->bw = le32_get_bits(rxd_s->dword4, BE_RXD_BW_MASK); desc_info->data_rate = le32_get_bits(rxd_s->dword4, BE_RXD_RX_DATARATE_MASK); @@ -3698,6 +3700,7 @@ void rtw89_core_query_rxdesc_v3(struct rtw89_dev *rtwdev, desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC); desc_info->sw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_SW_DEC); desc_info->addr1_match = le32_get_bits(rxd_s->dword3, BE_RXD_A1_MATCH); + desc_info->ampdu = le32_get_bits(rxd_s->dword3, BE_RXD_AMPDU); desc_info->bw = le32_get_bits(rxd_s->dword4, BE_RXD_BW_MASK); desc_info->data_rate = le32_get_bits(rxd_s->dword4, BE_RXD_RX_DATARATE_MASK); @@ -3830,6 +3833,11 @@ static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev, !(desc_info->sw_dec || desc_info->icv_err)) rx_status->flag |= RX_FLAG_DECRYPTED; + if (desc_info->ampdu) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->ampdu_reference = desc_info->ppdu_cnt; + } + rx_status->bw = rtw89_hw_to_rate_info_bw(desc_info->bw); data_rate = desc_info->data_rate; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index bccfee7535a7..66dbb1fc3ca8 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1130,6 +1130,7 @@ struct rtw89_rx_desc_info { bool hw_dec; bool sw_dec; bool addr1_match; + bool ampdu; u8 frag; u16 seq; u8 frame_type; From 9535e2279ba994fcb38c58f508589cd394894f90 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:48 +0800 Subject: [PATCH 052/285] wifi: rtw89: add VHT beamformed to radiotap Set VHT beamformed bit by PHY status IE-01 report. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 240502b25b2e..d6bf1d57e8e1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2099,6 +2099,7 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, phy_ppdu->chan_idx = le32_get_bits(ie->w0, RTW89_PHY_STS_IE01_W0_CH_IDX); phy_ppdu->ldpc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_LDPC); phy_ppdu->stbc = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_STBC); + phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF); if (!phy_ppdu->hdr_2_en) phy_ppdu->rx_path_en = @@ -2115,7 +2116,6 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, phy_ppdu->ofdm.avg_snr = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_AVG_SNR); phy_ppdu->ofdm.evm_max = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MAX); phy_ppdu->ofdm.evm_min = le32_get_bits(ie->w2, RTW89_PHY_STS_IE01_W2_EVM_MIN); - phy_ppdu->bf = le32_get_bits(ie->w3, RTW89_PHY_STS_IE01_W3_BF); phy_ppdu->ofdm.has = true; /* sign conversion for S(12,2) */ @@ -3146,6 +3146,8 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev, if (!phy_ppdu) return; + if (phy_ppdu->bf) + rx_status->enc_flags |= RX_ENC_FLAG_BF; if (phy_ppdu->ldpc) rx_status->enc_flags |= RX_ENC_FLAG_LDPC; if (phy_ppdu->stbc) @@ -6848,7 +6850,8 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | IEEE80211_RADIOTAP_MCS_HAVE_STBC; - hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC; + hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | + IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; ieee80211_hw_set(hw, SIGNAL_DBM); ieee80211_hw_set(hw, HAS_RATE_CONTROL); From a371139d8be96f851c8663f9f8e4ffe3c3271bac Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:49 +0800 Subject: [PATCH 053/285] wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR The SNIFFER_MODE bit can ignore filter rules, and receive packets to driver, so set the bit to accept all packets. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac80211.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index b72f6661fbd1..2c6711133c80 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -356,6 +356,11 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, } } + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) + rtwdev->hal.rx_fltr |= B_AX_SNIFFER_MODE; + else + rtwdev->hal.rx_fltr &= ~B_AX_SNIFFER_MODE; + rx_fltr = rtwdev->hal.rx_fltr; /* mac80211 doesn't configure filter when HW scan, driver need to From c847d0c14c47c2878908c6e9bdcfb707bdb88b60 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:50 +0800 Subject: [PATCH 054/285] wifi: rtw89: phy: define PHY status IE length for generations Both RTL8922A and RTL8922D are WiFi 7 chips, but their IE length of PHY status are different. Define them accordingly. Generation 0: WiFi 6 chips Generation 1: WiFi 7 RTL8922A Generation 2: WiFi 7 RTL8922D Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 19 ++----------------- drivers/net/wireless/realtek/rtw89/phy.c | 4 ++++ drivers/net/wireless/realtek/rtw89/phy.h | 5 +++++ drivers/net/wireless/realtek/rtw89/phy_be.c | 8 ++++++++ 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index d6bf1d57e8e1..caedb2bd21d5 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2040,29 +2040,14 @@ static void rtw89_core_rx_process_phy_ppdu_iter(void *data, } } -#define VAR_LEN 0xff -#define VAR_LEN_UNIT 8 static u16 rtw89_core_get_phy_status_ie_len(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr) { - static const u8 physts_ie_len_tabs[RTW89_CHIP_GEN_NUM][32] = { - [RTW89_CHIP_AX] = { - 16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, - VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN, - VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32 - }, - [RTW89_CHIP_BE] = { - 32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, - VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN, - VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32 - }, - }; - const u8 *physts_ie_len_tab; + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const u8 *physts_ie_len_tab = phy->physt_ie_len; u16 ie_len; u8 ie; - physts_ie_len_tab = physts_ie_len_tabs[rtwdev->chip->chip_gen]; - ie = le32_get_bits(iehdr->w0, RTW89_PHY_STS_IEHDR_TYPE); if (physts_ie_len_tab[ie] != VAR_LEN) ie_len = physts_ie_len_tab[ie]; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index f98a77f9fc1a..4f82b1a9fa4c 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -8948,6 +8948,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_ax = { .cr_base = 0x10000, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START, .physt_bmp_eht = 0xfc, + .physt_ie_len = {16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN, + VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 0, .ccx = &rtw89_ccx_regs_ax, .physts = &rtw89_physts_regs_ax, .cfo = &rtw89_cfo_regs_ax, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index a6e685654037..d8038ae5ca86 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -154,6 +154,9 @@ #define EDCCA_UNIT_CONVER 128 #define EDCCA_PWROFST_DEFAULT 18 +#define VAR_LEN 0xff +#define VAR_LEN_UNIT 8 + enum rtw89_phy_c2h_ra_func { RTW89_PHY_C2H_FUNC_STS_RPT, RTW89_PHY_C2H_FUNC_MU_GPTBL_RPT, @@ -573,6 +576,8 @@ struct rtw89_phy_gen_def { u32 cr_base; u32 physt_bmp_start; u32 physt_bmp_eht; + u8 physt_ie_len[32]; + u8 physt_gen; const struct rtw89_ccx_regs *ccx; const struct rtw89_physts_regs *physts; const struct rtw89_cfo_regs *cfo; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 5cd298a2c91b..23137f2dbd4b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -1561,6 +1561,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be = { .cr_base = 0x20000, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START, .physt_bmp_eht = R_PHY_STS_BITMAP_EHT, + .physt_ie_len = {32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN, + VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 1, .ccx = &rtw89_ccx_regs_be, .physts = &rtw89_physts_regs_be, .cfo = &rtw89_cfo_regs_be, @@ -1582,6 +1586,10 @@ const struct rtw89_phy_gen_def rtw89_phy_gen_be_v1 = { .cr_base = 0x0, .physt_bmp_start = R_PHY_STS_BITMAP_ADDR_START_BE4, .physt_bmp_eht = R_PHY_STS_BITMAP_EHT_BE4, + .physt_ie_len = {32, 40, 24, 24, 16, 16, 16, 16, VAR_LEN, VAR_LEN, VAR_LEN, 168, + VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 32, 56, + 96, VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32}, + .physt_gen = 2, .ccx = &rtw89_ccx_regs_be_v1, .physts = &rtw89_physts_regs_be_v1, .cfo = &rtw89_cfo_regs_be_v1, From 847758f15f466235df9bf64272dbdd3ded14520e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:51 +0800 Subject: [PATCH 055/285] wifi: rtw89: phy: enable IE-09/IE-10 PHY status report for monitor mode The IE-09/IE-10 of PHY status contain SIG-A/SIG-B respectively, so enable them in monitor mode to have rich information. If the parser detects length invalid, ignore to reference IE-09/IE-10 to prevent accessing out of range. The RTL8922D is generation 2 of PHY status, which doesn't report SIG-B by IE-10, so not enable it. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 43 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 6 ++- drivers/net/wireless/realtek/rtw89/mac80211.c | 3 ++ drivers/net/wireless/realtek/rtw89/phy.c | 17 +++++++- drivers/net/wireless/realtek/rtw89/phy.h | 1 + drivers/net/wireless/realtek/rtw89/txrx.h | 9 ++++ 6 files changed, 74 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index caedb2bd21d5..f877c2707c84 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2152,14 +2152,38 @@ static void rtw89_core_parse_phy_status_ie00_v2(struct rtw89_dev *rtwdev, rpl_path[i] = tmp_rpl[i] >> 1; } +static void rtw89_core_parse_phy_status_ie09(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_iehdr *iehdr, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + phy_ppdu->ie09 = (const void *)iehdr; +} + +static void rtw89_core_parse_phy_status_ie10(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_iehdr *iehdr, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + phy_ppdu->ie10 = (const void *)iehdr; +} + static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr, struct rtw89_rx_phy_ppdu *phy_ppdu) { + bool accept; u8 ie; ie = le32_get_bits(iehdr->w0, RTW89_PHY_STS_IEHDR_TYPE); + /* + * For normal mode, only parse ppdu_sts that are A1-matched, except for + * scanning that needs to get chan_idx in IE01. + */ + accept = phy_ppdu->to_self || ie == RTW89_PHYSTS_IE01_CMN_OFDM || + rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR; + if (!accept) + return 0; + switch (ie) { case RTW89_PHYSTS_IE00_CMN_CCK: rtw89_core_parse_phy_status_ie00(rtwdev, iehdr, phy_ppdu); @@ -2169,6 +2193,12 @@ static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev, case RTW89_PHYSTS_IE01_CMN_OFDM: rtw89_core_parse_phy_status_ie01(rtwdev, iehdr, phy_ppdu); break; + case RTW89_PHYSTS_IE09_FTR_0: + rtw89_core_parse_phy_status_ie09(rtwdev, iehdr, phy_ppdu); + break; + case RTW89_PHYSTS_IE10_FTR_PLCP_EXT: + rtw89_core_parse_phy_status_ie10(rtwdev, iehdr, phy_ppdu); + break; default: break; } @@ -2228,11 +2258,14 @@ static int rtw89_core_rx_process_phy_ppdu(struct rtw89_dev *rtwdev, static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu) { - u16 ie_len; void *pos, *end; + bool accept; + u16 ie_len; - /* mark invalid reports and bypass them */ - if (phy_ppdu->ie < RTW89_CCK_PKT) + /* for normal mode, mark invalid reports and bypass them */ + accept = phy_ppdu->ie >= RTW89_CCK_PKT || + rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR; + if (!accept) return -EINVAL; pos = phy_ppdu->buf + PHY_STS_HDR_LEN; @@ -2246,6 +2279,10 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu); pos += ie_len; if (pos > end || ie_len == 0) { + /* clear pointers to prevent accessing out of IE */ + phy_ppdu->ie09 = NULL; + phy_ppdu->ie10 = NULL; + rtw89_debug(rtwdev, RTW89_DBG_TXRX, "phy status parse failed\n"); return -EINVAL; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 66dbb1fc3ca8..ca716e95cb2c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -24,6 +24,8 @@ struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; struct rtw89_phy_rfk_log_fmt; struct rtw89_phy_calc_efuse_gain; +struct rtw89_phy_sts_ie09; +struct rtw89_phy_sts_ie10; struct rtw89_debugfs; struct rtw89_regd_data; struct rtw89_wow_cam_info; @@ -840,7 +842,7 @@ struct rtw89_rx_phy_ppdu { u8 mac_id; u8 chan_idx; u8 phy_idx; - u8 ie; + u8 ie; /* enum rtw89_phy_status_bitmap */ u16 rate; u8 rpl_avg; u8 rpl_path[RF_PATH_MAX]; @@ -862,6 +864,8 @@ struct rtw89_rx_phy_ppdu { bool to_self; bool valid; bool hdr_2_en; + const struct rtw89_phy_sts_ie09 *ie09; /* SIG-A */ + const struct rtw89_phy_sts_ie10 *ie10; /* SIG-B */ }; enum rtw89_mac_idx { diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 2c6711133c80..330ece51286d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -98,6 +98,9 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) !rtwdev->scanning) rtw89_enter_ips(rtwdev); + if (changed & IEEE80211_CONF_CHANGE_MONITOR) + rtw89_physts_parsing_init(rtwdev); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 4f82b1a9fa4c..15483a86951d 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -7085,10 +7085,21 @@ static void rtw89_physts_enable_hdr_2(struct rtw89_dev *rtwdev, enum rtw89_phy_i static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; const struct rtw89_chip_info *chip = rtwdev->chip; + u32 monitor_mode_mu_ies = 0; + u32 monitor_mode_su_ies = 0; u32 val; u8 i; + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) { + monitor_mode_mu_ies = BIT(RTW89_PHYSTS_IE09_FTR_0); + if (phy->physt_gen < 2) + monitor_mode_mu_ies |= BIT(RTW89_PHYSTS_IE10_FTR_PLCP_EXT); + + monitor_mode_su_ies = BIT(RTW89_PHYSTS_IE09_FTR_0); + } + rtw89_physts_enable_fail_report(rtwdev, false, phy_idx); /* enable hdr_2 for 8922D (PHYSTS_BE_GEN2 above) */ @@ -7102,6 +7113,7 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, val = rtw89_physts_get_ie_bitmap(rtwdev, i, phy_idx); if (i == RTW89_HE_MU || i == RTW89_VHT_MU) { val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF); + val |= monitor_mode_mu_ies; } else if (i == RTW89_TRIG_BASE_PPDU) { val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF) | BIT(RTW89_PHYSTS_IE01_CMN_OFDM); @@ -7115,11 +7127,14 @@ static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev, val |= BIT(RTW89_PHYSTS_IE20_DBG_OFDM_FD_USER_SEG_0); } + if (i == RTW89_HE_PKT || i == RTW89_VHT_PKT) + val |= monitor_mode_su_ies; + rtw89_physts_set_ie_bitmap(rtwdev, i, val, phy_idx); } } -static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) +void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) { __rtw89_physts_parsing_init(rtwdev, RTW89_PHY_0); if (rtwdev->dbcc_en) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index d8038ae5ca86..74fbf5baff58 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -914,6 +914,7 @@ void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, void rtw89_phy_dm_init(struct rtw89_dev *rtwdev); void rtw89_phy_dm_reinit(struct rtw89_dev *rtwdev); void rtw89_phy_dm_init_data(struct rtw89_dev *rtwdev); +void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev); void rtw89_phy_write32_idx(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 data, enum rtw89_phy_idx phy_idx); void rtw89_phy_write32_idx_set(struct rtw89_dev *rtwdev, u32 addr, u32 bits, diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 125ba2a9f145..18fe6d3d0f83 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -658,6 +658,15 @@ struct rtw89_phy_sts_ie01_v2 { #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_C GENMASK(11, 4) #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_D GENMASK(23, 16) +struct rtw89_phy_sts_ie09 { + __le64 qw0; +} __packed; + +struct rtw89_phy_sts_ie10 { + __le64 qw0; + u8 sigb[]; +} __packed; + enum rtw89_tx_channel { RTW89_TXCH_ACH0 = 0, RTW89_TXCH_ACH1 = 1, From bc1006b68b89dcc2abc3dc79f9be6fa53642461c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:52 +0800 Subject: [PATCH 056/285] wifi: rtw89: move HE radiotap to an individual function To implement more fields of HE radiotap, move the code to an individual function ahead. Not change logic at all. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 36 +++++++++++++---------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index f877c2707c84..a27e482e38a6 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3176,6 +3176,24 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev, rx_status->enc_flags |= u8_encode_bits(1, RX_ENC_FLAG_STBC_MASK); } +static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + static const struct ieee80211_radiotap_he known_he = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; + struct ieee80211_radiotap_he *he; + + rx_status->flag |= RX_FLAG_RADIOTAP_HE; + he = skb_push(skb, sizeof(*he)); + *he = known_he; +} + static const u8 rx_status_bw_to_radiotap_eht_usig[] = { [RATE_INFO_BW_20] = IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_20MHZ, [RATE_INFO_BW_5] = U8_MAX, @@ -3250,25 +3268,13 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status) { - static const struct ieee80211_radiotap_he known_he = { - .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | - IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), - .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), - }; - struct ieee80211_radiotap_he *he; - if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) return; - if (rx_status->encoding == RX_ENC_HE) { - rx_status->flag |= RX_FLAG_RADIOTAP_HE; - he = skb_push(skb, sizeof(*he)); - *he = known_he; - } else if (rx_status->encoding == RX_ENC_EHT) { + if (rx_status->encoding == RX_ENC_HE) + rtw89_core_update_radiotap_he(rtwdev, skb, rx_status); + else if (rx_status->encoding == RX_ENC_EHT) rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status); - } } static void rtw89_core_validate_rx_signal(struct ieee80211_rx_status *rx_status) From 236b25d9308d345c8f05130da0bba1af0e7908a8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:53 +0800 Subject: [PATCH 057/285] wifi: rtw89: fill VHT radiotap Fill VHT radiotap by PHY status IE-09 which contains VHT SIG-A. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 53 +++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 4 +- drivers/net/wireless/realtek/rtw89/txrx.h | 26 +++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a27e482e38a6..2a9326335c53 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3176,6 +3176,50 @@ void rtw89_core_update_rx_status_by_ppdu(struct rtw89_dev *rtwdev, rx_status->enc_flags |= u8_encode_bits(1, RX_ENC_FLAG_STBC_MASK); } +static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + const struct rtw89_phy_sts_ie09 *ie09; + struct ieee80211_radiotap_vht *vht; + u8 group_id; + u32 sig_a1; + u16 paid; + u8 nss; + + if (!phy_ppdu) + return; + + ie09 = phy_ppdu->ie09; + if (!ie09) + return; + + vht = skb_push(skb, sizeof(*vht)); + memset(vht, 0, sizeof(*vht)); + rx_status->flag |= RX_FLAG_RADIOTAP_VHT; + + sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK); + + group_id = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID); + vht->group_id = group_id; + + if (group_id == 0 || group_id == 63) { + paid = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_PAID); + vht->partial_aid = cpu_to_le16(paid); + } else { + /* let mac80211 fill vht->mcs_nss[0] */ +#define WITH_MCS_IS_NOT_KNOWN(nss) ((nss) ? (15 << 4) | (nss) : 0) + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS1); + vht->mcs_nss[1] = WITH_MCS_IS_NOT_KNOWN(nss); + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS2); + vht->mcs_nss[2] = WITH_MCS_IS_NOT_KNOWN(nss); + nss = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS3); + vht->mcs_nss[3] = WITH_MCS_IS_NOT_KNOWN(nss); +#undef WITH_MCS_IS_NOT_KNOWN + } +} + static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status) @@ -3266,12 +3310,15 @@ static void rtw89_core_update_radiotap_eht(struct rtw89_dev *rtwdev, static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) { if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) return; - if (rx_status->encoding == RX_ENC_HE) + if (rx_status->encoding == RX_ENC_VHT) + rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu); + else if (rx_status->encoding == RX_ENC_HE) rtw89_core_update_radiotap_he(rtwdev, skb, rx_status); else if (rx_status->encoding == RX_ENC_EHT) rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status); @@ -3484,7 +3531,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_hw_to_sband_rate(rx_status); rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu); - rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status); + rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status, phy_ppdu); rtw89_core_validate_rx_signal(rx_status); rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status); rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ca716e95cb2c..d1cd5997cf97 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -58,6 +58,7 @@ extern const struct ieee80211_ops rtw89_ops; #define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR) #define DELTA_SWINGIDX_SIZE 30 +#define RTW89_RADIOTAP_ROOM_VHT sizeof(struct ieee80211_radiotap_vht) #define RTW89_RADIOTAP_ROOM_HE sizeof(struct ieee80211_radiotap_he) #define RTW89_RADIOTAP_ROOM_EHT \ (sizeof(struct ieee80211_radiotap_tlv) + \ @@ -65,7 +66,8 @@ extern const struct ieee80211_ops rtw89_ops; sizeof(struct ieee80211_radiotap_tlv) + \ ALIGN(sizeof(struct ieee80211_radiotap_eht_usig), 4)) #define RTW89_RADIOTAP_ROOM \ - ALIGN(max(RTW89_RADIOTAP_ROOM_HE, RTW89_RADIOTAP_ROOM_EHT), 64) + ALIGN(max3(RTW89_RADIOTAP_ROOM_VHT, RTW89_RADIOTAP_ROOM_HE, \ + RTW89_RADIOTAP_ROOM_EHT), 64) #define RTW89_HTC_MASK_VARIANT GENMASK(1, 0) #define RTW89_HTC_VARIANT_HE 3 diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 18fe6d3d0f83..ddf8742d6712 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -662,6 +662,32 @@ struct rtw89_phy_sts_ie09 { __le64 qw0; } __packed; +#define RTW89_PHY_STS_IE09_L_SIG_MASK GENMASK(21, 5) /* Legacy/VHT/HE L-SIG */ +#define RTW89_PHY_STS_IE09_L_SIG_RATE GENMASK(3, 0) +#define RTW89_PHY_STS_IE09_L_SIG_RSVD BIT(4) +#define RTW89_PHY_STS_IE09_L_SIG_LENGTH GENMASK(16, 5) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK GENMASK_ULL(45, 22) /* VHT SIG-A1 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_BW GENMASK(1, 0) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD1 BIT(2) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_STBC BIT(3) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID GENMASK(9, 4) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_NSTS GENMASK(12, 10) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_SU_PAID GENMASK(21, 13) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS0 GENMASK(12, 10) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS1 GENMASK(15, 13) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS2 GENMASK(18, 16) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MU_NSTS3 GENMASK(21, 19) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_TXOP_NOPS BIT(22) +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD2 BIT(23) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK GENMASK_ULL(55, 46) /* VHT SIG-A2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI BIT(0) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI_DISA BIT(1) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE0 BIT(2) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_LDPC BIT(3) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE3 GENMASK(7, 4) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8) +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9) + struct rtw89_phy_sts_ie10 { __le64 qw0; u8 sigb[]; From 16a1becdd1fb9a600b46691694298be16101821a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:54 +0800 Subject: [PATCH 058/285] wifi: rtw89: fill HE-SU/HE-TB/HE-MU/HE-EXT_SU radiotap Fill HE radiotap by PHY status IE-09/IE-10 which contains HE SIG-A/SIG-B respectively. The IE-10 may contain two content channels (if bandwidth is larger than 40MHz), and starting address of second content channel can be calculated by length of first content channel containing up to 15 user fields with 8-byte alignment. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 393 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/core.h | 18 +- drivers/net/wireless/realtek/rtw89/txrx.h | 54 +++ 3 files changed, 461 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 2a9326335c53..7fa479ce52e2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3220,9 +3220,361 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, } } +static void rtw89_core_update_radiotap_he_su(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + const struct rtw89_phy_sts_ie09 *ie09) +{ + u32 sig_a1, sig_a2; + u16 t; + + if (!ie09) + return; + + sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK); + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN | + 0); + + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | + 0); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + + rx_status->he_dcm = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_DCM); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_SR); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_BEAMFORMED); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_TXBF); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PREFEC); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PE); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER); +} + +static void rtw89_core_update_radiotap_he_tb(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + const struct rtw89_phy_sts_ie09 *sig) +{ + u32 sig_a1, sig_a2; + u16 t; + + if (!sig) + return; + + sig_a1 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK); + sig_a2 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK); + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN); + + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB2); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB3); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3); + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB4); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); +} + +static u8 rtw89_core_he_mu_get_n_user_fields_by_ru_alloc(struct rtw89_dev *rtwdev, + u8 ru_alloc) +{ + u8 mu_users; + + if (ru_alloc <= 15) { + static const u8 entries_0_15[16] = { + 9, 8, 8, 7, 8, 7, 7, 6, 8, 7, 7, 6, 7, 6, 6, 5, + }; + + return entries_0_15[ru_alloc]; + } + + if (ru_alloc >= 16 && ru_alloc <= 95) { + mu_users = ru_alloc & 0x07; + + switch (ru_alloc & 0xF8) { + case 16: case 24: return mu_users + 3; + case 32: case 64: return mu_users + 6; + case 40: case 48: case 72: case 80: return mu_users + 5; + case 56: case 88: return mu_users + 4; + } + } + + if (ru_alloc >= 96 && ru_alloc <= 111) { + mu_users = ru_alloc & 0x03; + mu_users += ru_alloc >> 2 & 0x03; + + return mu_users + 2; + } + + if (ru_alloc == 112) + return 4; + + if (ru_alloc >= 113 && ru_alloc <= 115) + return 0; + + if (ru_alloc >= 128 && ru_alloc <= 191) { + mu_users = ru_alloc & 0x07; + mu_users += ru_alloc >> 3 & 0x07; + + return mu_users + 3; + } + + if (ru_alloc >= 192 && ru_alloc <= 215) + return (ru_alloc & 0x07) + 1; + + return 0; +} + +static int rtw89_core_he_mu_get_n_ru_alloc(struct rtw89_dev *rtwdev, u8 bw) +{ + switch (bw) { + case 0: + case 1: + return 1; + case 2: + case 4: + case 5: + return 2; + case 3: + case 6: + case 7: + return 3; + } + + return 0; +} + +static void rtw89_core_he_mu_get_cc_ptr(struct rtw89_dev *rtwdev, + const struct rtw89_phy_sts_ie10 *ie10, + u8 bw, int n_ru, int n_center_26tone, + const u8 **c1, const u8 **c2) +{ + const u8 *sigb, *end_sigb; + int total_bits, c2_offset; + int n_user_fields = 0; + const u8 *ptr; + int i; + + sigb = &ie10->sigb[0]; + end_sigb = (const void *)ie10 + + rtw89_core_get_phy_status_ie_len(rtwdev, (const void *)ie10); + + ptr = sigb; + if (ptr + n_ru + n_center_26tone >= end_sigb) + return; + *c1 = ptr; + + if (bw == 0) /* 20MHz doesn't have content channel 2 */ + return; + + for (i = 0; i < n_ru; i++) + n_user_fields += + rtw89_core_he_mu_get_n_user_fields_by_ru_alloc(rtwdev, (*c1)[i]); + + /* hardware report max 15 recordes, align 8 */ + n_user_fields = min(n_user_fields, 15); + + total_bits = n_ru * 8 + n_center_26tone + 10 + + n_user_fields * 21 + DIV_ROUND_UP(n_user_fields, 2) * 10; + c2_offset = ALIGN(DIV_ROUND_UP(total_bits, 8), 8); + + ptr = &sigb[c2_offset]; + if (ptr + n_ru + n_center_26tone >= end_sigb) + return; + + *c2 = ptr; +} + +static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_he *he, + struct ieee80211_radiotap_he_mu *he_mu, + const struct rtw89_phy_sts_ie09 *ie09, + const struct rtw89_phy_sts_ie10 *ie10) +{ + const u8 *c1 = NULL, *c2 = NULL; + int n_center_26tone, n_ru, i; + bool doppler, comp; + u32 sig_a1, sig_a2; + u16 t; + u8 bw; + + if (!ie09) + return; + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN); + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN); + + sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BSS_COLOR); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SR); + he->data4 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE); + + doppler = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER); + he->data6 |= le16_encode_bits(doppler, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_TXOP); + + if (doppler) { + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MID); + he->data6 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY); + } + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LPDC_XSYMSEG); + he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_FEC); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD); + + t = u32_get_bits(sig_a2, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_PE); + he->data5 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG); + + he_mu->flags1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN); + he_mu->flags2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS); + he_mu->flags1 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM); + he_mu->flags1 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM); + + t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_SYM_USR); + he_mu->flags2 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS); + + comp = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_COM); + he_mu->flags2 |= le16_encode_bits(comp, IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP); + + bw = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BW); + he_mu->flags2 |= le16_encode_bits(bw, IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); + + if (comp == 0 && bw <= 5) { + u8 punc = clamp_t(int, bw - 3, 0, 2); + + he_mu->flags2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN); + he_mu->flags2 |= + le16_encode_bits(punc, IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW); + } + + /* the Common field in the HE-SIG-B field is not present */ + if (comp || !ie10) + return; + + n_ru = rtw89_core_he_mu_get_n_ru_alloc(rtwdev, bw); + n_center_26tone = bw >= 1; + + rtw89_core_he_mu_get_cc_ptr(rtwdev, ie10, bw, n_ru, n_center_26tone, &c1, &c2); + if (!c1) + return; + + he_mu->flags1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN); + + for (i = 0; i < n_ru; i++) { + he_mu->ru_ch1[i] = c1[i]; + if (c2) + he_mu->ru_ch2[i] = c2[i]; + } + + if (n_center_26tone) { + u8 ru; + + ru = c1[n_ru] & BIT(0); + he_mu->flags1 |= + le16_encode_bits(ru, IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU); + + ru = c2 ? c2[n_ru] & BIT(0) : 0; + he_mu->flags1 |= + le16_encode_bits(ru, IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU); + } +} + +static u16 rtw89_core_get_radiotap_he_format(struct rtw89_rx_desc_info *desc_info) +{ + switch (desc_info->ppdu_type) { + case RTW89_RX_PPDU_T_HE_MU: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU; + case RTW89_RX_PPDU_T_HE_ERSU: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU; + case RTW89_RX_PPDU_T_HE_TB: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG; + default: + return IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU; + } +} + static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, + struct rtw89_rx_desc_info *desc_info, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) + struct ieee80211_rx_status *rx_status, + struct rtw89_rx_phy_ppdu *phy_ppdu) { static const struct ieee80211_radiotap_he known_he = { .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | @@ -3231,11 +3583,45 @@ static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), }; + struct ieee80211_radiotap_he_mu *he_mu = NULL; + const struct rtw89_phy_sts_ie09 *ie09; + const struct rtw89_phy_sts_ie10 *ie10; struct ieee80211_radiotap_he *he; + u16 he_format; + + he_format = rtw89_core_get_radiotap_he_format(desc_info); + + /* Radiotap of HE-MU must be placed after HE (skb_push ahead) */ + if (he_format == IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU) { + rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU; + he_mu = skb_push(skb, sizeof(*he_mu)); + memset(he_mu, 0, sizeof(*he_mu)); + } rx_status->flag |= RX_FLAG_RADIOTAP_HE; he = skb_push(skb, sizeof(*he)); *he = known_he; + + he->data1 |= le16_encode_bits(he_format, IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK); + + if (!phy_ppdu) + return; + + ie09 = phy_ppdu->ie09; + ie10 = phy_ppdu->ie10; + + switch (he_format) { + case IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU: + rtw89_core_update_radiotap_he_mu(rtwdev, skb, rx_status, he, he_mu, + ie09, ie10); + break; + case IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG: + rtw89_core_update_radiotap_he_tb(rtwdev, skb, rx_status, he, ie09); + break; + default: + rtw89_core_update_radiotap_he_su(rtwdev, skb, rx_status, he, ie09); + break; + } } static const u8 rx_status_bw_to_radiotap_eht_usig[] = { @@ -3309,6 +3695,7 @@ static void rtw89_core_update_radiotap_eht(struct rtw89_dev *rtwdev, } static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, + struct rtw89_rx_desc_info *desc_info, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct rtw89_rx_phy_ppdu *phy_ppdu) @@ -3319,7 +3706,7 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, if (rx_status->encoding == RX_ENC_VHT) rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu); else if (rx_status->encoding == RX_ENC_HE) - rtw89_core_update_radiotap_he(rtwdev, skb, rx_status); + rtw89_core_update_radiotap_he(rtwdev, desc_info, skb, rx_status, phy_ppdu); else if (rx_status->encoding == RX_ENC_EHT) rtw89_core_update_radiotap_eht(rtwdev, skb, rx_status); } @@ -3531,7 +3918,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_hw_to_sband_rate(rx_status); rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu); - rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status, phy_ppdu); + rtw89_core_update_radiotap(rtwdev, desc_info, skb_ppdu, rx_status, phy_ppdu); rtw89_core_validate_rx_signal(rx_status); rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status); rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d1cd5997cf97..b85d7fa4296a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -59,7 +59,8 @@ extern const struct ieee80211_ops rtw89_ops; #define DELTA_SWINGIDX_SIZE 30 #define RTW89_RADIOTAP_ROOM_VHT sizeof(struct ieee80211_radiotap_vht) -#define RTW89_RADIOTAP_ROOM_HE sizeof(struct ieee80211_radiotap_he) +#define RTW89_RADIOTAP_ROOM_HE (sizeof(struct ieee80211_radiotap_he) + \ + sizeof(struct ieee80211_radiotap_he_mu)) #define RTW89_RADIOTAP_ROOM_EHT \ (sizeof(struct ieee80211_radiotap_tlv) + \ ALIGN(struct_size((struct ieee80211_radiotap_eht *)0, user_info, 1), 4) + \ @@ -955,6 +956,21 @@ enum rtw89_bandwidth { RTW89_CHANNEL_WIDTH_10 = 7, }; +enum rtw89_rx_ppdu_type { + RTW89_RX_PPDU_T_LCCK = 0, + RTW89_RX_PPDU_T_SCCK = 1, + RTW89_RX_PPDU_T_OFDM = 2, + RTW89_RX_PPDU_T_HT = 3, + RTW89_RX_PPDU_T_HTGF = 4, + RTW89_RX_PPDU_T_VHT_SU = 5, + RTW89_RX_PPDU_T_VHT_MU = 6, + RTW89_RX_PPDU_T_HE_SU = 7, + RTW89_RX_PPDU_T_HE_ERSU = 8, + RTW89_RX_PPDU_T_HE_MU = 9, + RTW89_RX_PPDU_T_HE_TB = 10, + RTW89_RX_PPDU_T_UNKNOWN = 15, +}; + enum rtw89_ps_mode { RTW89_PS_MODE_NONE = 0, RTW89_PS_MODE_RFOFF = 1, diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index ddf8742d6712..4a567d3fe3f7 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -687,6 +687,60 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE3 GENMASK(7, 4) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE SU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_FORMAT BIT(0) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE BIT(1) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL BIT(2) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MCS GENMASK(6, 3) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_DCM BIT(7) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BSS_COLOR GENMASK(13, 8) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_RSVD1 BIT(14) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_SR GENMASK(18, 15) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BW GENMASK(20, 19) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_GILTF GENMASK(22, 21) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_NSTS GENMASK(25, 23) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE SU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_CODING BIT(7) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG BIT(8) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_STBC BIT(9) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_BEAMFORMED BIT(10) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PREFEC GENMASK(12, 11) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_PE BIT(13) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_RSVD2 BIT(14) +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER BIT(15) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE TB SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_FORMAT BIT(0) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR GENMASK(6, 1) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1 GENMASK(10, 7) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB2 GENMASK(14, 11) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB3 GENMASK(18, 15) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB4 GENMASK(22, 19) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_RSVD1 BIT(23) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BW GENMASK(25, 24) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE TB SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_RSVD1 GENMASK(15, 7) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE MU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL BIT(0) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS GENMASK(3, 1) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM BIT(4) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BSS_COLOR GENMASK(10, 5) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SR GENMASK(14, 11) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_BW GENMASK(17, 15) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_SYM_USR GENMASK(21, 18) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_COM BIT(22) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_GI_LTF GENMASK(24, 23) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER BIT(25) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE MU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP GENMASK(6, 0) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_RSVD BIT(7) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LTF_MID GENMASK(10, 8) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MID BIT(10) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LPDC_XSYMSEG BIT(11) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_STBC BIT(12) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_FEC GENMASK(14, 13) +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_PE BIT(15) struct rtw89_phy_sts_ie10 { __le64 qw0; From 41d78f3f35961ca533cff5bbc159e7fb3c32e3df Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:55 +0800 Subject: [PATCH 059/285] wifi: rtw89: debug: make implementation of beacon_info entry in order When adding more debugfs entries, the beacon_info entry becomes not in order. Move to correct location. Don't change logic at all. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 142 ++++++++++----------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 597052261974..6fe8e7254e80 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4919,6 +4919,77 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev, return count; } +static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, + char *buf, size_t bufsz) +{ + struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); + p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr); + p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi)); + p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); + p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; + struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; + struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; + u16 upper, lower = bcn_stat->tbtt_tu_min; + char *p = buf, *end = buf + bufsz; + u16 *drift = bcn_stat->drift; + u8 bcn_num = bcn_stat->num; + struct rtw89_bb_ctx *bb; + u8 count; + u8 i; + + rtw89_for_each_active_bb(rtwdev, bb) + p += rtw89_get_beacon_info(rtwdev, bb, p, end - p); + + p += scnprintf(p, end - p, "[Beacon info]\n"); + p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int); + p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim); + + p += scnprintf(p, end - p, "\n[Distribution]\n"); + p += scnprintf(p, end - p, "tbtt\n"); + for (i = 0; i < RTW89_BCN_TRACK_MAX_BIN_NUM; i++) { + upper = lower + RTW89_BCN_TRACK_BIN_WIDTH - 1; + if (i == RTW89_BCN_TRACK_MAX_BIN_NUM - 1) + upper = max(upper, bcn_stat->tbtt_tu_max); + + p += scnprintf(p, end - p, "%02u - %02u: %u\n", + lower, upper, bcn_dist->bins[i]); + + lower = upper + 1; + } + + p += scnprintf(p, end - p, "\ndrift\n"); + + for (i = 0; i < bcn_num; i += count) { + count = 1; + while (i + count < bcn_num && drift[i] == drift[i + count]) + count++; + + p += scnprintf(p, end - p, "%u: %u\n", drift[i], count); + } + p += scnprintf(p, end - p, "\nlower bound: %u\n", bcn_dist->lower_bound); + p += scnprintf(p, end - p, "upper bound: %u\n", bcn_dist->upper_bound); + p += scnprintf(p, end - p, "outlier count: %u\n", bcn_dist->outlier_count); + + p += scnprintf(p, end - p, "\n[Tracking]\n"); + p += scnprintf(p, end - p, "tbtt offset: %u\n", bcn_track->tbtt_offset); + p += scnprintf(p, end - p, "bcn timeout: %u\n", bcn_track->bcn_timeout); + + return p - buf; +} + enum __diag_mac_cmd { __CMD_EQUALV, __CMD_EQUALO, @@ -5255,77 +5326,6 @@ rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev, return p - buf; } -static int rtw89_get_beacon_info(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, - char *buf, size_t bufsz) -{ - struct rtw89_pkt_stat *pkt_stat = &bb->last_pkt_stat; - char *p = buf, *end = buf + bufsz; - - p += scnprintf(p, end - p, "[PHY %u]\n", bb->phy_idx); - p += scnprintf(p, end - p, "Beacon: %u\n", pkt_stat->beacon_nr); - p += scnprintf(p, end - p, "raw rssi: %lu\n", ewma_rssi_read(&bb->bcn_rssi)); - p += scnprintf(p, end - p, "hw rate: %u\n", pkt_stat->beacon_rate); - p += scnprintf(p, end - p, "length: %u\n\n", pkt_stat->beacon_len); - - return p - buf; -} - -static ssize_t -rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, - struct rtw89_debugfs_priv *debugfs_priv, - char *buf, size_t bufsz) -{ - struct rtw89_beacon_track_info *bcn_track = &rtwdev->bcn_track; - struct rtw89_beacon_stat *bcn_stat = &rtwdev->phystat.bcn_stat; - struct rtw89_beacon_dist *bcn_dist = &bcn_stat->bcn_dist; - u16 upper, lower = bcn_stat->tbtt_tu_min; - char *p = buf, *end = buf + bufsz; - u16 *drift = bcn_stat->drift; - u8 bcn_num = bcn_stat->num; - struct rtw89_bb_ctx *bb; - u8 count; - u8 i; - - rtw89_for_each_active_bb(rtwdev, bb) - p += rtw89_get_beacon_info(rtwdev, bb, p, end - p); - - p += scnprintf(p, end - p, "[Beacon info]\n"); - p += scnprintf(p, end - p, "interval: %u\n", bcn_track->beacon_int); - p += scnprintf(p, end - p, "dtim: %u\n", bcn_track->dtim); - - p += scnprintf(p, end - p, "\n[Distribution]\n"); - p += scnprintf(p, end - p, "tbtt\n"); - for (i = 0; i < RTW89_BCN_TRACK_MAX_BIN_NUM; i++) { - upper = lower + RTW89_BCN_TRACK_BIN_WIDTH - 1; - if (i == RTW89_BCN_TRACK_MAX_BIN_NUM - 1) - upper = max(upper, bcn_stat->tbtt_tu_max); - - p += scnprintf(p, end - p, "%02u - %02u: %u\n", - lower, upper, bcn_dist->bins[i]); - - lower = upper + 1; - } - - p += scnprintf(p, end - p, "\ndrift\n"); - - for (i = 0; i < bcn_num; i += count) { - count = 1; - while (i + count < bcn_num && drift[i] == drift[i + count]) - count++; - - p += scnprintf(p, end - p, "%u: %u\n", drift[i], count); - } - p += scnprintf(p, end - p, "\nlower bound: %u\n", bcn_dist->lower_bound); - p += scnprintf(p, end - p, "upper bound: %u\n", bcn_dist->upper_bound); - p += scnprintf(p, end - p, "outlier count: %u\n", bcn_dist->outlier_count); - - p += scnprintf(p, end - p, "\n[Tracking]\n"); - p += scnprintf(p, end - p, "tbtt offset: %u\n", bcn_track->tbtt_offset); - p += scnprintf(p, end - p, "bcn timeout: %u\n", bcn_track->bcn_timeout); - - return p - buf; -} - #define rtw89_debug_priv_get(name, opts...) \ { \ .cb_read = rtw89_debug_priv_ ##name## _get, \ From cd92d278c1864852cb4840c5e51c1cd0eaa60e85 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:56 +0800 Subject: [PATCH 060/285] wifi: rtw89: add debugfs entry of monitor mode options to capture HE-MU packets To capture HE-MU packets, set BSS color and AID for specific connected station. The writing format: For example, $ echo 0x4 0x16 > monitor_opts Read this entry to get current setting: bss_color=0x4 aid=0x16 By the way, add another sec2() function to create debugfs entries to prevent running smatch timeout. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 57 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.c | 23 ++++++--- drivers/net/wireless/realtek/rtw89/phy.h | 2 + 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 6fe8e7254e80..8ee800c76cfe 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -93,6 +93,7 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv beacon_info; struct rtw89_debugfs_priv diag_mac; struct rtw89_debugfs_priv diag_bb; + struct rtw89_debugfs_priv monitor_opts; }; struct rtw89_debugfs_iter_data { @@ -5326,6 +5327,54 @@ rtw89_debug_priv_diag_bb_get(struct rtw89_dev *rtwdev, return p - buf; } +static ssize_t +rtw89_debug_priv_monitor_opts_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; + u32 bss_color; + u32 aid; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + rtw89_leave_ps_mode(rtwdev); + + bss_color = rtw89_phy_read32_idx(rtwdev, chip->bss_clr_map_reg, + B_BSS_CLR_MAP_TGT, RTW89_PHY_0); + aid = rtw89_phy_read32_idx(rtwdev, chip->bss_clr_map_reg, + B_BSS_CLR_MAP_STAID, RTW89_PHY_0); + + p += scnprintf(p, end - p, "bss_color=0x%x aid=0x%x\n", bss_color, aid); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_monitor_opts_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + u32 bss_color; + u32 aid; + int num; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + num = sscanf(buf, "%x %x", &bss_color, &aid); + if (num != 2) { + rtw89_info(rtwdev, "valid format: \n"); + return -EINVAL; + } + + rtw89_leave_ps_mode(rtwdev); + + __rtw89_phy_set_bss_color(rtwdev, bss_color, aid, RTW89_PHY_0); + + return count; +} + #define rtw89_debug_priv_get(name, opts...) \ { \ .cb_read = rtw89_debug_priv_ ##name## _get, \ @@ -5390,6 +5439,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = { .beacon_info = rtw89_debug_priv_get(beacon_info), .diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK), .diag_bb = rtw89_debug_priv_get(diag_bb, RSIZE_8K, RLOCK), + .monitor_opts = rtw89_debug_priv_set_and_get(monitor_opts, RWLOCK), }; #define rtw89_debugfs_add(name, mode, fopname, parent) \ @@ -5435,12 +5485,18 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_r(phy_info); rtw89_debugfs_add_rw(bb_info); rtw89_debugfs_add_r(stations); +} + +static +void rtw89_debugfs_add_sec2(struct rtw89_dev *rtwdev, struct dentry *debugfs_topdir) +{ rtw89_debugfs_add_rw(disable_dm); rtw89_debugfs_add_rw(static_pd_th); rtw89_debugfs_add_rw(mlo_mode); rtw89_debugfs_add_r(beacon_info); rtw89_debugfs_add_r(diag_mac); rtw89_debugfs_add_r(diag_bb); + rtw89_debugfs_add_rw(monitor_opts); } void rtw89_debugfs_init(struct rtw89_dev *rtwdev) @@ -5457,6 +5513,7 @@ void rtw89_debugfs_init(struct rtw89_dev *rtwdev) rtw89_debugfs_add_sec0(rtwdev, debugfs_topdir); rtw89_debugfs_add_sec1(rtwdev, debugfs_topdir); + rtw89_debugfs_add_sec2(rtwdev, debugfs_topdir); } void rtw89_debugfs_deinit(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 15483a86951d..759be4dab42b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -8231,12 +8231,24 @@ void rtw89_phy_dm_init_data(struct rtw89_dev *rtwdev) __rtw89_phy_dm_init_data(rtwdev, bb); } +void __rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, u8 bss_color, u16 aid, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_reg_def *bss_clr_vld = &chip->bss_clr_vld; + + rtw89_phy_write32_idx(rtwdev, bss_clr_vld->addr, bss_clr_vld->mask, 0x1, + phy_idx); + rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_TGT, + bss_color, phy_idx); + rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_STAID, + aid, phy_idx); +} + void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); - const struct rtw89_chip_info *chip = rtwdev->chip; - const struct rtw89_reg_def *bss_clr_vld = &chip->bss_clr_vld; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; struct ieee80211_bss_conf *bss_conf; u8 bss_color; @@ -8253,12 +8265,7 @@ void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, rcu_read_unlock(); - rtw89_phy_write32_idx(rtwdev, bss_clr_vld->addr, bss_clr_vld->mask, 0x1, - phy_idx); - rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_TGT, - bss_color, phy_idx); - rtw89_phy_write32_idx(rtwdev, chip->bss_clr_map_reg, B_BSS_CLR_MAP_STAID, - vif->cfg.aid, phy_idx); + __rtw89_phy_set_bss_color(rtwdev, bss_color, vif->cfg.aid, phy_idx); } static bool rfk_chan_validate_desc(const struct rtw89_rfk_chan_desc *desc) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 74fbf5baff58..b4b818f786df 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -1128,6 +1128,8 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev); void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); +void __rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, u8 bss_color, u16 aid, + enum rtw89_phy_idx phy_idx); void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, enum rtw89_tssi_bandedge_cfg bandedge_cfg); From 884495c39de1a02f42bd40051b921e2311d6ac91 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:57 +0800 Subject: [PATCH 061/285] wifi: rtw89: phy: check length before parsing PHY status IE Hardware might report PHY status IE with unexpected length, and parser might access out of range. Check the length ahead. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7fa479ce52e2..7e1182a49942 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2276,7 +2276,6 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr = pos; ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, iehdr); - rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu); pos += ie_len; if (pos > end || ie_len == 0) { /* clear pointers to prevent accessing out of IE */ @@ -2285,8 +2284,11 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_TXRX, "phy status parse failed\n"); + return -EINVAL; } + + rtw89_core_process_phy_status_ie(rtwdev, iehdr, phy_ppdu); } rtw89_chip_convert_rpl_to_rssi(rtwdev, phy_ppdu); From 48402f736f93e6377ee47984d9dd2aa1b6815604 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:58 +0800 Subject: [PATCH 062/285] wifi: rtw89: phy: skip trailing 8-byte zeros of PHY status IE for RTL8922D Hardware reports a list of PHY status IE. In monitor mode, IE-09 of PHY status is enabled, and the report contains trailing 8-byte zeros, causing failed to parse and drop all IE information. The 8 zeros are recognize as IE type 0, but length of type 0 must be not 8 (reference to rtw89_phy_gen_def::physt_ie_len[0]). Check and skip them. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7e1182a49942..fddb0c822c28 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2275,6 +2275,13 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev, while (pos < end) { const struct rtw89_phy_sts_iehdr *iehdr = pos; + /* + * RTL8922D might reports 8 bytes zeros at end if IE09 presents. + * Check and ignore the zeros. + */ + if (unlikely(phy_ppdu->ie09 && end - pos == 8 && iehdr->w0 == 0)) + break; + ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, iehdr); pos += ie_len; if (pos > end || ie_len == 0) { From 0805ddc2fdb64c142eb2be8429497dfd8c9bf296 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:09:59 +0800 Subject: [PATCH 063/285] wifi: rtw89: phy: support PHY status IE-09 GEN2 for RTL8922D The format of PHY status IE-10 for RTL8922D is different from earlier chips. Fortunately only starting bit is different, but the layout is the same. Get the VHT/HE SIG-A value by corresponding mask accordingly. The IE-09 format of generation 0 and 1 are totally the same. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 72 +++++++++++++++++++---- drivers/net/wireless/realtek/rtw89/core.h | 4 +- drivers/net/wireless/realtek/rtw89/txrx.h | 23 +++++++- 3 files changed, 82 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index fddb0c822c28..c21737981b61 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2156,7 +2156,19 @@ static void rtw89_core_parse_phy_status_ie09(struct rtw89_dev *rtwdev, const struct rtw89_phy_sts_iehdr *iehdr, struct rtw89_rx_phy_ppdu *phy_ppdu) { - phy_ppdu->ie09 = (const void *)iehdr; + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const union rtw89_phy_sts_ie09 *ie09; + u16 ie_len; + + ie09 = (const void *)iehdr; + + if (phy->physt_gen >= 2) { + ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, (const void *)iehdr); + if (ie_len < sizeof(ie09->gen2)) + return; + } + + phy_ppdu->ie09 = ie09; } static void rtw89_core_parse_phy_status_ie10(struct rtw89_dev *rtwdev, @@ -3190,7 +3202,8 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, struct ieee80211_rx_status *rx_status, struct rtw89_rx_phy_ppdu *phy_ppdu) { - const struct rtw89_phy_sts_ie09 *ie09; + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; + const union rtw89_phy_sts_ie09 *ie09; struct ieee80211_radiotap_vht *vht; u8 group_id; u32 sig_a1; @@ -3208,7 +3221,10 @@ static void rtw89_core_update_radiotap_vht(struct rtw89_dev *rtwdev, memset(vht, 0, sizeof(*vht)); rx_status->flag |= RX_FLAG_RADIOTAP_VHT; - sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK); + if (phy->physt_gen >= 2) + sig_a1 = le64_get_bits(ie09->gen2.qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK_GEN2); + else + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK); group_id = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_VHT_SIG_A1_GRP_ID); vht->group_id = group_id; @@ -3233,16 +3249,26 @@ static void rtw89_core_update_radiotap_he_su(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct ieee80211_radiotap_he *he, - const struct rtw89_phy_sts_ie09 *ie09) + const union rtw89_phy_sts_ie09 *ie09) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; u32 sig_a1, sig_a2; u16 t; if (!ie09) return; - sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK); - sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK); + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(ie09->gen2.qw1, + RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_H6) << 6; + } else { + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK); + } he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN | IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN | @@ -3295,16 +3321,26 @@ static void rtw89_core_update_radiotap_he_tb(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct ieee80211_radiotap_he *he, - const struct rtw89_phy_sts_ie09 *sig) + const union rtw89_phy_sts_ie09 *sig) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; u32 sig_a1, sig_a2; u16 t; if (!sig) return; - sig_a1 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK); - sig_a2 = le64_get_bits(sig->qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK); + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(sig->gen2.qw0, + RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(sig->gen2.qw0, + RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(sig->gen2.qw1, + RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_H6); + } else { + sig_a1 = le64_get_bits(sig->gen0.qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK); + sig_a2 = le64_get_bits(sig->gen0.qw0, RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK); + } he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN | IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | @@ -3445,9 +3481,10 @@ static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev, struct ieee80211_rx_status *rx_status, struct ieee80211_radiotap_he *he, struct ieee80211_radiotap_he_mu *he_mu, - const struct rtw89_phy_sts_ie09 *ie09, + const union rtw89_phy_sts_ie09 *ie09, const struct rtw89_phy_sts_ie10 *ie10) { + const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; const u8 *c1 = NULL, *c2 = NULL; int n_center_26tone, n_ru, i; bool doppler, comp; @@ -3468,8 +3505,17 @@ static void rtw89_core_update_radiotap_he_mu(struct rtw89_dev *rtwdev, IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN); - sig_a1 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK); - sig_a2 = le64_get_bits(ie09->qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK); + if (phy->physt_gen >= 2) { + sig_a1 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK_GEN2); + sig_a2 = le64_get_bits(ie09->gen2.qw0, + RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_L0); + sig_a2 |= le64_get_bits(ie09->gen2.qw1, + RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_H6) << 6; + } else { + sig_a1 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK); + sig_a2 = le64_get_bits(ie09->gen0.qw0, RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK); + } t = u32_get_bits(sig_a1, RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL); he->data3 |= le16_encode_bits(t, IEEE80211_RADIOTAP_HE_DATA3_UL_DL); @@ -3593,7 +3639,7 @@ static void rtw89_core_update_radiotap_he(struct rtw89_dev *rtwdev, .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), }; struct ieee80211_radiotap_he_mu *he_mu = NULL; - const struct rtw89_phy_sts_ie09 *ie09; + const union rtw89_phy_sts_ie09 *ie09; const struct rtw89_phy_sts_ie10 *ie10; struct ieee80211_radiotap_he *he; u16 he_format; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b85d7fa4296a..a6dee97d99f2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -24,7 +24,7 @@ struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; struct rtw89_phy_rfk_log_fmt; struct rtw89_phy_calc_efuse_gain; -struct rtw89_phy_sts_ie09; +union rtw89_phy_sts_ie09; struct rtw89_phy_sts_ie10; struct rtw89_debugfs; struct rtw89_regd_data; @@ -867,7 +867,7 @@ struct rtw89_rx_phy_ppdu { bool to_self; bool valid; bool hdr_2_en; - const struct rtw89_phy_sts_ie09 *ie09; /* SIG-A */ + const union rtw89_phy_sts_ie09 *ie09; /* SIG-A */ const struct rtw89_phy_sts_ie10 *ie10; /* SIG-B */ }; diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 4a567d3fe3f7..4e7109fbe7c9 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -658,15 +658,23 @@ struct rtw89_phy_sts_ie01_v2 { #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_C GENMASK(11, 4) #define RTW89_PHY_STS_IE01_V2_W9_RPL_FD_D GENMASK(23, 16) -struct rtw89_phy_sts_ie09 { - __le64 qw0; +union rtw89_phy_sts_ie09 { + struct { + __le64 qw0; + } gen0; + struct { + __le64 qw0; + __le64 qw1; + } gen2; } __packed; #define RTW89_PHY_STS_IE09_L_SIG_MASK GENMASK(21, 5) /* Legacy/VHT/HE L-SIG */ +#define RTW89_PHY_STS_IE09_L_SIG_MASK_GEN2 GENMASK(31, 15) /* Legacy/VHT/HE L-SIG for GEN2 */ #define RTW89_PHY_STS_IE09_L_SIG_RATE GENMASK(3, 0) #define RTW89_PHY_STS_IE09_L_SIG_RSVD BIT(4) #define RTW89_PHY_STS_IE09_L_SIG_LENGTH GENMASK(16, 5) #define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK GENMASK_ULL(45, 22) /* VHT SIG-A1 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A1_MASK_GEN2 GENMASK_ULL(55, 32) /* VHT SIG-A1 for GEN2 */ #define RTW89_PHY_STS_IE09_VHT_SIG_A1_BW GENMASK(1, 0) #define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD1 BIT(2) #define RTW89_PHY_STS_IE09_VHT_SIG_A1_STBC BIT(3) @@ -680,6 +688,8 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_VHT_SIG_A1_TXOP_NOPS BIT(22) #define RTW89_PHY_STS_IE09_VHT_SIG_A1_RSVD2 BIT(23) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK GENMASK_ULL(55, 46) /* VHT SIG-A2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 56) /* VHT SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_VHT_SIG_A2_MASK_GEN2_H8 GENMASK_ULL(1, 0) /* VHT SIG-A2 for GEN2 */ #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI BIT(0) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SGI_DISA BIT(1) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_SUMU_CODE0 BIT(2) @@ -688,6 +698,7 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_VHT_SIG_A2_BF BIT(8) #define RTW89_PHY_STS_IE09_VHT_SIG_A2_RSVD3 BIT(9) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE SU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE SU SIG-A1 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_FORMAT BIT(0) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_BEAM_CHANGE BIT(1) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_ULDL BIT(2) @@ -700,6 +711,8 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_GILTF GENMASK(22, 21) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A1_NSTS GENMASK(25, 23) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE SU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE SU SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE SU SIG-A2 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_TXOP GENMASK(6, 0) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_CODING BIT(7) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_LDPC_XSYMSEG BIT(8) @@ -710,6 +723,7 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_RSVD2 BIT(14) #define RTW89_PHY_STS_IE09_HE_SU_SIG_A2_DOPPLER BIT(15) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE TB SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE TB SIG-A1 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_FORMAT BIT(0) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BSS_COLOR GENMASK(6, 1) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_SB1 GENMASK(10, 7) @@ -719,9 +733,12 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_RSVD1 BIT(23) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A1_BW GENMASK(25, 24) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE TB SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE TB SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE TB SIG-A2 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_TXOP GENMASK(6, 0) #define RTW89_PHY_STS_IE09_HE_TB_SIG_A2_RSVD1 GENMASK(15, 7) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK GENMASK_ULL(47, 22) /* HE MU SIG-A1 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_MASK_GEN2 GENMASK_ULL(57, 32) /* HE MU SIG-A1 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_ULDL BIT(0) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_MCS GENMASK(3, 1) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_SIGB_DCM BIT(4) @@ -733,6 +750,8 @@ struct rtw89_phy_sts_ie09 { #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_GI_LTF GENMASK(24, 23) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A1_DOPPLER BIT(25) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK GENMASK_ULL(63, 48) /* HE MU SIG-A2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_L0 GENMASK_ULL(63, 58) /* HE MU SIG-A2 for GEN2 */ +#define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_MASK_GEN2_H6 GENMASK_ULL(9, 0) /* HE MU SIG-A2 for GEN2 */ #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_TXOP GENMASK(6, 0) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_RSVD BIT(7) #define RTW89_PHY_STS_IE09_HE_MU_SIG_A2_LTF_MID GENMASK(10, 8) From f77199a0cf6660d45d72010e76c3ab4788e9da5f Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 6 May 2026 21:10:00 +0800 Subject: [PATCH 064/285] wifi: rtw89: check skb headroom before adding radiotap The radiotap headroom is allocated only if IEEE80211_CONF_MONITOR is set. However, it is potentially racing that SKB allocation without radiotap headroom but adding radiotap from matched PPDU status of another SKB. Add a check to avoid the case. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260506131000.1706298-15-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c21737981b61..432d46dfd26a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3758,6 +3758,13 @@ static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) return; + /* + * At transient adding a monitor vif from a station vif, the headroom + * might not include radiotap. + */ + if (unlikely(skb_headroom(skb) < RTW89_RADIOTAP_ROOM + NET_SKB_PAD)) + return; + if (rx_status->encoding == RX_ENC_VHT) rtw89_core_update_radiotap_vht(rtwdev, skb, rx_status, phy_ppdu); else if (rx_status->encoding == RX_ENC_HE) From 4c13e00cc6798f8e63354982ec172b7829ae825a Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 7 May 2026 10:22:12 +0200 Subject: [PATCH 065/285] wifi: rtw88: remove rtw_txq_dequeue Remove the rtw_txq_dequeue helper. It's a wrapper around ieee80211_tx_dequeue with just one caller. Call ieee80211_tx_dequeue directly in rtw_txq_push. There's no need to fetch txq for every frame, we can do this once outside of the rcu lock. Make the loop variable i unsigned long, it should have the same type as frames. Signed-off-by: Martin Kaiser Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260507082238.889656-1-martin@kaiser.cx --- drivers/net/wireless/realtek/rtw88/tx.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 3106edb84fb4..bea78971141e 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -619,31 +619,19 @@ static int rtw_txq_push_skb(struct rtw_dev *rtwdev, return 0; } -static struct sk_buff *rtw_txq_dequeue(struct rtw_dev *rtwdev, - struct rtw_txq *rtwtxq) -{ - struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); - struct sk_buff *skb; - - skb = ieee80211_tx_dequeue(rtwdev->hw, txq); - if (!skb) - return NULL; - - return skb; -} - static void rtw_txq_push(struct rtw_dev *rtwdev, struct rtw_txq *rtwtxq, unsigned long frames) { + struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); struct sk_buff *skb; + unsigned long i; int ret; - int i; rcu_read_lock(); for (i = 0; i < frames; i++) { - skb = rtw_txq_dequeue(rtwdev, rtwtxq); + skb = ieee80211_tx_dequeue(rtwdev->hw, txq); if (!skb) break; From 695e7f2e79010ca469d01098889c2f57340c7df1 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:38 +0800 Subject: [PATCH 066/285] wifi: rtw89: phy: define BB wrap data for RTL8922D variants The BB wrap is a hardware block to control TX power. Since RTL8922D has many variants with different CID and RFE types, prepare flow and dummy struct adopt to configuration functions for coming patches. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 4 ++ drivers/net/wireless/realtek/rtw89/core.h | 13 +++++++ drivers/net/wireless/realtek/rtw89/phy.h | 3 ++ 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 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 38 +++++++++++++++++++ 10 files changed, 64 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 432d46dfd26a..7b26aedc0883 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7312,6 +7312,10 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev) if (ret) goto out; + ret = rtw89_chip_data_setup(rtwdev); + if (ret) + goto out; + rtw89_core_setup_rfe_parms(rtwdev); rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index a6dee97d99f2..75237fb23a99 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -29,6 +29,7 @@ struct rtw89_phy_sts_ie10; struct rtw89_debugfs; struct rtw89_regd_data; struct rtw89_wow_cam_info; +struct rtw89_bb_wrap_data; struct rtw89_fw_cmd_ofld_info; extern const struct ieee80211_ops rtw89_ops; @@ -3851,6 +3852,7 @@ struct rtw89_chip_ops { enum rtw89_efuse_block block); int (*read_phycap)(struct rtw89_dev *rtwdev, u8 *phycap_map); void (*fem_setup)(struct rtw89_dev *rtwdev); + int (*data_setup)(struct rtw89_dev *rtwdev); void (*rfe_gpio)(struct rtw89_dev *rtwdev); void (*rfk_hw_init)(struct rtw89_dev *rtwdev); void (*rfk_init)(struct rtw89_dev *rtwdev); @@ -4597,6 +4599,7 @@ struct rtw89_bb_stat_cfg { }; struct rtw89_phy_info { + const struct rtw89_bb_wrap_data *bb_wrap_data; struct rtw89_bb_stat_cfg bb_stat_cfg; }; @@ -7484,6 +7487,16 @@ static inline void rtw89_chip_fem_setup(struct rtw89_dev *rtwdev) chip->ops->fem_setup(rtwdev); } +static inline int rtw89_chip_data_setup(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (!chip->ops->data_setup) + return 0; + + return chip->ops->data_setup(rtwdev); +} + static inline void rtw89_chip_rfe_gpio(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index b4b818f786df..eabaf46f4f4b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -572,6 +572,9 @@ struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; +struct rtw89_bb_wrap_data { +}; + struct rtw89_phy_gen_def { u32 cr_base; u32 physt_bmp_start; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index e047e716d9e3..2fb54e1fc7ab 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2556,6 +2556,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .read_efuse = rtw8851b_read_efuse, .read_phycap = rtw8851b_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = rtw8851b_rfe_gpio, .rfk_hw_init = NULL, .rfk_init = rtw8851b_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 686e489d42f2..d3b6e8a7f5bd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2302,6 +2302,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .read_efuse = rtw8852a_read_efuse, .read_phycap = rtw8852a_read_phycap, .fem_setup = rtw8852a_fem_setup, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852a_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 6ab99f72fda7..989ac38fe359 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -883,6 +883,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .read_efuse = rtw8852bx_read_efuse, .read_phycap = rtw8852bx_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852b_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 83de26273100..b58561e141cb 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -729,6 +729,7 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = { .read_efuse = rtw8852bx_read_efuse, .read_phycap = rtw8852bx_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852bt_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index cc278587b532..9ecd140dcdd5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3091,6 +3091,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .read_efuse = rtw8852c_read_efuse, .read_phycap = rtw8852c_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = NULL, .rfk_init = rtw8852c_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index e6f15ee2a86b..07270e83b778 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3086,6 +3086,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .read_efuse = rtw8922a_read_efuse, .read_phycap = rtw8922a_read_phycap, .fem_setup = NULL, + .data_setup = NULL, .rfe_gpio = NULL, .rfk_hw_init = rtw8922a_rfk_hw_init, .rfk_init = rtw8922a_rfk_init, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index b8b75fca0a1b..66b05aedcf11 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -286,6 +286,15 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_ADIE] = {.offset = 0x70000, .size = 0x10}, }; +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { +}; + +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { +}; + +static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { +}; + static void rtw8922d_sel_bt_rx_path(struct rtw89_dev *rtwdev, u8 val, enum rtw89_rf_path rx_path) { @@ -918,6 +927,34 @@ static void rtw8922d_power_trim(struct rtw89_dev *rtwdev) rtw8922d_pad_bias_trim(rtwdev); } +static int rtw8922d_data_setup(struct rtw89_dev *rtwdev) +{ + const struct rtw89_bb_wrap_data *data; + struct rtw89_hal *hal = &rtwdev->hal; + bool rfe35_41_44 = false; + + switch (rtwdev->efuse.rfe_type) { + case 35: + case 41: + case 44: + rfe35_41_44 = true; + break; + } + + if (hal->cid == RTL8922D_CID7025) { + data = &rtw8922d_bb_wrap_data_7025_default; + } else { + if (rfe35_41_44) + data = &rtw8922d_bb_wrap_data_7090_rfe35_41_44; + else + data = &rtw8922d_bb_wrap_data_7090_default; + } + + rtwdev->phy_info.bb_wrap_data = data; + + return 0; +} + static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, u8 mac_idx) @@ -2933,6 +2970,7 @@ static const struct rtw89_chip_ops rtw8922d_chip_ops = { .read_efuse = rtw8922d_read_efuse, .read_phycap = rtw8922d_read_phycap, .fem_setup = NULL, + .data_setup = rtw8922d_data_setup, .rfe_gpio = NULL, .rfk_hw_init = rtw8922d_rfk_hw_init, .rfk_init = rtw8922d_rfk_init, From 99237c755c279522410bf1ef951fd7991178933a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:39 +0800 Subject: [PATCH 067/285] wifi: rtw89: phy: set BB wrap of out-of-band DPD Control the out-of-band DPD (digital pre-distortion) to ensure out-of-band signal under requirement. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 6 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 40 +++++++++++-------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 3 ++ 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index eabaf46f4f4b..2933abd3aac8 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -572,7 +572,13 @@ struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; +enum rtw89_mdpd_onoff { + MDPD_ON = 0, + MDPD_OFF = 1, +}; + struct rtw89_bb_wrap_data { + u8 mdpd_by_dbw[4]; }; struct rtw89_phy_gen_def { diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 23137f2dbd4b..d3b46d973845 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -886,23 +886,31 @@ static void rtw89_phy_bb_set_oob_dpd_qam_comp_val(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_set_mdpd_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH7_BE4, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th; - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW7_BE4, 0x0, mac_idx); + if (!d) + return; + + th = d->mdpd_by_dbw[0]; + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_TH7_BE4, th, mac_idx); + + th = d->mdpd_by_dbw[2]; + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_CBW160_BE4, B_DPD_CBW160_OW7_BE4, th, mac_idx); } static void rtw89_phy_bb_set_cim3k_val(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 66b05aedcf11..326d12da2962 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -287,12 +287,15 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { + .mdpd_by_dbw = {MDPD_ON, MDPD_ON, MDPD_ON, MDPD_ON}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { + .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { + .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, }; static void rtw8922d_sel_bt_rx_path(struct rtw89_dev *rtwdev, u8 val, From 9adb75af3f5a041bd53d59288c2c5c65f88187c2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:40 +0800 Subject: [PATCH 068/285] wifi: rtw89: phy: set BB wrap of DPD by bandwidth Apply different settings to out-of-band DPD (digital pre-distortion) by bandwidth, as hardware defines separate registers. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 15 ++ drivers/net/wireless/realtek/rtw89/phy_be.c | 142 ++++++++++-------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 27 ++++ 3 files changed, 120 insertions(+), 64 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 2933abd3aac8..880ffcb47cf2 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -572,12 +572,27 @@ struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; +enum rtw89_rfsi_ctrl_band { + RFSI_CTRL_BAND_5_6GHZ, + RFSI_CTRL_BAND_2GHZ, + + RFSI_CTRL_BAND_NUM, +}; + enum rtw89_mdpd_onoff { MDPD_ON = 0, MDPD_OFF = 1, }; +enum rtw89_oob_dpd_onoff { + OOB_DPD_OFF = 0, + OOB_DPD_ON = 1, +}; + struct rtw89_bb_wrap_data { + struct { + u8 oob_dpd_by_cbw[8]; + } bands[RFSI_CTRL_BAND_NUM]; u8 mdpd_by_dbw[4]; }; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index d3b46d973845..0d3192717869 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -810,77 +810,91 @@ static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_val(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_set_oob_dpd_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK7_BE4, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th; - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK7_BE4, 0x0, mac_idx); + if (!d) + return; - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[0]; + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_CCK7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[1]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_CCK7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[2]; + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW20_BE4, B_OOB_CBW20_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[3]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[4]; + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_TH7_BE4, th, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW0_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW1_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW2_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW3_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW4_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW5_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW6_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW7_BE4, 0x0, mac_idx); + th = d->bands[0].oob_dpd_by_cbw[5]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW20_OW7_BE4, th, mac_idx); + + th = d->bands[0].oob_dpd_by_cbw[6]; + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW40_BE4, B_OOB_CBW40_OW7_BE4, th, mac_idx); + + th = d->bands[0].oob_dpd_by_cbw[7]; + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW0_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW1_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW2_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW3_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW4_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW5_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW6_BE4, th, mac_idx); + rtw89_write32_idx(rtwdev, R_OOB_CBW80_BE4, B_OOB_CBW80_OW7_BE4, th, mac_idx); } static void rtw89_phy_bb_set_mdpd_qam_comp_val(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 326d12da2962..2e5e264e75db 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -287,14 +287,41 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, .mdpd_by_dbw = {MDPD_ON, MDPD_ON, MDPD_ON, MDPD_ON}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, + OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, + }}, .mdpd_by_dbw = {MDPD_OFF, MDPD_OFF, MDPD_ON, MDPD_ON}, }; From f791fe306078f71aa0914d50241a92eda5533805 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:41 +0800 Subject: [PATCH 069/285] wifi: rtw89: phy: set BB wrap of control options Set main options to control BB wrap functions. For example, enable options by data bandwidth or channel bandwidth. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 11 ++++++++ drivers/net/wireless/realtek/rtw89/phy_be.c | 11 ++++++-- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 27 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 880ffcb47cf2..8eb122eeabe8 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -589,7 +589,18 @@ enum rtw89_oob_dpd_onoff { OOB_DPD_ON = 1, }; +#define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ + ((n0) << 0 | (n1) << 4 | (n2) << 8 | (n3) << 12 | \ + (n4) << 16 | (n5) << 20 | (n6) << 24 | (n7) << 28) + +struct rtw89_bb_wrap_common_data { + struct { + u32 rfsi_ct_opt[2]; + } bands[RFSI_CTRL_BAND_NUM]; +}; + struct rtw89_bb_wrap_data { + const struct rtw89_bb_wrap_common_data *common; struct { u8 oob_dpd_by_cbw[8]; } bands[RFSI_CTRL_BAND_NUM]; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 0d3192717869..0d0f87f464a5 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -648,13 +648,20 @@ static u32 rtw89_phy_bb_wrap_be_bandedge_decision(struct rtw89_dev *rtwdev, void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const u32 *val; u32 reg; + if (!d || !d->common) + return; + + val = d->common->bands[0].rfsi_ct_opt; + reg = rtw89_mac_reg_by_idx(rtwdev, R_RFSI_CT_OPT_0_BE4, phy_idx); - rtw89_write32(rtwdev, reg, 0x00010001); + rtw89_write32(rtwdev, reg, val[0]); reg = rtw89_mac_reg_by_idx(rtwdev, R_RFSI_CT_OPT_8_BE4, phy_idx); - rtw89_write32(rtwdev, reg, 0x00010001); + rtw89_write32(rtwdev, reg, val[1]); } EXPORT_SYMBOL(rtw89_phy_bb_wrap_set_rfsi_ct_opt); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 2e5e264e75db..18d75184f82b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -286,7 +286,32 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_ADIE] = {.offset = 0x70000, .size = 0x10}, }; +static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + }}, +}; + +static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), + _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + }}, +}; + static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { + .common = &rtw8922d_bb_wrap_common_data_7025, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, @@ -300,6 +325,7 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { + .common = &rtw8922d_bb_wrap_common_data_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, @@ -313,6 +339,7 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { + .common = &rtw8922d_bb_wrap_common_data_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, From 7924c027474cef6ed3aab3198816a8f5af8f226d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:42 +0800 Subject: [PATCH 070/285] wifi: rtw89: phy: set BB wrap of QAM threshold Make hardware to consider which QAM (data rate) to apply BB wrapper parameters, which are set by other registers. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 12 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 74 +++++++++++-------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 + 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 8eb122eeabe8..34876de834d7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -589,6 +589,17 @@ enum rtw89_oob_dpd_onoff { OOB_DPD_ON = 1, }; +enum rtw89_rfsi_ctrl_modulation { + RFSI_BPSK = 0, + RFSI_QPSK = 1, + RFSI_16QAM = 2, + RFSI_64QAM = 3, + RFSI_256QAM = 4, + RFSI_1024QAM = 5, + RFSI_4096QAM = 6, + RFSI_MAX, +}; + #define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ ((n0) << 0 | (n1) << 4 | (n2) << 8 | (n3) << 12 | \ (n4) << 16 | (n5) << 20 | (n6) << 24 | (n7) << 28) @@ -597,6 +608,7 @@ struct rtw89_bb_wrap_common_data { struct { u32 rfsi_ct_opt[2]; } bands[RFSI_CTRL_BAND_NUM]; + u8 qam_th[6]; }; struct rtw89_bb_wrap_data { diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 0d0f87f464a5..efd63f438424 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -691,42 +691,52 @@ EXPORT_SYMBOL(rtw89_phy_bb_wrap_set_rfsi_bandedge_ch); static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 th0, th1, th2; + + if (!d || !d->common) + return; + + th0 = d->common->qam_th[0]; + th1 = d->common->qam_th[1]; + th2 = d->common->qam_th[2]; + /* TH0 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_1_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_4_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_7_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_6_BE4, 0x1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_1_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_4_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_7_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_6_BE4, th0, mac_idx); /* TH1 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_1_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_4_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_2_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_5_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_8_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_1_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_4_BE4, 0x2, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_7_BE4, 0x2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_1_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_4_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_2_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_5_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_8_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_1_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_4_BE4, th1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_7_BE4, th1, mac_idx); /* TH2 */ - rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_2_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_0_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_3_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_6_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_9_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_2_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_5_BE4, 0x4, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_8_BE4, 0x4, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH0_BE4, B_QAM_TH0_2_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_0_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_3_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_6_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH1_BE4, B_QAM_TH1_9_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_2_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_5_BE4, th2, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_TH2_BE4, B_QAM_TH2_8_BE4, th2, mac_idx); /* DPD 160M */ - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_0_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_1_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_2_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_3_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_4_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_5_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_6_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_7_BE4, 0x1, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_0_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_1_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_2_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_3_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH0_BE4, B_DPD_DBW160_TH0_4_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_5_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_6_BE4, th0, mac_idx); + rtw89_write32_idx(rtwdev, R_DPD_DBW160_TH1_BE4, B_DPD_DBW160_TH1_7_BE4, th0, mac_idx); /* DPD 20M */ rtw89_write32_idx(rtwdev, R_DPD_CBW_TH0_BE4, B_DPD_CBW20_TH0_0_BE4, 0x2, mac_idx); rtw89_write32_idx(rtwdev, R_DPD_CBW_TH0_BE4, B_DPD_CBW20_TH0_1_BE4, 0x2, mac_idx); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 18d75184f82b..a867652907bc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -296,6 +296,7 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, }}, + .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 = { @@ -308,6 +309,7 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, }}, + .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { From 7c4ef01bb3c37fb135e425245522a5fb2460ef59 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:43 +0800 Subject: [PATCH 071/285] wifi: rtw89: phy: set BB wrap of QAM options Apply these options to selected QAM to TX signal under requirements. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 15 ++++ drivers/net/wireless/realtek/rtw89/phy_be.c | 75 +++++++++++-------- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 36 +++++++++ 3 files changed, 94 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 34876de834d7..094ee7d7c10a 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -600,10 +600,21 @@ enum rtw89_rfsi_ctrl_modulation { RFSI_MAX, }; +#define MAX_TX_RFSI_CTRL_OPT 10 + #define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ ((n0) << 0 | (n1) << 4 | (n2) << 8 | (n3) << 12 | \ (n4) << 16 | (n5) << 20 | (n6) << 24 | (n7) << 28) +#define _qam_comp_code(c) ((((c) & (BIT(15) | BIT(14))) >> 11) | \ + (((c) & BIT(12)) >> 10) | \ + (((c) & (BIT(9) | BIT(8))) >> 8)) + +#define _10qam_comp_code(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9) \ + _qam_comp_code(c0), _qam_comp_code(c1), _qam_comp_code(c2), _qam_comp_code(c3), \ + _qam_comp_code(c4), _qam_comp_code(c5), _qam_comp_code(c6), _qam_comp_code(c7), \ + _qam_comp_code(c8), _qam_comp_code(c9) + struct rtw89_bb_wrap_common_data { struct { u32 rfsi_ct_opt[2]; @@ -614,6 +625,10 @@ struct rtw89_bb_wrap_common_data { struct rtw89_bb_wrap_data { const struct rtw89_bb_wrap_common_data *common; struct { + u16 qam_comp_th0[MAX_TX_RFSI_CTRL_OPT]; + u16 qam_comp_th1[MAX_TX_RFSI_CTRL_OPT]; /* encoded */ + u16 qam_comp_th2[MAX_TX_RFSI_CTRL_OPT]; /* encoded */ + u16 qam_comp_ow[MAX_TX_RFSI_CTRL_OPT]; u8 oob_dpd_by_cbw[8]; } bands[RFSI_CTRL_BAND_NUM]; u8 mdpd_by_dbw[4]; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index efd63f438424..dca36a5dfb0a 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -788,40 +788,51 @@ static void rtw89_phy_bb_wrap_tx_rfsi_scenario_def(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, 0x4410, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, 0x0, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const u16 *th; - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_L, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_M, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2L, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2M, 0x8, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2M, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2H, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2L, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2M, 0x0, mac_idx); + if (!d || !d->common) + return; - rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, 0x4010, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, 0x0, mac_idx); + th = d->bands[0].qam_comp_th0; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, th[7], mac_idx); + + th = d->bands[0].qam_comp_th1; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_L, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_M, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_H, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_L, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_M, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_H, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_L, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_M, th[7], mac_idx); + + th = d->bands[0].qam_comp_th2; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2L, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2M, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH4_BE4, B_QAM_COMP_TH4_2H, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2L, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2M, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH5_BE4, B_QAM_COMP_TH5_2H, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2L, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH6_BE4, B_QAM_COMP_TH6_2M, th[7], mac_idx); + + th = d->bands[0].qam_comp_ow; + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, th[0], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, th[1], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, th[2], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, th[3], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, th[4], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, th[5], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, th[6], mac_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, th[7], mac_idx); } static void rtw89_phy_bb_set_oob_dpd_qam_comp_val(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index a867652907bc..114f53ab9f7c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -316,10 +316,22 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { .common = &rtw8922d_bb_wrap_common_data_7025, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6400, 0x6500, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4400, 0x4400, 0x4000, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }, [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4400, 0x4400, 0, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }}, @@ -330,10 +342,22 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { .common = &rtw8922d_bb_wrap_common_data_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }, [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }}, @@ -344,10 +368,22 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = .common = &rtw8922d_bb_wrap_common_data_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x6400, 0x6500, 0x6000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0x4000, + 0, 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }, [RFSI_CTRL_BAND_2GHZ] = { + .qam_comp_th0 = {0x4000, 0x4400, 0x4500, 0, 0, 0, 0, 0, 0, 0}, + .qam_comp_th1 = {_10qam_comp_code(0x4000, 0x4400, 0x4500, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_th2 = {_10qam_comp_code(0x4000, 0x4400, 0x4400, 0, 0, + 0, 0, 0, 0, 0)}, + .qam_comp_ow = {0x4000, 0x4000, 0x4000, 0, 0, 0, 0, 0, 0, 0}, .oob_dpd_by_cbw = {OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF, OOB_DPD_OFF}, }}, From ce23c529c722fab7152d6797b4798acd9a18fb69 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:44 +0800 Subject: [PATCH 072/285] wifi: rtw89: phy: set BB wrap of trigger-base partial band Apply options to trigger-base partial band. For example, TX at RU-106 on a 160MHz bandwidth. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 1 + drivers/net/wireless/realtek/rtw89/phy_be.c | 8 +++++++- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 094ee7d7c10a..2298ba245050 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -618,6 +618,7 @@ enum rtw89_rfsi_ctrl_modulation { struct rtw89_bb_wrap_common_data { struct { u32 rfsi_ct_opt[2]; + u8 pb_tb; } bands[RFSI_CTRL_BAND_NUM]; u8 qam_th[6]; }; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index dca36a5dfb0a..a730b0329117 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -771,13 +771,19 @@ static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_wrap_tx_rfsi_scenario_def(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + u8 pb_tb = 0; + + if (d && d->common) + pb_tb = d->common->bands[0].pb_tb; + rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_ER_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_SUBF_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_FTM_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_RFSI_CT_DEF_BE4, B_RFSI_CT_SENS_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DEF_BE, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_PB_BE4, 0x0, mac_idx); + rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_PB_BE4, pb_tb, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DL_WO_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_DL_BF_BE4, 0x0, mac_idx); rtw89_write32_idx(rtwdev, R_FBTB_CT_DEF_BE4, B_FBTB_CT_MUMIMO_BE4, 0x0, mac_idx); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 114f53ab9f7c..15c44dd0d9fa 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -291,10 +291,12 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 [RFSI_CTRL_BAND_5_6GHZ] = { .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 3, }, [RFSI_CTRL_BAND_2GHZ] = { .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 0, }}, .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; @@ -304,10 +306,12 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 [RFSI_CTRL_BAND_5_6GHZ] = { .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 3, }, [RFSI_CTRL_BAND_2GHZ] = { .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, + .pb_tb = 0, }}, .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; From 89ff0338727fde5931185b2d2091273aff8d78ea Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:45 +0800 Subject: [PATCH 073/285] wifi: rtw89: phy: set BB wrap of CIM3K The options to enable calibrations of CIM 3rd-order for threshold, non-bandedge and bandedge. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 13 ++++++++++ drivers/net/wireless/realtek/rtw89/phy_be.c | 24 +++++++++++++++---- drivers/net/wireless/realtek/rtw89/reg.h | 3 +++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 4 ++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 2298ba245050..88a572bb2890 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -589,6 +589,16 @@ enum rtw89_oob_dpd_onoff { OOB_DPD_ON = 1, }; +enum rtw89_cim3k_onoff { + CIM3K_ON = 1, + CIM3K_OFF = 0, +}; + +enum rtw89_cim3k_en_dis { + CIM3K_ENABLE = 1, + CIM3K_DISABLE = 0, +}; + enum rtw89_rfsi_ctrl_modulation { RFSI_BPSK = 0, RFSI_QPSK = 1, @@ -617,6 +627,9 @@ enum rtw89_rfsi_ctrl_modulation { struct rtw89_bb_wrap_common_data { struct { + struct rtw89_bb_wrap_data_cim3k { + u8 th, ow, non_bandedge, bandedge; + } cim3k; u32 rfsi_ct_opt[2]; u8 pb_tb; } bands[RFSI_CTRL_BAND_NUM]; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index a730b0329117..cf7c52d4a0fc 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -964,10 +964,26 @@ static void rtw89_phy_bb_set_mdpd_qam_comp_val(struct rtw89_dev *rtwdev, static void rtw89_phy_bb_set_cim3k_val(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_OW_BE4, 0x0, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_NONBE_BE4, 0x1, mac_idx); - rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_BANDEDGE_BE4, 0x1, mac_idx); + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_data_cim3k *p; + + if (!d || !d->common) + return; + + p = &d->common->bands[0].cim3k; + + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH_BE4, p->th, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_OW_BE4, p->ow, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_NONBE_BE4, + p->non_bandedge, mac_idx); + rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_BANDEDGE_BE4, + p->bandedge, mac_idx); + + if (rtwdev->chip->chip_id != RTL8922D) + return; + + rtw89_write32_idx(rtwdev, R_CIM3K_SU_FORCE, B_CIM3K_SU_FORCE_EN, 1, mac_idx); + rtw89_write32_idx(rtwdev, R_CIM3K_SU_FORCE, B_CIM3K_SU_FORCE_VAL, 0, mac_idx); } static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 716747c3a10e..e46c32ec8e90 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10596,6 +10596,9 @@ #define B_FBTB_CT_MUMIMO_BE4 GENMASK(19, 16) #define B_FBTB_CT_FTM_BE4 GENMASK(23, 20) #define B_FBTB_CT_SENS_BE4 GENMASK(27, 24) +#define R_CIM3K_SU_FORCE 0x119F8 +#define B_CIM3K_SU_FORCE_EN BIT(17) +#define B_CIM3K_SU_FORCE_VAL BIT(18) #define R_RFSI_CT_OPT_0_BE4 0x11A94 #define R_RFSI_CT_OPT_8_BE4 0x11A98 #define R_QAM_COMP_TH0_BE4 0x11A9C diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 15c44dd0d9fa..ec94067aa1a5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -289,11 +289,13 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 = { .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_ENABLE, CIM3K_ENABLE}, .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, .pb_tb = 3, }, [RFSI_CTRL_BAND_2GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_ENABLE, CIM3K_ENABLE}, .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, .pb_tb = 0, @@ -304,11 +306,13 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7025 static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 = { .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_DISABLE, CIM3K_ENABLE}, .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, .pb_tb = 3, }, [RFSI_CTRL_BAND_2GHZ] = { + .cim3k = {CIM3K_OFF, CIM3K_OFF, CIM3K_DISABLE, CIM3K_ENABLE}, .rfsi_ct_opt = {_8nibble(2, 2, 2, 2, 1, 1, 1, 1), _8nibble(2, 2, 2, 2, 1, 1, 1, 1)}, .pb_tb = 0, From a395d7eed2f822a340f946ed8a81d6b55bfa9dc4 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:46 +0800 Subject: [PATCH 074/285] wifi: rtw89: phy: change order to align register order Change the order to align what vendor driver does. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy_be.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index cf7c52d4a0fc..68de34886a76 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -587,8 +587,8 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev, addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_FORCE_LMT, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_LMT_ON, 0); - addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_BOOST, mac_idx); - rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RATE_ON, 0); + addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); + rtw89_write32_mask(rtwdev, addr, B_BE_FORCE_PWR_BY_RATE_EN, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_OFST_RULMT, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ENON, 0); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ON, 0); @@ -596,8 +596,8 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev, rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ALL, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_COEX_CTRL, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_COEX_ON, 0); - addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); - rtw89_write32_mask(rtwdev, addr, B_BE_FORCE_PWR_BY_RATE_EN, 0); + addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_BOOST, mac_idx); + rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RATE_ON, 0); } static void rtw89_phy_bb_wrap_ftm_init(struct rtw89_dev *rtwdev, From 4f2b5c83657ed795b102f4378592086192cacc56 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:47 +0800 Subject: [PATCH 075/285] wifi: rtw89: phy: configure control options of BB wrapper by RFSI band The settings of control options are predefined per RFSI band (2GHz band or not 5/6GHz band) and used for fine-tuning transmit power. Calculate and set the RFSI band once creating channel context, and use it by BB wrapper configurations. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 2 ++ drivers/net/wireless/realtek/rtw89/core.h | 8 ++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 8 +------- drivers/net/wireless/realtek/rtw89/phy_be.c | 5 +++-- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index cd846cb81f0c..6f11335b4968 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -150,6 +150,8 @@ void rtw89_chan_create(struct rtw89_chan *chan, u8 center_chan, u8 primary_chan, primary_freq); chan->pri_sb_idx = rtw89_get_primary_sb_idx(center_chan, primary_chan, bandwidth); + chan->rfsi_band = band == RTW89_BAND_2G ? RFSI_CTRL_BAND_2GHZ : + RFSI_CTRL_BAND_5_6GHZ; } static void _rtw89_chan_update_punctured(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 75237fb23a99..4666e3041ef3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1015,6 +1015,13 @@ enum rtw89_sc_offset { RTW89_SC_40_LOWER = 10, }; +enum rtw89_rfsi_ctrl_band { + RFSI_CTRL_BAND_5_6GHZ, + RFSI_CTRL_BAND_2GHZ, + + RFSI_CTRL_BAND_NUM, +}; + /* only mgd features can be added to the enum */ enum rtw89_wow_flags { RTW89_WOW_FLAG_EN_MAGIC_PKT, @@ -1038,6 +1045,7 @@ struct rtw89_chan { enum rtw89_tx_comp_band tx_comp_band; enum rtw89_sc_offset pri_ch_idx; u8 pri_sb_idx; + enum rtw89_rfsi_ctrl_band rfsi_band; }; struct rtw89_chan_rcd { diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 88a572bb2890..ebf8a669f5d7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -572,13 +572,6 @@ struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; -enum rtw89_rfsi_ctrl_band { - RFSI_CTRL_BAND_5_6GHZ, - RFSI_CTRL_BAND_2GHZ, - - RFSI_CTRL_BAND_NUM, -}; - enum rtw89_mdpd_onoff { MDPD_ON = 0, MDPD_OFF = 1, @@ -1032,6 +1025,7 @@ static inline void rtw89_phy_bb_wrap_init(struct rtw89_dev *rtwdev) } void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, + enum rtw89_rfsi_ctrl_band ctrl_band, enum rtw89_phy_idx phy_idx); void rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 68de34886a76..16f679589d90 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -646,6 +646,7 @@ static u32 rtw89_phy_bb_wrap_be_bandedge_decision(struct rtw89_dev *rtwdev, } void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, + enum rtw89_rfsi_ctrl_band rfsi_band, enum rtw89_phy_idx phy_idx) { const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; @@ -655,7 +656,7 @@ void rtw89_phy_bb_wrap_set_rfsi_ct_opt(struct rtw89_dev *rtwdev, if (!d || !d->common) return; - val = d->common->bands[0].rfsi_ct_opt; + val = d->common->bands[rfsi_band].rfsi_ct_opt; reg = rtw89_mac_reg_by_idx(rtwdev, R_RFSI_CT_OPT_0_BE4, phy_idx); rtw89_write32(rtwdev, reg, val[0]); @@ -1004,7 +1005,7 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, rtw89_phy_bb_set_mdpd_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_cim3k_val(rtwdev, mac_idx); - rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); + rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, 0, phy_idx); rtw89_entity_get_conf(rtwdev, &conf); chan = conf.chans[phy_idx]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index ec94067aa1a5..ade89234afd6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2284,7 +2284,7 @@ static void rtw8922d_set_channel_bb(struct rtw89_dev *rtwdev, u8 pri_sb = chan->pri_sb_idx; u32 val; - rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); + rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, chan->rfsi_band, phy_idx); rtw8922d_ctrl_ch(rtwdev, chan, phy_idx); rtw8922d_ctrl_bw(rtwdev, pri_sb, chan->band_width, phy_idx); rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); From 7076af642955693935e60bc94546d105fb0395ca Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 11 May 2026 15:01:48 +0800 Subject: [PATCH 076/285] wifi: rtw89: phy: add BB wrapper generation 3 for RTL8922D variant The BB wrapper generation 3 support more settings to fine tune power threshold and CCK CFIR/filter per RFSI band. Add them accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260511070148.25257-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.h | 18 +++ drivers/net/wireless/realtek/rtw89/phy_be.c | 112 +++++++++++++++++- drivers/net/wireless/realtek/rtw89/reg.h | 70 +++++++++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 17 +++ 4 files changed, 212 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index ebf8a669f5d7..5af66656d0b8 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -603,6 +603,16 @@ enum rtw89_rfsi_ctrl_modulation { RFSI_MAX, }; +enum rtw89_cfir_onoff { + CFIR_ON = 1, + CFIR_OFF = 0, +}; + +enum rtw89_filter_onoff { + FILTER_A_ON = 1, + FILTER_A_OFF = 0, +}; + #define MAX_TX_RFSI_CTRL_OPT 10 #define _8nibble(n0, n1, n2, n3, n4, n5, n6, n7) \ @@ -629,8 +639,16 @@ struct rtw89_bb_wrap_common_data { u8 qam_th[6]; }; +struct rtw89_bb_wrap_common_data_gen3 { + struct { + u8 qam_th[6]; + } bands[RFSI_CTRL_BAND_NUM]; + u8 cck_val[2]; +}; + struct rtw89_bb_wrap_data { const struct rtw89_bb_wrap_common_data *common; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; struct { u16 qam_comp_th0[MAX_TX_RFSI_CTRL_OPT]; u16 qam_comp_th1[MAX_TX_RFSI_CTRL_OPT]; /* encoded */ diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 16f679589d90..d0a6dfdbfea7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -769,6 +769,75 @@ static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(struct rtw89_dev *rtwdev, rtw89_write32_idx(rtwdev, R_COMP_CIM3K_BE4, B_COMP_CIM3K_TH2_BE4, 0x2, mac_idx); } +static void rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_gen3_init(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_mac_idx mac_idx) +{ + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; + const u8 *ths; + + if (!d || !d->common_gen3) + return; + + common_gen3 = d->common_gen3; + ths = common_gen3->bands[chan->rfsi_band].qam_th; + + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_0_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_1_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_2_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_3_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_4_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_5_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_6_BE4, ths[0], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH0_7_BE4, ths[0], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH1_0_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH0_BE4, B_QAM3_TH1_1_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_2_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_3_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_4_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_5_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_6_BE4, ths[1], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH1_7_BE4, ths[1], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_0_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_1_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_2_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH1_BE4, B_QAM3_TH2_3_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_4_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_5_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_6_BE4, ths[2], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH2_7_BE4, ths[2], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_0_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_1_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_2_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_3_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_4_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH2_BE4, B_QAM3_TH3_5_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH3_6_BE4, ths[3], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH3_7_BE4, ths[3], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_0_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_1_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_2_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_3_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_4_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_5_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_6_BE4, ths[4], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_TH4_7_BE4, ths[4], mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_0_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_1_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_2_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_3_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_4_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_5_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_6_BE4, ths[5], mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH3_BE4, B_QAM3_TH5_7_BE4, ths[5], mac_idx); +} + static void rtw89_phy_bb_wrap_tx_rfsi_scenario_def(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { @@ -987,6 +1056,39 @@ static void rtw89_phy_bb_set_cim3k_val(struct rtw89_dev *rtwdev, rtw89_write32_idx(rtwdev, R_CIM3K_SU_FORCE, B_CIM3K_SU_FORCE_VAL, 0, mac_idx); } +static void rtw89_phy_bb_set_cck_cfir_filter_val_gen3(struct rtw89_dev *rtwdev, + enum rtw89_mac_idx mac_idx) +{ + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_bb_wrap_common_data_gen3 *common_gen3; + u8 cck0, cck1; + + if (!d || !d->common_gen3) + return; + + common_gen3 = d->common_gen3; + cck0 = common_gen3->cck_val[0]; + cck1 = common_gen3->cck_val[1]; + + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR0_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR1_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR2_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR3_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR4_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR5_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR6_BE4, cck0, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_TH4_BE4, B_QAM3_CFIR7_BE4, cck0, mac_idx); + + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR0_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR1_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR2_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR3_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR4_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR5_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR6_BE4, cck1, mac_idx); + rtw89_write32_idx(rtwdev, R_QAM3_FLTR_BE4, B_QAM3_FLTR7_BE4, cck1, mac_idx); +} + static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx) { @@ -998,18 +1100,18 @@ static void rtw89_phy_bb_wrap_tx_rfsi_ctrl_init(struct rtw89_dev *rtwdev, if (chip_id != RTL8922D) return; + rtw89_entity_get_conf(rtwdev, &conf); + chan = conf.chans[phy_idx]; + rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_init(rtwdev, mac_idx); + rtw89_phy_bb_wrap_tx_rfsi_qam_comp_th_gen3_init(rtwdev, chan, mac_idx); rtw89_phy_bb_wrap_tx_rfsi_scenario_def(rtwdev, mac_idx); rtw89_phy_bb_wrap_tx_rfsi_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_oob_dpd_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_mdpd_qam_comp_val(rtwdev, mac_idx); rtw89_phy_bb_set_cim3k_val(rtwdev, mac_idx); - + rtw89_phy_bb_set_cck_cfir_filter_val_gen3(rtwdev, mac_idx); rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, 0, phy_idx); - - rtw89_entity_get_conf(rtwdev, &conf); - chan = conf.chans[phy_idx]; - rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); } diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index e46c32ec8e90..c9bfb163c32e 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10599,6 +10599,17 @@ #define R_CIM3K_SU_FORCE 0x119F8 #define B_CIM3K_SU_FORCE_EN BIT(17) #define B_CIM3K_SU_FORCE_VAL BIT(18) +#define R_QAM3_TH0_BE4 0x119FC +#define B_QAM3_TH0_0_BE4 GENMASK(2, 0) +#define B_QAM3_TH0_1_BE4 GENMASK(5, 3) +#define B_QAM3_TH0_2_BE4 GENMASK(8, 6) +#define B_QAM3_TH0_3_BE4 GENMASK(11, 9) +#define B_QAM3_TH0_4_BE4 GENMASK(14, 12) +#define B_QAM3_TH0_5_BE4 GENMASK(17, 15) +#define B_QAM3_TH0_6_BE4 GENMASK(20, 18) +#define B_QAM3_TH0_7_BE4 GENMASK(23, 21) +#define B_QAM3_TH1_0_BE4 GENMASK(26, 24) +#define B_QAM3_TH1_1_BE4 GENMASK(29, 27) #define R_RFSI_CT_OPT_0_BE4 0x11A94 #define R_RFSI_CT_OPT_8_BE4 0x11A98 #define R_QAM_COMP_TH0_BE4 0x11A9C @@ -10634,6 +10645,65 @@ #define B_BANDEDGE_DBW80_BE4 BIT(31) #define R_BANDEDGE_DBWY_BE4 0x11AD0 #define B_BANDEDGE_DBW160_BE4 BIT(0) +#define R_QAM3_TH1_BE4 0x11BE8 +#define B_QAM3_TH1_2_BE4 GENMASK(2, 0) +#define B_QAM3_TH1_3_BE4 GENMASK(5, 3) +#define B_QAM3_TH1_4_BE4 GENMASK(8, 6) +#define B_QAM3_TH1_5_BE4 GENMASK(11, 9) +#define B_QAM3_TH1_6_BE4 GENMASK(14, 12) +#define B_QAM3_TH1_7_BE4 GENMASK(17, 15) +#define B_QAM3_TH2_0_BE4 GENMASK(20, 18) +#define B_QAM3_TH2_1_BE4 GENMASK(23, 21) +#define B_QAM3_TH2_2_BE4 GENMASK(26, 24) +#define B_QAM3_TH2_3_BE4 GENMASK(29, 27) +#define R_QAM3_TH2_BE4 0x11BEC +#define B_QAM3_TH2_4_BE4 GENMASK(2, 0) +#define B_QAM3_TH2_5_BE4 GENMASK(5, 3) +#define B_QAM3_TH2_6_BE4 GENMASK(8, 6) +#define B_QAM3_TH2_7_BE4 GENMASK(11, 9) +#define B_QAM3_TH3_0_BE4 GENMASK(14, 12) +#define B_QAM3_TH3_1_BE4 GENMASK(17, 15) +#define B_QAM3_TH3_2_BE4 GENMASK(20, 18) +#define B_QAM3_TH3_3_BE4 GENMASK(23, 21) +#define B_QAM3_TH3_4_BE4 GENMASK(26, 24) +#define B_QAM3_TH3_5_BE4 GENMASK(29, 27) +#define R_QAM3_TH3_BE4 0x11BF4 +#define B_QAM3_TH3_6_BE4 GENMASK(2, 0) +#define B_QAM3_TH3_7_BE4 GENMASK(5, 3) +#define B_QAM3_TH5_0_BE4 GENMASK(8, 6) +#define B_QAM3_TH5_1_BE4 GENMASK(11, 9) +#define B_QAM3_TH5_2_BE4 GENMASK(14, 12) +#define B_QAM3_TH5_3_BE4 GENMASK(17, 15) +#define B_QAM3_TH5_4_BE4 GENMASK(20, 18) +#define B_QAM3_TH5_5_BE4 GENMASK(23, 21) +#define B_QAM3_TH5_6_BE4 GENMASK(26, 24) +#define B_QAM3_TH5_7_BE4 GENMASK(29, 27) +#define R_QAM3_TH4_BE4 0x11BF8 +#define B_QAM3_TH4_0_BE4 GENMASK(2, 0) +#define B_QAM3_TH4_1_BE4 GENMASK(5, 3) +#define B_QAM3_TH4_2_BE4 GENMASK(8, 6) +#define B_QAM3_TH4_3_BE4 GENMASK(11, 9) +#define B_QAM3_TH4_4_BE4 GENMASK(14, 12) +#define B_QAM3_TH4_5_BE4 GENMASK(17, 15) +#define B_QAM3_TH4_6_BE4 GENMASK(20, 18) +#define B_QAM3_TH4_7_BE4 GENMASK(23, 21) +#define B_QAM3_CFIR0_BE4 BIT(24) +#define B_QAM3_CFIR1_BE4 BIT(25) +#define B_QAM3_CFIR2_BE4 BIT(26) +#define B_QAM3_CFIR3_BE4 BIT(27) +#define B_QAM3_CFIR4_BE4 BIT(28) +#define B_QAM3_CFIR5_BE4 BIT(29) +#define B_QAM3_CFIR6_BE4 BIT(30) +#define B_QAM3_CFIR7_BE4 BIT(31) +#define R_QAM3_FLTR_BE4 0x11BFC +#define B_QAM3_FLTR0_BE4 GENMASK(1, 0) +#define B_QAM3_FLTR1_BE4 GENMASK(3, 2) +#define B_QAM3_FLTR2_BE4 GENMASK(5, 4) +#define B_QAM3_FLTR3_BE4 GENMASK(7, 6) +#define B_QAM3_FLTR4_BE4 GENMASK(9, 8) +#define B_QAM3_FLTR5_BE4 GENMASK(11, 10) +#define B_QAM3_FLTR6_BE4 GENMASK(13, 12) +#define B_QAM3_FLTR7_BE4 GENMASK(15, 14) #define R_SYS_DBCC_BE4 0x20000 #define B_SYS_DBCC_BE4 BIT(0) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index ade89234afd6..c6c37e25e4c8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -320,8 +320,23 @@ static const struct rtw89_bb_wrap_common_data rtw8922d_bb_wrap_common_data_7090 .qam_th = {RFSI_BPSK, RFSI_QPSK, RFSI_256QAM, RFSI_MAX, RFSI_MAX, RFSI_MAX}, }; +static const +struct rtw89_bb_wrap_common_data_gen3 rtw8922d_bb_wrap_common_data_gen3_7090 = { + .bands = { + [RFSI_CTRL_BAND_5_6GHZ] = { + .qam_th = {RFSI_QPSK, RFSI_BPSK, RFSI_256QAM, RFSI_MAX, + RFSI_MAX, RFSI_MAX}, + }, + [RFSI_CTRL_BAND_2GHZ] = { + .qam_th = {RFSI_MAX, RFSI_MAX, RFSI_256QAM, RFSI_MAX, + RFSI_MAX, RFSI_MAX}, + }}, + .cck_val = {CFIR_OFF, FILTER_A_OFF}, +}; + static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { .common = &rtw8922d_bb_wrap_common_data_7025, + .common_gen3 = NULL, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .qam_comp_th0 = {0x4000, 0x6400, 0x6500, 0x6000, 0, 0, 0, 0, 0, 0}, @@ -348,6 +363,7 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7025_default = { static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { .common = &rtw8922d_bb_wrap_common_data_7090, + .common_gen3 = &rtw8922d_bb_wrap_common_data_gen3_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, @@ -374,6 +390,7 @@ static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_default = { static const struct rtw89_bb_wrap_data rtw8922d_bb_wrap_data_7090_rfe35_41_44 = { .common = &rtw8922d_bb_wrap_common_data_7090, + .common_gen3 = &rtw8922d_bb_wrap_common_data_gen3_7090, .bands = { [RFSI_CTRL_BAND_5_6GHZ] = { .qam_comp_th0 = {0x4000, 0x6420, 0x6520, 0x6000, 0, 0, 0, 0, 0, 0}, From e384397796eaf1ecba9e3d91a3f10ec63c7dbc76 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 15 May 2026 09:44:21 +0800 Subject: [PATCH 077/285] wifi: rtw89: debug: Wi-Fi 7 show count of SER L0 simulation Wi-Fi 7 has dedicated HW register to count SER L0 simulation, i.e. manually triggered by users. Show count of it in dbgfs ser_counters. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 8ee800c76cfe..e5976b8e7056 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3760,6 +3760,7 @@ rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, } struct rtw89_dbg_ser_counters { + unsigned int l0_sim; unsigned int l0; unsigned int l1; unsigned int l0_to_l1; @@ -3780,6 +3781,7 @@ static void rtw89_dbg_get_ser_counters_be(struct rtw89_dev *rtwdev, { const u32 val = rtw89_read32(rtwdev, R_BE_SER_DBG_INFO); + cnt->l0_sim = rtw89_read8(rtwdev, R_BE_SER_L0_DBG_CNT1 + 3); cnt->l0 = u32_get_bits(val, B_BE_SER_L0_COUNTER_MASK); cnt->l1 = u32_get_bits(val, B_BE_SER_L1_COUNTER_MASK); cnt->l0_to_l1 = u32_get_bits(val, B_BE_SER_L0_PROMOTE_L1_EVENT_MASK); @@ -3810,6 +3812,10 @@ static ssize_t rtw89_debug_priv_ser_counters_get(struct rtw89_dev *rtwdev, p += scnprintf(p, end - p, "SER L1 SW Count: %u\n", sw_cnt->l1); p += scnprintf(p, end - p, "SER L2 SW Count: %u\n", sw_cnt->l2); + /* Some chipsets don't have dedicated cnt for SER simulation. */ + p += scnprintf(p, end - p, "---\n"); + p += scnprintf(p, end - p, "SER L0 Simulation Count: %d\n", cnt.l0_sim); + /* Some chipsets won't record SER simulation in HW cnt. */ p += scnprintf(p, end - p, "---\n"); p += scnprintf(p, end - p, "SER L0 Count: %d\n", cnt.l0); From becd1c37efc369694dbbaf0ea2a4c06b83c72546 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 15 May 2026 09:44:22 +0800 Subject: [PATCH 078/285] wifi: rtw89: debug: Wi-Fi 7 update simulation of SER L0/L1 by halt H2C command Wi-Fi 7 FW fixes support of triggering SER L0/L1 simulation via halt H2C command on v0.35.108.0. After that, the halt H2C command trigger for Wi-Fi 6 and Wi-Fi 7 can be the same. Update FW feature table and share the halt H2C command trigger function between Wi-Fi 6 and Wi-Fi 7. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 127 +++++++++------------ drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 2 files changed, 58 insertions(+), 71 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index e5976b8e7056..c0fbb016cea4 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3543,53 +3543,13 @@ rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, return count; } -static int rtw89_dbg_trigger_l1_error_by_halt_h2c_ax(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_FORCE); -} - -static int rtw89_dbg_trigger_l1_error_by_halt_h2c_be(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - rtw89_leave_ps_mode(rtwdev); - - rtw89_write32_set(rtwdev, R_BE_FW_TRIGGER_IDCT_ISR, - B_BE_DMAC_FW_TRIG_IDCT | B_BE_DMAC_FW_ERR_IDCT_IMR); - - return 0; -} - -static int rtw89_dbg_trigger_l1_error_by_halt_h2c(struct rtw89_dev *rtwdev) -{ - const struct rtw89_chip_info *chip = rtwdev->chip; - - switch (chip->chip_gen) { - case RTW89_CHIP_AX: - return rtw89_dbg_trigger_l1_error_by_halt_h2c_ax(rtwdev); - case RTW89_CHIP_BE: - return rtw89_dbg_trigger_l1_error_by_halt_h2c_be(rtwdev); - default: - return -EOPNOTSUPP; - } -} - -static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) +static int rtw89_dbg_trigger_l1_error_ax(struct rtw89_dev *rtwdev) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; - struct rtw89_cpuio_ctrl ctrl_para = {0}; + struct rtw89_cpuio_ctrl ctrl_para = {}; u16 pkt_id; int ret; - if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) - return rtw89_dbg_trigger_l1_error_by_halt_h2c(rtwdev); - - rtw89_leave_ps_mode(rtwdev); - ret = mac->dle_buf_req(rtwdev, 0x20, true, &pkt_id); if (ret) return ret; @@ -3608,6 +3568,52 @@ static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) return 0; } +static int rtw89_dbg_trigger_l1_error_be(struct rtw89_dev *rtwdev) +{ + int ret; + + ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_FW_TRIGGER_IDCT_ISR, + B_BE_DMAC_FW_TRIG_IDCT | B_BE_DMAC_FW_ERR_IDCT_IMR); + + return 0; +} + +static int rtw89_dbg_trigger_l1_error_by_halt_h2c(struct rtw89_dev *rtwdev) +{ + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L1_RESET_FORCE); +} + +static int rtw89_dbg_trigger_l1_error(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + int (*sim_l1)(struct rtw89_dev *rtwdev); + + switch (chip->chip_gen) { + case RTW89_CHIP_AX: + sim_l1 = rtw89_dbg_trigger_l1_error_ax; + break; + case RTW89_CHIP_BE: + sim_l1 = rtw89_dbg_trigger_l1_error_be; + break; + default: + return -EOPNOTSUPP; + } + + if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) + return rtw89_dbg_trigger_l1_error_by_halt_h2c(rtwdev); + + rtw89_leave_ps_mode(rtwdev); + + return sim_l1(rtwdev); +} + static int rtw89_dbg_trigger_l0_error_ax(struct rtw89_dev *rtwdev) { u16 val16; @@ -3632,55 +3638,36 @@ static int rtw89_dbg_trigger_l0_error_ax(struct rtw89_dev *rtwdev) static int rtw89_dbg_trigger_l0_error_be(struct rtw89_dev *rtwdev) { - u8 val8; int ret; ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_CMAC_SEL); if (ret) return ret; - val8 = rtw89_read8(rtwdev, R_BE_CMAC_FUNC_EN); - rtw89_write8(rtwdev, R_BE_CMAC_FUNC_EN, val8 & ~B_BE_TMAC_EN); - mdelay(1); - rtw89_write8(rtwdev, R_BE_CMAC_FUNC_EN, val8); - - return 0; -} - -static int rtw89_dbg_trigger_l0_error_by_halt_h2c_ax(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L0_RESET_FORCE); -} - -static int rtw89_dbg_trigger_l0_error_by_halt_h2c_be(struct rtw89_dev *rtwdev) -{ - if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) - return -EBUSY; - - rtw89_leave_ps_mode(rtwdev); - rtw89_write32_set(rtwdev, R_BE_CMAC_FW_TRIGGER_IDCT_ISR, B_BE_CMAC_FW_TRIG_IDCT | B_BE_CMAC_FW_ERR_IDCT_IMR); return 0; } +static int rtw89_dbg_trigger_l0_error_by_halt_h2c(struct rtw89_dev *rtwdev) +{ + if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) + return -EBUSY; + + return rtw89_mac_set_err_status(rtwdev, MAC_AX_ERR_L0_RESET_FORCE); +} + static int rtw89_dbg_trigger_l0_error(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; - int (*sim_l0_by_halt_h2c)(struct rtw89_dev *rtwdev); int (*sim_l0)(struct rtw89_dev *rtwdev); switch (chip->chip_gen) { case RTW89_CHIP_AX: - sim_l0_by_halt_h2c = rtw89_dbg_trigger_l0_error_by_halt_h2c_ax; sim_l0 = rtw89_dbg_trigger_l0_error_ax; break; case RTW89_CHIP_BE: - sim_l0_by_halt_h2c = rtw89_dbg_trigger_l0_error_by_halt_h2c_be; sim_l0 = rtw89_dbg_trigger_l0_error_be; break; default: @@ -3688,7 +3675,7 @@ static int rtw89_dbg_trigger_l0_error(struct rtw89_dev *rtwdev) } if (RTW89_CHK_FW_FEATURE(SIM_SER_L0L1_BY_HALT_H2C, &rtwdev->fw)) - return sim_l0_by_halt_h2c(rtwdev); + return rtw89_dbg_trigger_l0_error_by_halt_h2c(rtwdev); rtw89_leave_ps_mode(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ff3914a16b81..c5a598a507f4 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -931,8 +931,8 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, RFK_PRE_NOTIFY_MCC_V1), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 92, 0, TX_HISTORY_V1), - __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 97, 0, SIM_SER_L0L1_BY_HALT_H2C), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), + __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 108, 0, SIM_SER_L0L1_BY_HALT_H2C), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, From 125fd4514fa8ca355a6b8e45bf4ff319c483a77b Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 15 May 2026 09:44:23 +0800 Subject: [PATCH 079/285] wifi: rtw89: fw: dump status of H2C command and C2H event for SER When SER (system error recovery) happens, there may be some handshake between FW and SW, e.g. SER Level 1. These handshake are based on H2C commands and C2H events. Dump the status of them to enhance SER debug. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index c5a598a507f4..29d8d13d11fe 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -8293,6 +8293,12 @@ void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev) rtw89_info(rtwdev, "FW MISC = 0x%x\n", rtw89_read32(rtwdev, R_AX_UDM3)); rtw89_info(rtwdev, "R_AX_HALT_C2H = 0x%x\n", rtw89_read32(rtwdev, R_AX_HALT_C2H)); + rtw89_info(rtwdev, "R_AX_HALT_C2H_CTRL = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_C2H_CTRL)); + rtw89_info(rtwdev, "R_AX_HALT_H2C = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_H2C)); + rtw89_info(rtwdev, "R_AX_HALT_H2C_CTRL = 0x%x\n", + rtw89_read32(rtwdev, R_AX_HALT_H2C_CTRL)); rtw89_info(rtwdev, "R_AX_SER_DBG_INFO = 0x%x\n", rtw89_read32(rtwdev, R_AX_SER_DBG_INFO)); From 779bbe1902f29d0ef131249ddd42a8dfbe21d0fb Mon Sep 17 00:00:00 2001 From: Dian-Syuan Yang Date: Fri, 15 May 2026 09:44:24 +0800 Subject: [PATCH 080/285] wifi: rtw89: pci: enable LTR based on pcie control register Originally, driver always transmits LTR (Latency Tolerance Reporting) to pcie host, but it may cause pcie link down on some platforms because LTR is not supported. As a result, driver will check the control register of LTR setting to decide whether to enable LTR feature. This applies to Wi-Fi 6 chips only. For Wi-Fi 7 chips, although the driver still issues LTR, the hardware has its own internal logic to determine whether to actually transmit it to pcie host. Signed-off-by: Dian-Syuan Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 64554eb35a72..f7107dc05b71 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -3039,6 +3039,17 @@ static int rtw89_pci_mode_op(struct rtw89_dev *rtwdev) return 0; } +static bool rtw89_pci_dev_ltr_enabled(struct rtw89_dev *rtwdev) +{ + struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + struct pci_dev *pdev = rtwpci->pdev; + u16 cap; + + pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &cap); + + return !!(cap & PCI_EXP_DEVCTL2_LTR_EN); +} + static int rtw89_pci_ops_deinit(struct rtw89_dev *rtwdev) { const struct rtw89_pci_info *info = rtwdev->pci_info; @@ -3143,7 +3154,7 @@ int rtw89_pci_ltr_set(struct rtw89_dev *rtwdev, bool en) { u32 val; - if (!en) + if (!en || !rtw89_pci_dev_ltr_enabled(rtwdev)) return 0; val = rtw89_read32(rtwdev, R_AX_LTR_CTRL_0); @@ -3179,6 +3190,9 @@ int rtw89_pci_ltr_set_v1(struct rtw89_dev *rtwdev, bool en) u32 dec_ctrl; u32 val32; + if (!rtw89_pci_dev_ltr_enabled(rtwdev)) + return 0; + val32 = rtw89_read32(rtwdev, R_AX_LTR_CTRL_0); if (rtw89_pci_ltr_is_err_reg_val(val32)) return -EINVAL; From e0bb1246e3078912396fb6ad5a18fe067c38e462 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:25 +0800 Subject: [PATCH 081/285] wifi: rtw89: pci: not disable PCI completion timeout control for a variant of RTL8922DE The variant of RTL8922DE change the design, and no need to disable PCI completion timeout. Apply the setting accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index f7107dc05b71..fe1152c560bd 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4380,10 +4380,20 @@ static void rtw89_pci_l1ss_cfg(struct rtw89_dev *rtwdev) static void rtw89_pci_cpl_timeout_cfg(struct rtw89_dev *rtwdev) { struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; + struct rtw89_hal *hal = &rtwdev->hal; struct pci_dev *pdev = rtwpci->pdev; + bool dis = true; - pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); + if (chip_id == RTL8922D && hal->cid == RTL8922D_CID7090) + dis = false; + + if (dis) + pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); + else + pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); } static int rtw89_pci_poll_io_idle_ax(struct rtw89_dev *rtwdev) From 8ee752289e1fc4a0fc55b042d467d6726da65cff Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:26 +0800 Subject: [PATCH 082/285] wifi: rtw89: pci: disable PCI PHY error flag 8 As the PHY error flag 8 works improperly, disable it to prevent false alarm causing SER. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 4 +++- drivers/net/wireless/realtek/rtw89/pci_be.c | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index d0d1e2b99698..c3f2d0df5846 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -58,7 +58,7 @@ #define B_AX_DIV GENMASK(15, 14) #define RAC_SET_PPR_V1 0x31 #define RAC_ANA40 0x40 -#define PHY_ERR_IMR_DIS (BIT(9) | BIT(0)) +#define PHY_ERR_IMR_DIS (BIT(9) | BIT(8) | BIT(0)) #define RAC_ANA41 0x41 #define PHY_ERR_FLAG_EN BIT(6) @@ -963,6 +963,8 @@ #define R_BE_PCIE_HRPWM 0x30C0 #define R_BE_PCIE_CRPWM 0x30C4 +#define R_BE_PCIE_HCI2FW_ISR 0x30CC + #define R_BE_L1_2_CTRL_HCILDO 0x3110 #define B_BE_PM_CLKREQ_EXT_RB BIT(11) #define B_BE_PCIE_DIS_RTK_PRST_N_L1_2 BIT(10) diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index 473d491eb3f4..6390980b8ee0 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -347,6 +347,7 @@ static void rtw89_pci_pcie_setting_be(struct rtw89_dev *rtwdev) rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_R_SYM_PRST_CPHY_RST); rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_USUS_OFFCAPC_EN); + rtw89_write32(rtwdev, R_BE_PCIE_HCI2FW_ISR, 0xFFFFFFFF); } static void rtw89_pci_ser_setting_be(struct rtw89_dev *rtwdev) @@ -843,6 +844,8 @@ static int __maybe_unused rtw89_pci_resume_be(struct device *dev) rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G1 + RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN); } + + rtw89_write32(rtwdev, R_BE_PCIE_HCI2FW_ISR, 0xFFFFFFFF); } rtw89_pci_basic_cfg(rtwdev, true); From 8c9e5fb13434ca76a0f8247e8f9dc1bbfb298e34 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:27 +0800 Subject: [PATCH 083/285] wifi: rtw89: clear auto K delay value before downloading firmware Once downloading firmware, do calibration on delay function to ensure firmware can do proper delay for hardware IO. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 3 +++ drivers/net/wireless/realtek/rtw89/mac.h | 12 ++++++++++++ drivers/net/wireless/realtek/rtw89/mac_be.c | 7 +++++++ drivers/net/wireless/realtek/rtw89/reg.h | 4 ++++ 4 files changed, 26 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 87e3320b5a81..6153162ad9b0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4318,6 +4318,8 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb) return ret; } + rtw89_mac_fwdl_preconfig(rtwdev); + ret = rtw89_fw_download(rtwdev, RTW89_FW_NORMAL, include_bb); if (ret) return ret; @@ -7477,6 +7479,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .reset_pwr_state = rtw89_mac_reset_pwr_state_ax, .disable_cpu = rtw89_mac_disable_cpu_ax, + .fwdl_preconfig = NULL, .fwdl_enable_wcpu = rtw89_mac_enable_cpu_ax, .fwdl_get_status = rtw89_fw_get_rdy_ax, .fwdl_check_path_ready = rtw89_fwdl_check_path_ready_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 75691a9549a7..69f8d7d818a0 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1112,6 +1112,7 @@ struct rtw89_mac_gen_def { int (*reset_pwr_state)(struct rtw89_dev *rtwdev); void (*disable_cpu)(struct rtw89_dev *rtwdev); + void (*fwdl_preconfig)(struct rtw89_dev *rtwdev); int (*fwdl_enable_wcpu)(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw, bool include_bb); u8 (*fwdl_get_status)(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type); @@ -1725,6 +1726,17 @@ static inline int rtw89_mac_efuse_read_ecv(struct rtw89_dev *rtwdev) return mac->efuse_read_ecv(rtwdev); } +static inline +void rtw89_mac_fwdl_preconfig(struct rtw89_dev *rtwdev) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + + if (!mac->fwdl_preconfig) + return; + + mac->fwdl_preconfig(rtwdev); +} + static inline void rtw89_fwdl_secure_idmem_share_mode(struct rtw89_dev *rtwdev, u8 mode) { diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index dd0dbe78a1ed..f24c119b99f1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -622,6 +622,12 @@ static void rtw89_mac_disable_cpu_be(struct rtw89_dev *rtwdev) rtw89_write32(rtwdev, R_BE_UDM2, 0); } +static void rtw89_mac_fwdl_preconfig_be(struct rtw89_dev *rtwdev) +{ + rtw89_write32_clr(rtwdev, R_BE_FW_AUTO_CAL_DELAY, B_BE_WCPU_FW_DELAY_COUNT_VALID); + rtw89_write32_mask(rtwdev, R_BE_FW_AUTO_CAL_DELAY, B_BE_WCPU_FW_DELAY_COUNT_MASK, 0); +} + static void set_cpu_en(struct rtw89_dev *rtwdev, bool include_bb) { u32 set = B_BE_WLANCPU_FWDL_EN; @@ -3288,6 +3294,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .reset_pwr_state = rtw89_mac_reset_pwr_state_be, .disable_cpu = rtw89_mac_disable_cpu_be, + .fwdl_preconfig = rtw89_mac_fwdl_preconfig_be, .fwdl_enable_wcpu = rtw89_mac_fwdl_enable_wcpu_be, .fwdl_get_status = fwdl_get_status_be, .fwdl_check_path_ready = rtw89_fwdl_check_path_ready_be, diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index c9bfb163c32e..c2ffa6c196e4 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4329,6 +4329,10 @@ #define R_BE_SECURE_BOOT_MALLOC_INFO 0x0184 +#define R_BE_FW_AUTO_CAL_DELAY 0x0188 +#define B_BE_WCPU_FW_DELAY_COUNT_VALID BIT(15) +#define B_BE_WCPU_FW_DELAY_COUNT_MASK GENMASK(14, 0) + #define R_BE_FWS0IMR 0x0190 #define B_BE_FS_HALT_H2C_INT_EN BIT(31) #define B_BE_FS_FSM_HIOE_TO_EVENT_INT_EN BIT(30) From dcf5c7262bffa2cbafc6523be63c287d15d8ae22 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Fri, 15 May 2026 09:44:28 +0800 Subject: [PATCH 084/285] wifi: rtw89: wow: send ARP reply packets instead of Null packets to keep alive In WoWLAN mode, the firmware periodically sends Null packets to the AP to keep the connection alive and prevent the AP from disconnecting the client due to inactivity. However, it was observed that certain APs, such as TP-Link Archer BE800, do not recognize Null packets as valid activity and still drop the connection. Replacing Null packets with ARP reply packets effectively resolves this compatibility issue and maintains the connection. Specifically, while the firmware typically handles standard ARP offloads by dynamically modifying target IP/MAC fields, these keep-alive ARP reply packets are pre-filled by the driver with the DUT's own MAC and IP addresses for both sender and target fields. In this case, the firmware transmits the packets as-is without further modification. This approach ensures compatibility with APs that require valid Layer 3 traffic for activity monitoring while simplifying the firmware's processing logic during WoWLAN mode. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-9-pkshih@realtek.com --- 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 29d8d13d11fe..eb5683d64461 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2958,6 +2958,8 @@ static struct sk_buff *rtw89_arp_response_get(struct rtw89_dev *rtwdev, ether_addr_copy(arp_skb->sender_hw, rtwvif_link->mac_addr); arp_skb->sender_ip = rtwvif->ip_addr; + ether_addr_copy(arp_skb->target_hw, rtwvif_link->mac_addr); + arp_skb->target_ip = rtwvif->ip_addr; return skb; } @@ -9617,7 +9619,7 @@ int rtw89_fw_h2c_keep_alive(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtw if (enable) { ret = rtw89_fw_h2c_add_general_pkt(rtwdev, rtwvif_link, - RTW89_PKT_OFLD_TYPE_NULL_DATA, + RTW89_PKT_OFLD_TYPE_ARP_RSP, &pkt_id); if (ret) return -EPERM; From 63ccdfac8677387dfdbd9d4336089e9823280704 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Fri, 15 May 2026 09:44:29 +0800 Subject: [PATCH 085/285] wifi: rtw89: correct drop logic for malformed AMPDU frames The previous commit aims to fix issue caused by malformed AMPDU frames. But the drop logic fails to deal with the first AMPDU packet paired with certain range of sequence number, and leads to unexpected packet drop. It is more likely to encounter this failure when there are busy traffic during rekey process and could lead to disconnection from the AP. Fix this by adding a initial state judgement and only reset status during pairwise rekey. Fixes: bda294ed0ed0 ("wifi: rtw89: Drop malformed AMPDU frames with abnormal PN") Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 3 ++- drivers/net/wireless/realtek/rtw89/mac80211.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 7b26aedc0883..0f3168d85369 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3948,7 +3948,8 @@ static bool rtw89_core_skb_pn_valid(struct rtw89_dev *rtwdev, last_pn = tid_stats->last_pn; if (pn > last_pn) { - if (ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { + if (last_pn != -1LL && + ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { dev_kfree_skb_any(skb); return false; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 330ece51286d..aade5c5b79e8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -979,7 +979,8 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, rtw89_err(rtwdev, "failed to add key to sec cam\n"); return ret; } - rtw89_core_tid_rx_stats_reset(rtwdev); + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + rtw89_core_tid_rx_stats_reset(rtwdev); break; case DISABLE_KEY: flush_work(&rtwdev->txq_work); From f98b4684930f631d878bdcf412196fa570692f32 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:30 +0800 Subject: [PATCH 086/285] wifi: rtw89: 8922d: change naming number and update values for WDE/PLE quota The WDE/PLE quota are to configure memory size for TX/RX. Some quota are renamed, and some values are changed. Update them accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 7 ++++--- drivers/net/wireless/realtek/rtw89/mac.h | 7 ++++--- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 18 ++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 6153162ad9b0..8df1b9af719d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1847,18 +1847,19 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_qt28 = {1040, 0, 32, 48, 43, 13, 208, 0, 62, 14, 24, 0,}, /* USB 52C USB3.0 */ .ple_qt42 = {1068, 0, 16, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, - .ple_qt42_v2 = {91, 91, 32, 16, 19, 13, 91, 91, 44, 18, 1, 4, 0, 0,}, /* USB 52C USB3.0 */ .ple_qt43 = {3068, 0, 32, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, - .ple_qt43_v2 = {645, 645, 32, 16, 2062, 2056, 2134, 2134, 2087, 2061, 1, 2047, 0, 0,}, /* DLFW 52C */ .ple_qt44 = {0, 0, 16, 256, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_qt44_v2 = {91, 91, 32, 16, 19, 13, 91, 91, 44, 18, 1, 4, 0, 0,}, /* DLFW 52C */ .ple_qt45 = {0, 0, 32, 256, 0, 0, 0, 0, 0, 0, 0, 0,}, + .ple_qt45_v2 = {645, 645, 32, 2055, 2058, 2052, 2132, 2132, 2083, 2057, 1, 2043, 0, 0,}, /* 8852C PCIE SCC */ .ple_qt46 = {525, 0, 16, 20, 13, 13, 178, 0, 32, 62, 8, 16,}, /* 8852C PCIE SCC */ .ple_qt47 = {525, 0, 32, 20, 1034, 13, 1199, 0, 1053, 62, 160, 1037,}, + .ple_qt47_v2 = {0, 0, 32, 2703, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,}, .ple_qt57 = {147, 0, 16, 20, 13, 13, 178, 0, 32, 14, 8, 0,}, /* PCIE 64 */ .ple_qt58 = {147, 0, 16, 20, 157, 13, 229, 0, 172, 14, 24, 0,}, @@ -1889,7 +1890,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0,}, - .dle_input18 = {128, 128, 11454, 2048, 0, 2048, 24, 24,}, + .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 24, 24,}, }; EXPORT_SYMBOL(rtw89_mac_size); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 69f8d7d818a0..3d57f0acfba1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1007,13 +1007,14 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt27; const struct rtw89_ple_quota ple_qt28; const struct rtw89_ple_quota ple_qt42; - const struct rtw89_ple_quota ple_qt42_v2; const struct rtw89_ple_quota ple_qt43; - const struct rtw89_ple_quota ple_qt43_v2; const struct rtw89_ple_quota ple_qt44; + const struct rtw89_ple_quota ple_qt44_v2; const struct rtw89_ple_quota ple_qt45; + const struct rtw89_ple_quota ple_qt45_v2; const struct rtw89_ple_quota ple_qt46; const struct rtw89_ple_quota ple_qt47; + const struct rtw89_ple_quota ple_qt47_v2; const struct rtw89_ple_quota ple_qt57; const struct rtw89_ple_quota ple_qt58; const struct rtw89_ple_quota ple_qt59; @@ -1037,7 +1038,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_rsvd_size rsvd1_size0; const struct rtw89_dle_rsvd_size rsvd1_size2; const struct rtw89_dle_input dle_input3; - const struct rtw89_dle_input dle_input18; + const struct rtw89_dle_input dle_input20; }; extern const struct rtw89_mac_size_set rtw89_mac_size; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index c6c37e25e4c8..c1ce507a250a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -37,9 +37,7 @@ static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { {0, 601, 0}, /* B0HIQ */ {2, 603, 0}, /* B1MGQ */ {0, 601, 0}, /* B1HIQ */ - {0, 0, 0}, /* FWCMDQ */ - {0, 0, 0}, /* BMC */ - {0, 0, 0}, /* H2D */ + {0, 0, 0}, /* GCQ */ }; static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { @@ -62,20 +60,20 @@ static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size16_v1, &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, - &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt42_v2, - &rtw89_mac_size.ple_qt43_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt44_v2, + &rtw89_mac_size.ple_qt45_v2, &rtw89_mac_size.ple_rsvd_qt9, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, - &rtw89_mac_size.dle_input18}, + &rtw89_mac_size.dle_input20}, [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size16_v1, &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, - &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt42_v2, - &rtw89_mac_size.ple_qt43_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt44_v2, + &rtw89_mac_size.ple_qt45_v2, &rtw89_mac_size.ple_rsvd_qt9, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, - &rtw89_mac_size.dle_input18}, + &rtw89_mac_size.dle_input20}, [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18_v1, &rtw89_mac_size.ple_size22_v1, &rtw89_mac_size.wde_qt3, &rtw89_mac_size.wde_qt3, &rtw89_mac_size.ple_qt5_v2, - &rtw89_mac_size.ple_qt5_v2, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.ple_qt47_v2, &rtw89_mac_size.ple_rsvd_qt1, &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, &rtw89_mac_size.dle_input3}, [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, From d270863883b93fa058f36f57a261b4a518642b78 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:31 +0800 Subject: [PATCH 087/285] wifi: rtw89: mac: add field of release report size to DLE quota The release report size is defined to indicate quota for TX completion. Define the field as vendor driver does. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4666e3041ef3..aaef2c8b35be 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4167,6 +4167,7 @@ struct rtw89_dle_input { u32 h2c_max_size; u32 rx_amsdu_size; /* unit: KB */ u32 c2h_max_size; + u32 rls_rpt_max_size; u32 mpdu_info_tbl_b0; u32 mpdu_info_tbl_b1; }; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 8df1b9af719d..5c22b2accf7c 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1889,8 +1889,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .rsvd0_size6 = {40960, 0,}, .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, - .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0,}, - .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 24, 24,}, + .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0, 0,}, + .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 1024, 24, 24,}, }; EXPORT_SYMBOL(rtw89_mac_size); From 77be46f2819c141c6f970156e155fcfaf4c8dddd Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:32 +0800 Subject: [PATCH 088/285] wifi: rtw89: mac: consolidate quota into a struct for variant chips RTL8922D has many variants, using different quota tables. Consolidate the quota data into a struct, and then select corresponding table for the chip variant. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 20 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/mac.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 6 ++++-- .../net/wireless/realtek/rtw89/rtw8852bt.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 6 ++++-- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 7 +++++-- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 7 +++++-- 9 files changed, 52 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index aaef2c8b35be..27d9a916d58c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4239,6 +4239,11 @@ struct rtw89_fw_def { u16 fw_b_aid; }; +struct rtw89_qta_def { + const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_DLE_TYPE_NUM]; + const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM]; +}; + struct rtw89_phy_table { const struct rtw89_reg2_def *regs; u32 n_regs; @@ -4654,8 +4659,7 @@ struct rtw89_chip_info { u16 max_rx_agg_num; bool dis_2g_40m_ul_ofdma; u32 rsvd_ple_ofst; - const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_DLE_TYPE_NUM]; - const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM]; + struct rtw89_qta_def qta_def; u8 wde_qempty_acq_grpnum; u8 wde_qempty_mgq_grpsel; u32 rf_base_addr[2]; @@ -4784,6 +4788,7 @@ struct rtw89_chip_variant { bool no_mcs_12_13: 1; u32 fw_min_ver_code; const struct rtw89_fw_def *fw_def_override; + const struct rtw89_qta_def *qta_def_override; }; union rtw89_bus_info { @@ -7743,6 +7748,17 @@ const struct rtw89_fw_def *rtw89_chip_get_fw_def(struct rtw89_dev *rtwdev) return __rtw89_chip_get_fw_def(rtwdev->chip, rtwdev->variant); } +static inline +const struct rtw89_qta_def *rtw89_chip_get_qta_def(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_variant *variant = rtwdev->variant; + + if (variant && variant->qta_def_override) + return variant->qta_def_override; + + return &rtwdev->chip->qta_def; +} + static inline void rtw89_load_txpwr_table(struct rtw89_dev *rtwdev, const struct rtw89_txpwr_table *tbl) { diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 5c22b2accf7c..bd606d1da634 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -894,11 +894,12 @@ EXPORT_SYMBOL(rtw89_mac_set_err_status); static int hfc_reset_param(struct rtw89_dev *rtwdev) { + const struct rtw89_qta_def *qta_def = rtw89_chip_get_qta_def(rtwdev); const struct rtw89_hfc_param_ini *param_ini, *param_inis; struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; u8 qta_mode = rtwdev->mac.dle_info.qta_mode; - param_inis = rtwdev->chip->hfc_param_ini[rtwdev->hci.dle_type]; + param_inis = qta_def->hfc_param_ini[rtwdev->hci.dle_type]; if (!param_inis) return -EINVAL; @@ -1897,10 +1898,11 @@ EXPORT_SYMBOL(rtw89_mac_size); static const struct rtw89_dle_mem *get_dle_mem_cfg(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode) { + const struct rtw89_qta_def *qta_def = rtw89_chip_get_qta_def(rtwdev); struct rtw89_mac_info *mac = &rtwdev->mac; const struct rtw89_dle_mem *cfg, *cfgs; - cfgs = rtwdev->chip->dle_mem[rtwdev->hci.dle_type]; + cfgs = qta_def->dle_mem[rtwdev->hci.dle_type]; if (!cfgs) return NULL; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 2fb54e1fc7ab..60f362593696 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2648,14 +2648,16 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, - .hfc_param_ini = {rtw8851b_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8851b_hfc_param_ini_pcie, rtw8851b_hfc_param_ini_usb, rtw8851b_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8851b_dle_mem_pcie, + .dle_mem = {rtw8851b_dle_mem_pcie, rtw8851b_dle_mem_usb2, rtw8851b_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index d3b6e8a7f5bd..2c1f166e687f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2385,14 +2385,16 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, rtw8852a_hfc_param_ini_usb, rtw8852a_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852a_dle_mem_pcie, + .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}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 989ac38fe359..4e7b068aaa75 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -979,14 +979,16 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x2f800, - .hfc_param_ini = {rtw8852b_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852b_hfc_param_ini_pcie, rtw8852b_hfc_param_ini_usb, rtw8852b_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852b_dle_mem_pcie, + .dle_mem = {rtw8852b_dle_mem_pcie, rtw8852b_dle_mem_usb3, rtw8852b_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index b58561e141cb..7fcc877f6ea0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -825,8 +825,10 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = true, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL, NULL}, - .dle_mem = {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL}, + .qta_def = { + .hfc_param_ini = {rtw8852bt_hfc_param_ini_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9ecd140dcdd5..7bb1264bcaef 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3174,14 +3174,16 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x6f800, - .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, rtw8852c_hfc_param_ini_usb, rtw8852c_hfc_param_ini_usb, NULL}, - .dle_mem = {rtw8852c_dle_mem_pcie, + .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}, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 07270e83b778..ad3618dfd57d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3169,14 +3169,16 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .max_rx_agg_num = 64, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x8f800, - .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, + .qta_def = { + .hfc_param_ini = {rtw8922a_hfc_param_ini_pcie, rtw8922a_hfc_param_ini_usb2, rtw8922a_hfc_param_ini_usb3, NULL}, - .dle_mem = {rtw8922a_dle_mem_pcie, + .dle_mem = {rtw8922a_dle_mem_pcie, rtw8922a_dle_mem_usb2, rtw8922a_dle_mem_usb3, NULL}, + }, .wde_qempty_acq_grpnum = 4, .wde_qempty_mgq_grpsel = 4, .rf_base_addr = {0xe000, 0xf000}, @@ -3307,6 +3309,7 @@ const struct rtw89_chip_variant rtw8922ae_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 35, 54, 0), .fw_def_override = NULL, + .qta_def_override = NULL, }; EXPORT_SYMBOL(rtw8922ae_vs_variant); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index c1ce507a250a..fe3b0dc2a027 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3171,8 +3171,10 @@ const struct rtw89_chip_info rtw8922d_chip_info = { .max_rx_agg_num = 256, .dis_2g_40m_ul_ofdma = false, .rsvd_ple_ofst = 0x5f800, - .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL, NULL}, - .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, + .qta_def = { + .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL, NULL}, + .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, + }, .wde_qempty_acq_grpnum = 8, .wde_qempty_mgq_grpsel = 8, .rf_base_addr = {0x3e000, 0x3f000}, @@ -3308,6 +3310,7 @@ const struct rtw89_chip_variant rtw8922de_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 0, 0, 0), .fw_def_override = &rtw8922de_vs_fw_def, + .qta_def_override = NULL, }; EXPORT_SYMBOL(rtw8922de_vs_variant); From 9d69ffa4f8da8257e247a4b7163e0aae4b624d7e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 15 May 2026 09:44:33 +0800 Subject: [PATCH 089/285] wifi: rtw89: 8922d: add quota for RTL8922DE variant The existing quota set is for RTL8922DE-VS, so rename the existing tables. Add new quota set for RTL8922DE, containing more memory to yield better performance, so rearrange quota accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260515014433.16168-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 8 ++ drivers/net/wireless/realtek/rtw89/mac.h | 8 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 73 +++++++++++++++++-- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index bd606d1da634..8c395517bd2f 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1748,6 +1748,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_size18_v1 = {RTW89_WDE_PG_64, 0, 640, 0,}, /* 8852C PCIE SCC */ .wde_size19 = {RTW89_WDE_PG_64, 3328, 0,}, + .wde_size22_v1 = {RTW89_WDE_PG_128, 384, 0, 0,}, .wde_size23 = {RTW89_WDE_PG_64, 1022, 2,}, /* 8852B USB2.0/USB3.0 SCC turbo */ .wde_size30 = {RTW89_WDE_PG_64, 220, 36,}, @@ -1781,6 +1782,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_size22_v1 = {RTW89_PLE_PG_128, 2736, 0, 40960,}, /* 8851B USB2.0 SCC turbo */ .ple_size27 = {RTW89_PLE_PG_128, 1396, 12,}, + .ple_size29_v1 = {RTW89_PLE_PG_128, 1895, 182, 49152,}, /* 8852B USB3.0 SCC turbo */ .ple_size31 = {RTW89_PLE_PG_128, 1392, 16,}, /* 8852C USB2.0 */ @@ -1811,6 +1813,7 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt18 = {3228, 60, 0, 40,}, .wde_qt19_v1 = {613, 6, 0, 20,}, .wde_qt23 = {958, 48, 0, 16,}, + .wde_qt23_v1 = {371, 3, 0, 10,}, /* 8852B USB2.0/USB3.0 SCC turbo */ .wde_qt30 = {210, 2, 0, 8,}, /* 8852C USB2.0 */ @@ -1868,6 +1871,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* 8851B USB2.0 SCC turbo */ .ple_qt61 = {858, 0, 16, 48, 4, 13, 370, 0, 32, 14, 8, 0, 0,}, .ple_qt62 = {858, 0, 32, 48, 37, 13, 403, 0, 65, 14, 24, 0, 0,}, + .ple_qt64_v2 = {91, 91, 32, 16, 19, 13, 93, 93, 44, 34, 1, 4, 0, 0,}, + .ple_qt65_v2 = {645, 645, 32, 1380, 1383, 1377, 1457, 1457, 1408, 1398, 1, 1368,}, /* USB2.0 52C */ .ple_qt78 = {1560, 0, 16, 48, 13, 13, 390, 0, 32, 38, 8, 16, 0,}, /* USB2.0 52C */ @@ -1888,10 +1893,13 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .rsvd0_size3 = {57344, 0,}, .rsvd0_size5 = {32768, 0,}, .rsvd0_size6 = {40960, 0,}, + .rsvd0_size17 = {49152, 0,}, .rsvd1_size0 = {587776, 2048,}, .rsvd1_size2 = {391168, 2048,}, + .rsvd1_size3 = {315008, 78208,}, .dle_input3 = {0, 0, 0, 16384, 0, 2048, 0, 0, 0,}, .dle_input20 = {128, 128, 11454, 2048, 0, 2048, 1024, 24, 24,}, + .dle_input28 = {128, 128, 11454, 2048, 0, 2048, 2048, 24, 24,}, }; EXPORT_SYMBOL(rtw89_mac_size); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 3d57f0acfba1..539061fc15e8 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -946,6 +946,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size wde_size18; const struct rtw89_dle_size wde_size18_v1; const struct rtw89_dle_size wde_size19; + const struct rtw89_dle_size wde_size22_v1; const struct rtw89_dle_size wde_size23; const struct rtw89_dle_size wde_size30; const struct rtw89_dle_size wde_size31; @@ -966,6 +967,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size ple_size20_v1; const struct rtw89_dle_size ple_size22_v1; const struct rtw89_dle_size ple_size27; + const struct rtw89_dle_size ple_size29_v1; const struct rtw89_dle_size ple_size31; const struct rtw89_dle_size ple_size34; const struct rtw89_wde_quota wde_qt0; @@ -984,6 +986,7 @@ struct rtw89_mac_size_set { const struct rtw89_wde_quota wde_qt18; const struct rtw89_wde_quota wde_qt19_v1; const struct rtw89_wde_quota wde_qt23; + const struct rtw89_wde_quota wde_qt23_v1; const struct rtw89_wde_quota wde_qt30; const struct rtw89_wde_quota wde_qt31; const struct rtw89_ple_quota ple_qt0; @@ -1020,6 +1023,8 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt59; const struct rtw89_ple_quota ple_qt61; const struct rtw89_ple_quota ple_qt62; + const struct rtw89_ple_quota ple_qt64_v2; + const struct rtw89_ple_quota ple_qt65_v2; const struct rtw89_ple_quota ple_qt78; const struct rtw89_ple_quota ple_qt79; const struct rtw89_ple_quota ple_qt_52a_wow; @@ -1035,10 +1040,13 @@ struct rtw89_mac_size_set { const struct rtw89_dle_rsvd_size rsvd0_size3; const struct rtw89_dle_rsvd_size rsvd0_size5; const struct rtw89_dle_rsvd_size rsvd0_size6; + const struct rtw89_dle_rsvd_size rsvd0_size17; const struct rtw89_dle_rsvd_size rsvd1_size0; const struct rtw89_dle_rsvd_size rsvd1_size2; + const struct rtw89_dle_rsvd_size rsvd1_size3; const struct rtw89_dle_input dle_input3; const struct rtw89_dle_input dle_input20; + const struct rtw89_dle_input dle_input28; }; extern const struct rtw89_mac_size_set rtw89_mac_size; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index fe3b0dc2a027..37d3bc1eafad 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -25,6 +25,62 @@ RTW89_GEN_MODULE_FWNAME(RTW8922DS_FW_BASENAME, RTW8922DS_FW_FORMAT_MAX) static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { + {2, 732, 0}, /* ACH 0 */ + {0, 730, 0}, /* ACH 1 */ + {2, 732, 0}, /* ACH 2 */ + {0, 730, 0}, /* ACH 3 */ + {2, 732, 0}, /* ACH 4 */ + {0, 730, 0}, /* ACH 5 */ + {2, 732, 0}, /* ACH 6 */ + {0, 730, 0}, /* ACH 7 */ + {2, 732, 0}, /* B0MGQ */ + {0, 730, 0}, /* B0HIQ */ + {2, 732, 0}, /* B1MGQ */ + {0, 730, 0}, /* B1HIQ */ + {0, 0, 0}, /* GCQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { + 742, /* Group 0 */ + 0, /* Group 1 */ + 742, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DBCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + +static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size22_v1, + &rtw89_mac_size.ple_size29_v1, &rtw89_mac_size.wde_qt23_v1, + &rtw89_mac_size.wde_qt23_v1, &rtw89_mac_size.ple_qt64_v2, + &rtw89_mac_size.ple_qt65_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.rsvd0_size17, &rtw89_mac_size.rsvd1_size3, + &rtw89_mac_size.dle_input28}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size22_v1, + &rtw89_mac_size.ple_size29_v1, &rtw89_mac_size.wde_qt23_v1, + &rtw89_mac_size.wde_qt23_v1, &rtw89_mac_size.ple_qt64_v2, + &rtw89_mac_size.ple_qt65_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.rsvd0_size17, &rtw89_mac_size.rsvd1_size3, + &rtw89_mac_size.dle_input28}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18_v1, + &rtw89_mac_size.ple_size22_v1, &rtw89_mac_size.wde_qt3, + &rtw89_mac_size.wde_qt3, &rtw89_mac_size.ple_qt5_v2, + &rtw89_mac_size.ple_qt47_v2, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, + &rtw89_mac_size.dle_input3}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const struct rtw89_hfc_ch_cfg rtw8922ds_hfc_chcfg_pcie[] = { {2, 603, 0}, /* ACH 0 */ {0, 601, 0}, /* ACH 1 */ {2, 603, 0}, /* ACH 2 */ @@ -40,24 +96,24 @@ static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { {0, 0, 0}, /* GCQ */ }; -static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { +static const struct rtw89_hfc_pub_cfg rtw8922ds_hfc_pubcfg_pcie = { 613, /* Group 0 */ 0, /* Group 1 */ 613, /* Public Max */ 0, /* WP threshold */ }; -static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { - [RTW89_QTA_SCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, +static const struct rtw89_hfc_param_ini rtw8922ds_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8922ds_hfc_chcfg_pcie, &rtw8922ds_hfc_pubcfg_pcie, &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, - [RTW89_QTA_DBCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + [RTW89_QTA_DBCC] = {rtw8922ds_hfc_chcfg_pcie, &rtw8922ds_hfc_pubcfg_pcie, &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, RTW89_HCIFC_POH}, [RTW89_QTA_INVALID] = {NULL}, }; -static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { +static const struct rtw89_dle_mem rtw8922ds_dle_mem_pcie[] = { [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size16_v1, &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt44_v2, @@ -3306,11 +3362,16 @@ static const struct rtw89_fw_def rtw8922de_vs_fw_def = { .fw_b_aid = RTL8922D_AID7060, }; +static const struct rtw89_qta_def rtw8922ds_qta_def = { + .hfc_param_ini = {rtw8922ds_hfc_param_ini_pcie, NULL, NULL}, + .dle_mem = {rtw8922ds_dle_mem_pcie, NULL, NULL, NULL}, +}; + const struct rtw89_chip_variant rtw8922de_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 0, 0, 0), .fw_def_override = &rtw8922de_vs_fw_def, - .qta_def_override = NULL, + .qta_def_override = &rtw8922ds_qta_def, }; EXPORT_SYMBOL(rtw8922de_vs_variant); From cc9cdac8009979e5b77a9d05841ab2d9ca03e68e Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 16 May 2026 15:32:12 +0300 Subject: [PATCH 090/285] wifi: rtw89: Add missing TX queue mappings for RTL8922AU A user with MLO-capable router reports: [ 753.080409] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 [ 753.080417] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 [ 813.649426] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 [ 813.649445] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 [ 813.855983] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26 Map RTW89_TX_QSEL_B1_MGMT and RTW89_TX_QSEL_B1_HI to RTW89_TXCH_CH10. This is probably the right channel for these queues. This function is also used for RTL8852AU and RTL8852CU, but those currently aren't used in DBCC mode with rtw89 so it makes no difference for them. Link: https://github.com/morrownr/rtw89/issues/83#issuecomment-4314735734 Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/0c8e0cf1-13e9-4c67-a67f-5f6f79fd0658@gmail.com --- drivers/net/wireless/realtek/rtw89/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 0f3168d85369..c1df3e2ba11e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -854,6 +854,9 @@ u8 rtw89_core_get_ch_dma_v2(struct rtw89_dev *rtwdev, u8 qsel) case RTW89_TX_QSEL_B0_MGMT: case RTW89_TX_QSEL_B0_HI: return RTW89_TXCH_CH8; + case RTW89_TX_QSEL_B1_MGMT: + case RTW89_TX_QSEL_B1_HI: + return RTW89_TXCH_CH10; } } EXPORT_SYMBOL(rtw89_core_get_ch_dma_v2); From c80788f7c5aed8d420366b821f867a8a353d83a5 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Mon, 18 May 2026 16:23:10 +0200 Subject: [PATCH 091/285] wifi: rtw88: increase TX report timeout to fix race condition The driver expects the firmware to report TX status within 500ms. However, a timeout can be triggered when the hardware performs background scans while under TX load. During these scans, the firmware stays off-channel for periods exceeding 500ms, delaying the delivery of TX reports back to the driver. When this occurs, the purge timer fires prematurely and drops the tracking skbs from the queue. This results in the host stack interpreting the missing status as packet loss, leading to TCP window collapse. In testing with iperf3, this causes throughput to drop from ~90 Mbps to near-zero for approximately 2 seconds until the connection recovers. Increase RTW_TX_PROBE_TIMEOUT to 2500ms for RTL8723DU. This duration is sufficient to accommodate off-channel dwell time during full background scans, ensuring the purge timer only trips during genuine firmware lockups and preventing unnecessary TCP retransmission cycles. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Cc: stable@vger.kernel.org Acked-by: Ping-Ke Shih Tested-by: Luka Gejak Signed-off-by: Luka Gejak Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260518142311.10328-1-luka.gejak@linux.dev --- drivers/net/wireless/realtek/rtw88/tx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index bea78971141e..9d747a060b98 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -196,6 +196,7 @@ void rtw_tx_report_purge_timer(struct timer_list *t) void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) { struct rtw_tx_report *tx_report = &rtwdev->tx_report; + unsigned long timeout = RTW_TX_PROBE_TIMEOUT; unsigned long flags; u8 *drv_data; @@ -207,7 +208,11 @@ void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) __skb_queue_tail(&tx_report->queue, skb); spin_unlock_irqrestore(&tx_report->q_lock, flags); - mod_timer(&tx_report->purge_timer, jiffies + RTW_TX_PROBE_TIMEOUT); + if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D && + rtwdev->hci.type == RTW_HCI_TYPE_USB) + timeout = msecs_to_jiffies(2500); + + mod_timer(&tx_report->purge_timer, jiffies + timeout); } EXPORT_SYMBOL(rtw_tx_report_enqueue); From 6b964941bbfe6e0f18b1a5e008486dbb62df440a Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Mon, 18 May 2026 16:23:11 +0200 Subject: [PATCH 092/285] wifi: rtw88: usb: fix memory leaks on USB write failures When rtw_usb_write_port() fails to submit a USB Request Block (URB) (e.g., due to device disconnect or ENOMEM), the completion callback is never executed. Currently, the driver ignores the return value of rtw_usb_write_port() in rtw_usb_write_data() and rtw_usb_tx_agg_skb(). Because these functions rely on the completion callback to free the socket buffers (skbs) and the transaction control block (txcb), a submission failure results in: 1. A memory leak of the allocated skb in rtw_usb_write_data(). 2. A memory leak of the txcb structure and all aggregated skbs in rtw_usb_tx_agg_skb(). Fix this by checking the return value of rtw_usb_write_port(). If it fails, explicitly free the skb in rtw_usb_write_data(), and properly purge the tx_ack_queue and free the txcb in rtw_usb_tx_agg_skb(). The issue was discovered in practice during device disconnect/reconnect scenarios and memory pressure conditions. Tested by verifying normal TX operation continues after the fix without regressions. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Cc: stable@vger.kernel.org Acked-by: Ping-Ke Shih Tested-by: Luka Gejak Signed-off-by: Luka Gejak Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260518142311.10328-2-luka.gejak@linux.dev --- drivers/net/wireless/realtek/rtw88/usb.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 718940ebba31..1a0bdbf52cb0 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -399,6 +399,7 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list int agg_num = 0; unsigned int align_next = 0; u8 qsel; + int ret; if (skb_queue_empty(list)) return false; @@ -456,7 +457,13 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list tx_desc = (struct rtw_tx_desc *)skb_head->data; qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL); - rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb); + ret = rtw_usb_write_port(rtwdev, qsel, skb_head, + rtw_usb_write_port_tx_complete, txcb); + if (ret) { + ieee80211_purge_tx_queue(rtwdev->hw, &txcb->tx_ack_queue); + kfree(txcb); + return false; + } return true; } @@ -518,8 +525,10 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev, ret = rtw_usb_write_port(rtwdev, qsel, skb, rtw_usb_write_port_complete, skb); - if (unlikely(ret)) + if (unlikely(ret)) { rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret); + dev_kfree_skb_any(skb); + } return ret; } From f50cf5f5d6a3f0f13a8f1af26a77200d9f6b2e6d Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 20 May 2026 20:38:17 +0800 Subject: [PATCH 093/285] wifi: rtw89: fw: load TX power track element according to AID RF parameters has different TX power track table for different AID. FW elements may include multiple TX power track tables for different AID. So, load the corresponding one. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index eb5683d64461..3e6161743015 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1209,12 +1209,17 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev, { struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_hal *hal = &rtwdev->hal; + u16 aid = le16_to_cpu(elm->aid); u32 needed_bitmap = 0; u32 offset = 0; int subband; u32 bitmap; int type; + if (aid && aid != hal->aid) + return 1; + if (chip->support_bands & BIT(NL80211_BAND_6GHZ)) needed_bitmap |= RTW89_DEFAULT_NEEDED_FW_TXPWR_TRK_6GHZ; if (chip->support_bands & BIT(NL80211_BAND_5GHZ)) From 57efc7b096a6ff97d1bf69a832b27fec5c1c4e9e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 20 May 2026 20:38:18 +0800 Subject: [PATCH 094/285] wifi: rtw89: 8922d: refactor digital power compensation to support new format Because base settings of digital power compensation can be shared across all bands, the settings are divided into two parts -- base and individual values per bands. Refactor the code to be reuse with new format. No change logic at all. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 37d3bc1eafad..35014b5144fe 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2172,29 +2172,42 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, enum rtw89_phy_idx phy_idx) { -#define DIGITAL_PWR_COMP_REG_NUM 22 +#define DIGITAL_PWR_COMP_BASE_NUM 7 +#define DIGITAL_PWR_COMP_VALS_NUM 15 static const u32 pw_comp_cr[2] = {R_RX_PATH0_TBL0_BE4, R_RX_PATH1_TBL0_BE4}; - const __le32 (*pwr_comp_val)[2][RTW89_TX_COMP_BAND_NR] - [BB_PATH_NUM_8922D][DIGITAL_PWR_COMP_REG_NUM]; + const struct { + struct { + __le32 base[DIGITAL_PWR_COMP_BASE_NUM]; + __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; + } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; + } *pwr_comp_v0; struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; const struct rtw89_fw_element_hdr *txcomp_elm = elm_info->tx_comp; - const __le32 *digital_pwr_comp; + const __le32 (*comp_base)[DIGITAL_PWR_COMP_BASE_NUM]; + const __le32 (*comp_vals)[DIGITAL_PWR_COMP_VALS_NUM]; u32 addr, val; u32 i; - if (sizeof(*pwr_comp_val) != le32_to_cpu(txcomp_elm->size)) { + if (sizeof(*pwr_comp_v0) == le32_to_cpu(txcomp_elm->size)) { + pwr_comp_v0 = (const void *)txcomp_elm->u.common.contents; + comp_base = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].base; + comp_vals = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].vals; + } else { rtw89_debug(rtwdev, RTW89_DBG_UNEXP, "incorrect power comp size %d\n", le32_to_cpu(txcomp_elm->size)); return; } - pwr_comp_val = (const void *)txcomp_elm->u.common.contents; - digital_pwr_comp = (*pwr_comp_val)[nss][chan->tx_comp_band][path]; addr = pw_comp_cr[path]; - for (i = 0; i < DIGITAL_PWR_COMP_REG_NUM; i++, addr += 4) { - val = le32_to_cpu(digital_pwr_comp[i]); + for (i = 0; i < ARRAY_SIZE(*comp_base); i++, addr += 4) { + val = le32_to_cpu((*comp_base)[i]); + rtw89_phy_write32_idx(rtwdev, addr, MASKDWORD, val, phy_idx); + } + + for (i = 0; i < ARRAY_SIZE(*comp_vals); i++, addr += 4) { + val = le32_to_cpu((*comp_vals)[i]); rtw89_phy_write32_idx(rtwdev, addr, MASKDWORD, val, phy_idx); } } From c77a155c96d7818626ad24b49aa50bc376af547b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 20 May 2026 20:38:19 +0800 Subject: [PATCH 095/285] wifi: rtw89: 8922d: support new digital power compensation format The new format shared the base set (7 elements). As there are total 40 sets, save 7 * 39 elements. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 35014b5144fe..795efed6e683 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2181,6 +2181,12 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; } *pwr_comp_v0; + const struct { + __le32 base[DIGITAL_PWR_COMP_BASE_NUM]; + struct { + __le32 vals[DIGITAL_PWR_COMP_VALS_NUM]; + } sets[2][RTW89_TX_COMP_BAND_NR][BB_PATH_NUM_8922D]; + } *pwr_comp; struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; const struct rtw89_fw_element_hdr *txcomp_elm = elm_info->tx_comp; const __le32 (*comp_base)[DIGITAL_PWR_COMP_BASE_NUM]; @@ -2188,7 +2194,11 @@ static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, u32 addr, val; u32 i; - if (sizeof(*pwr_comp_v0) == le32_to_cpu(txcomp_elm->size)) { + if (sizeof(*pwr_comp) == le32_to_cpu(txcomp_elm->size)) { + pwr_comp = (const void *)txcomp_elm->u.common.contents; + comp_base = &pwr_comp->base; + comp_vals = &pwr_comp->sets[nss][chan->tx_comp_band][path].vals; + } else if (sizeof(*pwr_comp_v0) == le32_to_cpu(txcomp_elm->size)) { pwr_comp_v0 = (const void *)txcomp_elm->u.common.contents; comp_base = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].base; comp_vals = &pwr_comp_v0->sets[nss][chan->tx_comp_band][path].vals; From 89ab7535e428afe31d48fd2edd39fee5badb2456 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 20 May 2026 20:38:20 +0800 Subject: [PATCH 096/285] wifi: rtw89: fw: load TX compensation element by RFE type Originally driver uses TX compensation element by AID, and now tables are by RFE type. Add the logic accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 6 ++++++ drivers/net/wireless/realtek/rtw89/fw.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 3e6161743015..d6a594b75ab2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1423,12 +1423,18 @@ int rtw89_build_tx_comp_from_elm(struct rtw89_dev *rtwdev, const union rtw89_fw_element_arg arg) { struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_hal *hal = &rtwdev->hal; + u8 rfe_type; u16 aid; aid = le16_to_cpu(elm->aid); + rfe_type = elm->u.tx_comp.rfe_type; + if (aid && aid != hal->aid) return 1; /* ignore if aid not matched */ + else if (rfe_type && rfe_type != efuse->rfe_type) + return 1; /* ignore if rfe_type not matched */ else if (elm_info->tx_comp) return 1; /* ignore if an element is existing */ diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index cde8fd34723b..20721d5209aa 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4548,6 +4548,11 @@ struct rtw89_fw_element_hdr { u8 rsvd[4]; u8 rules_and_msgs[]; } __packed diag_mac; + struct { + u8 rfe_type; + u8 priv[7]; + u8 contents[]; + } __packed tx_comp; struct __rtw89_fw_txpwr_element txpwr; struct __rtw89_fw_regd_element regd; } __packed u; From ed438b312d0758eaac868ab1275e743709fa4dac Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 20 May 2026 20:38:21 +0800 Subject: [PATCH 097/285] wifi: rtw89: Wi-Fi 7 configure TX power limit for large MRU Support of Large MRU (Multiple Resource Unit) starts from RTL8922D_CID7090, i.e. RTL8922A and RTL8922D-VS variant do not support it. There are the new corresponding control registers. So, configure them. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 3 + drivers/net/wireless/realtek/rtw89/phy.h | 10 ++ drivers/net/wireless/realtek/rtw89/phy_be.c | 163 ++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/reg.h | 9 ++ 4 files changed, 185 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 27d9a916d58c..fb32d2bd9cb0 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -998,6 +998,9 @@ enum rtw89_ru_bandwidth { RTW89_RU106 = 2, RTW89_RU52_26 = 3, RTW89_RU106_26 = 4, + RTW89_RU484_242 = 5, + RTW89_RU996_484 = 6, + RTW89_RU996_484_242 = 7, RTW89_RU_NUM, }; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 5af66656d0b8..532232892831 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -568,6 +568,16 @@ struct rtw89_txpwr_limit_ru_be { s8 ru106_26[RTW89_RU_SEC_NUM_BE]; }; +#define RTW89_RU484_242_SEC_NUM_BE 4 +#define RTW89_RU996_484_SEC_NUM_BE 2 +#define RTW89_RU996_484_242_SEC_NUM_BE 2 + +struct rtw89_txpwr_limit_large_mru_be { + s8 ru484_242[RTW89_NSS_NUM][RTW89_RU484_242_SEC_NUM_BE]; + s8 ru996_484[RTW89_NSS_NUM][RTW89_RU996_484_SEC_NUM_BE]; + s8 ru996_484_242[RTW89_NSS_NUM][RTW89_RU996_484_242_SEC_NUM_BE]; +}; + struct rtw89_phy_rfk_log_fmt { const struct rtw89_fw_element_hdr *elm[RTW89_PHY_C2H_RFK_LOG_FUNC_NUM]; }; diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index d0a6dfdbfea7..99263355e2f1 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -1699,11 +1699,168 @@ static void rtw89_phy_fill_limit_ru_be(struct rtw89_dev *rtwdev, } } +static +void rtw89_phy_fill_limit_ru484_242_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU484_242_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_80: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 8); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 8); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 24); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch - 8); + (*lmt)[2] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 8); + (*lmt)[3] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU484_242, + ntx, ch + 24); + break; + } +} + +static +void rtw89_phy_fill_limit_ru996_484_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU996_484_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch - 16); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484, + ntx, ch + 16); + break; + } +} + +static +void rtw89_phy_fill_limit_ru996_484_242_be(struct rtw89_dev *rtwdev, + s8 (*lmt)[RTW89_RU996_484_242_SEC_NUM_BE], + u8 ntx, u8 band, u8 ch, u8 bw) +{ + switch (bw) { + case RTW89_CHANNEL_WIDTH_160: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch); + break; + case RTW89_CHANNEL_WIDTH_320: + (*lmt)[0] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch - 16); + (*lmt)[1] = rtw89_phy_read_txpwr_limit_ru(rtwdev, band, + RTW89_RU996_484_242, + ntx, ch + 16); + break; + } +} + +static +void rtw89_phy_fill_limit_large_mru_be(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + struct rtw89_txpwr_limit_large_mru_be *lmt, + bool has_bf) +{ + u8 band = chan->band_type; + u8 ch = chan->channel; + u8 bw = chan->band_width; + int i; + + memset(lmt, 0, sizeof(*lmt)); + + if (has_bf) + return; + + for (i = 0; i <= RTW89_NSS_2; i++) { + rtw89_phy_fill_limit_ru484_242_be(rtwdev, &lmt->ru484_242[i], + i, band, ch, bw); + rtw89_phy_fill_limit_ru996_484_be(rtwdev, &lmt->ru996_484[i], + i, band, ch, bw); + rtw89_phy_fill_limit_ru996_484_242_be(rtwdev, &lmt->ru996_484_242[i], + i, band, ch, bw); + } +} + +static +void rtw89_phy_conf_limit_large_mru_be(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx, + bool has_bf) +{ + struct rtw89_txpwr_limit_large_mru_be lmt_lmru; + u32 addr, val; + + rtw89_phy_fill_limit_large_mru_be(rtwdev, chan, &lmt_lmru, has_bf); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU484_242_0 : + R_BE_TXAGC_MAX_1TX_RU484_242_0; + + val = u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][2], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_1][3], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); + + val = u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][2], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru484_242[RTW89_NSS_2][3], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr + 4, val); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU996_484_0 : + R_BE_TXAGC_MAX_1TX_RU996_484_0; + + val = u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_2][0], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru996_484[RTW89_NSS_2][1], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); + + addr = has_bf ? R_BE_TXAGC_MAX_1TX_BF_RU996_484_242_0 : + R_BE_TXAGC_MAX_1TX_RU996_484_242_0; + + val = u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_1][0], GENMASK(7, 0)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_1][1], GENMASK(15, 8)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_2][0], GENMASK(23, 16)) | + u32_encode_bits(lmt_lmru.ru996_484_242[RTW89_NSS_2][1], GENMASK(31, 24)); + + rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); +} + static void rtw89_phy_set_txpwr_limit_ru_be(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { + const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_txpwr_limit_ru_be lmt_ru; + struct rtw89_hal *hal = &rtwdev->hal; const s8 *ptr; u32 addr, val; u8 i, j; @@ -1730,6 +1887,12 @@ static void rtw89_phy_set_txpwr_limit_ru_be(struct rtw89_dev *rtwdev, rtw89_mac_txpwr_write32(rtwdev, phy_idx, addr, val); } } + + if (!(chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + return; + + rtw89_phy_conf_limit_large_mru_be(rtwdev, chan, phy_idx, false); + rtw89_phy_conf_limit_large_mru_be(rtwdev, chan, phy_idx, true); } const struct rtw89_phy_gen_def rtw89_phy_gen_be = { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index c2ffa6c196e4..2369fca3ede7 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8362,6 +8362,11 @@ #define R_BE_PWR_LISTEN_PATH 0x11988 #define B_BE_PWR_LISTEN_PATH_EN GENMASK(31, 28) +#define R_BE_TXAGC_MAX_1TX_RU484_242_0 0x11990 +#define R_BE_TXAGC_MAX_1TX_RU996_484_0 0x119A4 +#define R_BE_TXAGC_MAX_1TX_RU996_484_242_0 0x119AC +#define R_BE_TXAGC_MAX_1TX_BF_RU484_242_0 0x119DC + #define R_BE_PWR_REF_CTRL 0x11A20 #define B_BE_PWR_REF_CTRL_OFDM GENMASK(9, 1) #define B_BE_PWR_REF_CTRL_CCK GENMASK(18, 10) @@ -8399,6 +8404,8 @@ B_BE_PWR_FORCE_MACID_EN_VAL | \ B_BE_PWR_FORCE_MACID_EN_ON) +#define R_BE_TXAGC_MAX_1TX_BF_RU996_484_0 0x11A4C + #define R_BE_PWR_REG_CTRL 0x11A50 #define B_BE_PWR_BT_EN BIT(23) @@ -8412,6 +8419,8 @@ #define R_BE_PWR_TH 0x11A78 #define R_BE_PWR_RSSI_TARGET_LMT 0x11A84 +#define R_BE_TXAGC_MAX_1TX_BF_RU996_484_242_0 0x11ADC + #define R_BE_PWR_OFST_SW 0x11AE8 #define B_BE_PWR_OFST_SW_DB GENMASK(27, 24) From a153f9ce94e669b7b6bd78f637c38a9b24d906a6 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 20 May 2026 20:38:22 +0800 Subject: [PATCH 098/285] wifi: rtw89: debug: show large MRU in txpwr_table dbgfs Read and print the CR settings of large MRU. For now, only RTL8922D_CID7090 support it. For example, [TX power limit_large_mru] RU484_242 1TX - IDX_0 IDX_1 IDX_2 IDX_3 | 17, 0, 0, 0, dBm RU484_242 2TX - IDX_0 IDX_1 IDX_2 IDX_3 | 16, 0, 0, 0, dBm RU996_484 1TX - IDX_0 IDX_1 | 0, 0, dBm RU996_484 2TX - IDX_0 IDX_1 | 0, 0, dBm RU996_484_242 1TX - IDX_0 IDX_1 | 0, 0, dBm RU996_484_242 2TX - IDX_0 IDX_1 | 0, 0, dBm Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index c0fbb016cea4..8f5af873e09f 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -767,6 +767,45 @@ static const struct txpwr_map __txpwr_map_lmt_ru_be = { .addr_to_1ss = 0, /* not support */ }; +static const struct txpwr_ent __txpwr_ent_lmt_ru484_242_be[] = { + __GEN_TXPWR_ENT4("RU484_242 1TX ", "IDX_0 ", "IDX_1 ", "IDX_2 ", "IDX_3 "), + __GEN_TXPWR_ENT4("RU484_242 2TX ", "IDX_0 ", "IDX_1 ", "IDX_2 ", "IDX_3 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru484_242_be = { + .ent = __txpwr_ent_lmt_ru484_242_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru484_242_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU484_242_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU484_242_0 + 4, + .addr_to_1ss = 0, /* not support */ +}; + +static const struct txpwr_ent __txpwr_ent_lmt_ru996_484_be[] = { + __GEN_TXPWR_ENT2("RU996_484 1TX ", "IDX_0 ", "IDX_1 "), + __GEN_TXPWR_ENT2("RU996_484 2TX ", "IDX_0 ", "IDX_1 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru996_484_be = { + .ent = __txpwr_ent_lmt_ru996_484_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru996_484_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU996_484_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU996_484_0, + .addr_to_1ss = 0, /* not support */ +}; + +static const struct txpwr_ent __txpwr_ent_lmt_ru996_484_242_be[] = { + __GEN_TXPWR_ENT2("RU996_484_242 1TX ", "IDX_0 ", "IDX_1 "), + __GEN_TXPWR_ENT2("RU996_484_242 2TX ", "IDX_0 ", "IDX_1 "), +}; + +static const struct txpwr_map __txpwr_map_lmt_ru996_484_242_be = { + .ent = __txpwr_ent_lmt_ru996_484_242_be, + .size = ARRAY_SIZE(__txpwr_ent_lmt_ru996_484_242_be), + .addr_from = R_BE_TXAGC_MAX_1TX_RU996_484_242_0, + .addr_to = R_BE_TXAGC_MAX_1TX_RU996_484_242_0, + .addr_to_1ss = 0, /* not support */ +}; + static unsigned int __print_txpwr_ent(char *buf, size_t bufsz, const struct txpwr_ent *ent, const s8 *bufp, const unsigned int cur, unsigned int *ate) @@ -882,6 +921,9 @@ struct dbgfs_txpwr_table { const struct txpwr_map *byr; const struct txpwr_map *lmt; const struct txpwr_map *lmt_ru; + const struct txpwr_map *lmt_ru484_242; + const struct txpwr_map *lmt_ru996_484; + const struct txpwr_map *lmt_ru996_484_242; }; static const struct dbgfs_txpwr_table dbgfs_txpwr_table_ax = { @@ -894,6 +936,9 @@ static const struct dbgfs_txpwr_table dbgfs_txpwr_table_be = { .byr = &__txpwr_map_byr_be, .lmt = &__txpwr_map_lmt_be, .lmt_ru = &__txpwr_map_lmt_ru_be, + .lmt_ru484_242 = &__txpwr_map_lmt_ru484_242_be, + .lmt_ru996_484 = &__txpwr_map_lmt_ru996_484_be, + .lmt_ru996_484_242 = &__txpwr_map_lmt_ru996_484_242_be, }; static const struct dbgfs_txpwr_table *dbgfs_txpwr_tables[RTW89_CHIP_GEN_NUM] = { @@ -934,6 +979,8 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_sar_parm sar_parm = {}; const struct dbgfs_txpwr_table *tbl; const struct rtw89_chan *chan; @@ -979,6 +1026,35 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, return n; p += n; + switch (chip_gen) { + case RTW89_CHIP_AX: + goto out; + case RTW89_CHIP_BE: + if (!(chip->chip_id == RTL8922D && hal->cid == RTL8922D_CID7090)) + goto out; + break; + default: + return -EOPNOTSUPP; + } + + p += scnprintf(p, end - p, "\n[TX power limit_large_mru]\n"); + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru484_242); + if (n < 0) + return n; + p += n; + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru996_484); + if (n < 0) + return n; + p += n; + + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru996_484_242); + if (n < 0) + return n; + p += n; + +out: return p - buf; } From 756abe2ebe3e14922c6e1317c89ce8004e85b6e9 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 20 May 2026 20:38:23 +0800 Subject: [PATCH 099/285] wifi: rtw89: 8922d: configure TX shape settings By default, BB enables triangular spectrum by a series of register settings. According to band and regulation, RF parameters determine whether TX shape needs to be restricted or not. So now, clear the corresponding settings if it has no need to do. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260520123823.1792954-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 61 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 2369fca3ede7..61709182f84c 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10626,6 +10626,8 @@ #define R_RFSI_CT_OPT_0_BE4 0x11A94 #define R_RFSI_CT_OPT_8_BE4 0x11A98 #define R_QAM_COMP_TH0_BE4 0x11A9C +#define B_QAM_COMP_TH_TRIANGULAR_L GENMASK(11, 10) +#define B_QAM_COMP_TH_TRIANGULAR_H GENMASK(27, 26) #define R_QAM_COMP_TH1_BE4 0x11AA0 #define R_QAM_COMP_TH2_BE4 0x11AA4 #define R_QAM_COMP_TH3_BE4 0x11AA8 @@ -10649,6 +10651,8 @@ #define B_QAM_COMP_TH6_2L GENMASK(9, 5) #define B_QAM_COMP_TH6_2M GENMASK(19, 15) #define R_OW_VAL_0_BE4 0x11AAC +#define B_OW_VAL_TRIANGULAR_L GENMASK(11, 10) +#define B_OW_VAL_TRIANGULAR_H GENMASK(27, 26) #define R_OW_VAL_1_BE4 0x11AB0 #define R_OW_VAL_2_BE4 0x11AB4 #define R_OW_VAL_3_BE4 0x11AB8 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 795efed6e683..838c26231897 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2734,6 +2734,66 @@ static void rtw8922d_set_txpwr_ref(struct rtw89_dev *rtwdev, B_BE_PWR_REF_CTRL_CCK, ref_cck); } +static void rtw8922d_set_tx_shape(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_bb_wrap_data *d = rtwdev->phy_info.bb_wrap_data; + const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; + const struct rtw89_tx_shape *tx_shape = &rfe_parms->tx_shape; + u8 tx_shape_idx; + u8 band, regd; + const u16 *th; + + band = chan->band_type; + regd = rtw89_regd_get(rtwdev, band); + tx_shape_idx = (*tx_shape->lmt)[band][RTW89_RS_OFDM][regd]; + + if (tx_shape_idx == 0) + goto disable; + + th = d->bands[chan->rfsi_band].qam_comp_th0; + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKLWORD, th[0], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, MASKHWORD, th[1], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKLWORD, th[2], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, MASKHWORD, th[3], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKLWORD, th[4], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, MASKHWORD, th[5], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKLWORD, th[6], phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, MASKHWORD, th[7], phy_idx); + + th = d->bands[chan->rfsi_band].qam_comp_ow; + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKLWORD, th[0], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, MASKHWORD, th[1], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKLWORD, th[2], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, MASKHWORD, th[3], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKLWORD, th[4], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, MASKHWORD, th[5], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKLWORD, th[6], phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, MASKHWORD, th[7], phy_idx); + + return; + +disable: + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH0_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH1_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH2_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, B_QAM_COMP_TH_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_QAM_COMP_TH3_BE4, B_QAM_COMP_TH_TRIANGULAR_H, 0, phy_idx); + + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_0_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_1_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_2_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, B_OW_VAL_TRIANGULAR_L, 0, phy_idx); + rtw89_write32_idx(rtwdev, R_OW_VAL_3_BE4, B_OW_VAL_TRIANGULAR_H, 0, phy_idx); +} + static void rtw8922d_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -2765,6 +2825,7 @@ static void rtw8922d_set_txpwr(struct rtw89_dev *rtwdev, { rtw89_phy_set_txpwr_byrate(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_offset(rtwdev, chan, phy_idx); + rtw8922d_set_tx_shape(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); From aa7d92e83811a0b557b75f7a0ce85315f4358bf2 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 20 May 2026 17:14:26 +0300 Subject: [PATCH 100/285] wifi: rtw88: Add more validation for the RX descriptor Some RTL8821CE cards can return frames with corrupted RX descriptor, causing warnings and crashes if they are passed to the upper layers. The PHY status size field is 4 bits wide, but in rtw88 its value should only be 0 or 4. Checking this catches most of the corrupt frames. If a PHY status is present, the PHY status size should not be 0. The frame size should not be less than or equal to 4 and should not exceed 11454. The rate should not exceed 4SS MCS9. Discard the frame if any of these checks fail. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221286 Signed-off-by: Bitterblue Smith Tested-by: Oleksandr Havrylov Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/5bfecba3-8a90-4e0f-9558-af5cd8a14975@gmail.com --- drivers/net/wireless/realtek/rtw88/pci.c | 16 +++++++----- drivers/net/wireless/realtek/rtw88/rx.c | 31 +++++++++++++++-------- drivers/net/wireless/realtek/rtw88/rx.h | 6 ++--- drivers/net/wireless/realtek/rtw88/sdio.c | 8 +++++- drivers/net/wireless/realtek/rtw88/usb.c | 9 ++++--- 5 files changed, 47 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index c2bf44e880cf..a30467228912 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1042,20 +1042,21 @@ static int rtw_pci_get_hw_rx_ring_nr(struct rtw_dev *rtwdev, static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, u8 hw_queue, u32 limit) { + struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU]; const struct rtw_chip_info *chip = rtwdev->chip; struct napi_struct *napi = &rtwpci->napi; - struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU]; - struct rtw_rx_pkt_stat pkt_stat; + u32 pkt_desc_sz = chip->rx_pkt_desc_sz; + u32 buf_desc_sz = chip->rx_buf_desc_sz; struct ieee80211_rx_status rx_status; + struct rtw_rx_pkt_stat pkt_stat; struct sk_buff *skb, *new; u32 cur_rp = ring->r.rp; u32 count, rx_done = 0; u32 pkt_offset; - u32 pkt_desc_sz = chip->rx_pkt_desc_sz; - u32 buf_desc_sz = chip->rx_buf_desc_sz; + dma_addr_t dma; u32 new_len; u8 *rx_desc; - dma_addr_t dma; + int ret; count = rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci); count = min(count, limit); @@ -1067,7 +1068,10 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, dma_sync_single_for_cpu(rtwdev->dev, dma, RTK_PCI_RX_BUF_SIZE, DMA_FROM_DEVICE); rx_desc = skb->data; - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) + goto next_rp; /* offset from rx_desc to payload */ pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c index d9e11343d498..01fd299abb7f 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.c +++ b/drivers/net/wireless/realtek/rtw88/rx.c @@ -3,6 +3,7 @@ */ #include "main.h" +#include "mac.h" #include "rx.h" #include "ps.h" #include "debug.h" @@ -261,9 +262,9 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev, } } -void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, - struct rtw_rx_pkt_stat *pkt_stat, - struct ieee80211_rx_status *rx_status) +int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, + struct rtw_rx_pkt_stat *pkt_stat, + struct ieee80211_rx_status *rx_status) { u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz; struct rtw_rx_desc *rx_desc = rx_desc8; @@ -295,20 +296,28 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, pkt_stat->tsf_low = le32_get_bits(rx_desc->w5, RTW_RX_DESC_W5_TSFL); - if (unlikely(pkt_stat->rate >= DESC_RATE_MAX)) { - rtw_dbg(rtwdev, RTW_DBG_UNEXP, - "unexpected RX rate=0x%x\n", pkt_stat->rate); + if (unlikely(pkt_stat->rate >= DESC_RATE_MAX)) + return -EINVAL; - pkt_stat->rate = DESC_RATE1M; - pkt_stat->bw = RTW_CHANNEL_WIDTH_20; - } + if (unlikely(pkt_stat->drv_info_sz && + pkt_stat->drv_info_sz != PHY_STATUS_SIZE)) + return -EINVAL; + + if (unlikely(pkt_stat->phy_status && !pkt_stat->drv_info_sz)) + return -EINVAL; + + if (unlikely(pkt_stat->pkt_len > IEEE80211_MAX_MPDU_LEN_VHT_11454)) + return -EINVAL; /* drv_info_sz is in unit of 8-bytes */ pkt_stat->drv_info_sz *= 8; /* c2h cmd pkt's rx/phy status is not interested */ if (pkt_stat->is_c2h) - return; + return 0; + + if (unlikely(pkt_stat->pkt_len <= FCS_LEN)) + return -EINVAL; phy_status = rx_desc8 + desc_sz + pkt_stat->shift; hdr = phy_status + pkt_stat->drv_info_sz; @@ -318,5 +327,7 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, rtwdev->chip->ops->query_phy_status(rtwdev, phy_status, pkt_stat); rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status); + + return 0; } EXPORT_SYMBOL(rtw_rx_query_rx_desc); diff --git a/drivers/net/wireless/realtek/rtw88/rx.h b/drivers/net/wireless/realtek/rtw88/rx.h index 6b7dee245c0a..74359f641c76 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.h +++ b/drivers/net/wireless/realtek/rtw88/rx.h @@ -45,9 +45,9 @@ struct rtw_rx_desc { void rtw_rx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct sk_buff *skb); -void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, - struct rtw_rx_pkt_stat *pkt_stat, - struct ieee80211_rx_status *rx_status); +int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, + struct rtw_rx_pkt_stat *pkt_stat, + struct ieee80211_rx_status *rx_status); void rtw_update_rx_freq_from_ie(struct rtw_dev *rtwdev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status, struct rtw_rx_pkt_stat *pkt_stat); diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c index 1318e94f8524..5b40d74b16ee 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -995,7 +995,13 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len) while (true) { rx_desc = skb->data; - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) { + dev_kfree_skb_any(skb); + return; + } + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + pkt_stat.shift; diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 1a0bdbf52cb0..64e1c3420e0a 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -619,8 +619,8 @@ static void rtw_usb_rx_handler(struct work_struct *work) u32 max_skb_len = pkt_desc_sz + PHY_STATUS_SIZE * 8 + IEEE80211_MAX_MPDU_LEN_VHT_11454; u32 pkt_offset, next_pkt, skb_len; + int limit, ret; u8 *rx_desc; - int limit; for (limit = 0; limit < 200; limit++) { rx_skb = skb_dequeue(&rtwusb->rx_queue); @@ -636,8 +636,11 @@ static void rtw_usb_rx_handler(struct work_struct *work) rx_desc = rx_skb->data; do { - rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, - &rx_status); + ret = rtw_rx_query_rx_desc(rtwdev, rx_desc, + &pkt_stat, &rx_status); + if (ret) + break; + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + pkt_stat.shift; From 8368970b62404ce2ce70d04c1cfff62700d7d8d5 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 20 May 2026 17:44:35 +0300 Subject: [PATCH 101/285] wifi: rtw89: usb: Support switching to USB 3 mode The Realtek wifi 6/7 devices which support USB 3 are weird: when first plugged in, they pretend to be USB 2. The driver needs to send some commands to the device, which make it disappear and come back as a USB 3 device. Implement the required commands in rtw89. Add a new function rtw89_usb_write32_quiet() to avoid the warnings when writing to R_{AX,BE}_PAD_CTRL2. Even though the write succeeds, usb_control_msg() returns -EPROTO, probably because the USB device disappears immediately. This results in some confusing warnings in the kernel log. When a USB 3 device is plugged into a USB 2 port, rtw89 will try to switch it to USB 3 mode only once. The device will disappear and come back still in USB 2 mode, of course. Tested with RTL8832AU, RTL8832BU, RTL8832CU, and RTL8912AU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/e955451c-93a1-4d04-8024-d224a04f1d4a@gmail.com --- drivers/net/wireless/realtek/rtw89/reg.h | 27 ++++++ drivers/net/wireless/realtek/rtw89/usb.c | 104 ++++++++++++++++++++++- 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 61709182f84c..086ef77ebb9f 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -164,6 +164,19 @@ #define R_AX_DBG_PORT_SEL 0x00C0 #define B_AX_DEBUG_ST_MASK GENMASK(31, 0) +#define R_AX_PAD_CTRL2 0x00C4 +#define B_AX_FORCE_U3_CK BIT(23) +#define B_AX_USB2_FORCE BIT(22) +#define B_AX_USB3_FORCE BIT(21) +#define B_AX_USB3_USB2_TRANSITION BIT(20) +#define B_AX_USB23_SW_MODE_V1 GENMASK(19, 18) +#define USB_MODE_U2 0x1 +#define USB_MODE_U3 0x2 +#define B_AX_NO_PDN_CHIPOFF_V1 BIT(17) +#define B_AX_RSM_EN_V1 BIT(16) +#define B_AX_MATCH_CNT GENMASK(15, 8) +#define USB_SWITCH_DELAY 0xF + #define R_AX_PMC_DBG_CTRL2 0x00CC #define B_AX_SYSON_DIS_PMCR_AX_WRMSK BIT(2) @@ -4235,6 +4248,20 @@ #define B_BE_TOGGLE BIT(31) #define B_BE_DATA_LINE_MASK GENMASK(30, 0) +#define R_BE_PAD_CTRL2 0x00C4 +#define B_BE_USB23_SW_MODE BIT(31) +#define B_BE_USB3_GEN_MODE BIT(30) +#define B_BE_USB3_LANE_MODE BIT(29) +#define B_BE_USB_AUTO_INSTALL_MASK BIT(28) +#define B_BE_FORCE_CLK_U2 BIT(25) +#define B_BE_FORCE_U2_CK BIT(24) +#define B_BE_FORCE_U3_CK BIT(23) +#define B_BE_USB2_FORCE BIT(22) +#define B_BE_USB3_FORCE BIT(21) +#define B_BE_NO_PDN_CHIPOFF_V1 BIT(17) +#define B_BE_RSM_EN_V1 BIT(16) +#define B_BE_MATCH_CNT GENMASK(15, 8) + #define R_BE_PMC_DBG_CTRL2 0x00CC #define B_BE_EFUSE_BURN_GNT_MASK GENMASK(31, 24) #define B_BE_DIS_IOWRAP_TIMEOUT BIT(16) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index c6d55e669776..198378018062 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -11,8 +11,8 @@ static void rtw89_usb_read_port_complete(struct urb *urb); -static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, - void *data, u16 len, u8 reqtype) +static void __rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, + void *data, u16 len, u8 reqtype, bool warn) { struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); struct usb_device *udev = rtwusb->udev; @@ -52,7 +52,7 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, if (ret == -ESHUTDOWN || ret == -ENODEV) set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); - else if (ret < 0) + else if (ret < 0 && warn) rtw89_warn(rtwdev, "usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n", str_read_write(reqtype == RTW89_USB_VENQT_READ), @@ -69,6 +69,12 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, } } +static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, + void *data, u16 len, u8 reqtype) +{ + __rtw89_usb_vendorreq(rtwdev, addr, data, len, reqtype, true); +} + static u32 rtw89_usb_read_cmac(struct rtw89_dev *rtwdev, u32 addr) { u32 addr32, val32, shift; @@ -157,6 +163,14 @@ static void rtw89_usb_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 val) rtw89_usb_vendorreq(rtwdev, addr, &data, 4, RTW89_USB_VENQT_WRITE); } +static void rtw89_usb_write32_quiet(struct rtw89_dev *rtwdev, u32 addr, u32 val) +{ + __le32 data = cpu_to_le32(val); + + __rtw89_usb_vendorreq(rtwdev, addr, &data, 4, + RTW89_USB_VENQT_WRITE, false); +} + static u32 rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, u8 txch) @@ -1059,6 +1073,83 @@ static void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev, usb_set_intfdata(intf, NULL); } +static int rtw89_usb_switch_mode_ax(struct rtw89_dev *rtwdev) +{ + u32 pad_ctrl2; + + /* No known USB 3 devices with this chip. */ + if (rtwdev->chip->chip_id == RTL8851B) + return 0; + + pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_AX_PAD_CTRL2); + + rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n", + __func__, pad_ctrl2); + + /* Already tried to switch but it's a USB 2 port. */ + if (u32_get_bits(pad_ctrl2, B_AX_MATCH_CNT) == USB_SWITCH_DELAY) + return 0; + + /* Add delay to prevent some platforms would not detect USB switch */ + u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_AX_MATCH_CNT); + + pad_ctrl2 &= ~(B_AX_FORCE_U3_CK | B_AX_USB2_FORCE | + B_AX_USB3_FORCE | B_AX_USB3_USB2_TRANSITION); + + u32p_replace_bits(&pad_ctrl2, USB_MODE_U3, B_AX_USB23_SW_MODE_V1); + + pad_ctrl2 |= B_AX_NO_PDN_CHIPOFF_V1 | B_AX_RSM_EN_V1; + + rtw89_usb_write32_quiet(rtwdev, R_AX_PAD_CTRL2, pad_ctrl2); + + return 1; +} + +static int rtw89_usb_switch_mode_be(struct rtw89_dev *rtwdev) +{ + u32 pad_ctrl2; + + pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_BE_PAD_CTRL2); + + rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n", + __func__, pad_ctrl2); + + /* Already tried to switch but it's a USB 2 port. */ + if (u32_get_bits(pad_ctrl2, B_BE_MATCH_CNT) == USB_SWITCH_DELAY) + return 0; + + /* Add delay to prevent some platforms would not detect USB switch */ + u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_BE_MATCH_CNT); + + pad_ctrl2 |= B_BE_RSM_EN_V1 | B_BE_NO_PDN_CHIPOFF_V1 | + B_BE_USB_AUTO_INSTALL_MASK | B_BE_USB23_SW_MODE; + + pad_ctrl2 &= ~(B_BE_USB3_FORCE | B_BE_USB2_FORCE | + B_BE_FORCE_U3_CK | B_BE_FORCE_U2_CK | + B_BE_FORCE_CLK_U2 | B_BE_USB3_GEN_MODE | + B_BE_USB3_LANE_MODE); + + rtw89_usb_write32_quiet(rtwdev, R_BE_PAD_CTRL2, pad_ctrl2); + + return 1; +} + +static int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev) +{ + struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + + if (rtwusb->udev->speed == USB_SPEED_SUPER) { + rtw89_info(rtwdev, + "2.4 GHz performance may be better in a USB 2 port\n"); + return 0; + } + + if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) + return rtw89_usb_switch_mode_ax(rtwdev); + + return rtw89_usb_switch_mode_be(rtwdev); +} + int rtw89_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1091,6 +1182,13 @@ int rtw89_usb_probe(struct usb_interface *intf, goto err_free_hw; } + ret = rtw89_usb_switch_mode(rtwdev); + if (ret) { + /* Not a fail, but we do need to skip rtw89_core_register. */ + ret = 0; + goto err_intf_deinit; + } + if (rtwusb->udev->speed == USB_SPEED_SUPER) rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB3; else From c7427f297ddb01f593217c21b2416f1093b80194 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 28 Apr 2026 13:50:17 -0700 Subject: [PATCH 102/285] wifi: ath11k: use kzalloc_flex for struct scan_req_params Convert kzalloc_obj + kcalloc to kzalloc_flex to save an allocation. Add __counted_by to get extra runtime analysis. Move counting variable assignment immediately after allocation before any potential accesses. kzalloc_flex does this anyway for GCC >= 15. Signed-off-by: Rosen Penev Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260428205017.26288-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/mac.c | 72 ++++++++++----------------- drivers/net/wireless/ath/ath11k/wmi.h | 2 +- 2 files changed, 28 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index a48b6bf1f29a..2d55cdc4d165 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4227,13 +4227,14 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, if (ret) goto exit; - arg = kzalloc_obj(*arg); + arg = kzalloc_flex(*arg, chan_list, req->n_channels); if (!arg) { ret = -ENOMEM; goto exit; } + arg->num_chan = req->n_channels; ath11k_wmi_start_scan_init(ar, arg); arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH11K_SCAN_ID; @@ -4261,38 +4262,27 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, arg->scan_f_passive = 1; } - if (req->n_channels) { - arg->num_chan = req->n_channels; - arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), - GFP_KERNEL); + for (i = 0; i < arg->num_chan; i++) { + if (test_bit(WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL, + ar->ab->wmi_ab.svc_map)) { + arg->chan_list[i] = + u32_encode_bits(req->channels[i]->center_freq, + WMI_SCAN_CONFIG_PER_CHANNEL_MASK); - if (!arg->chan_list) { - ret = -ENOMEM; - goto exit; - } - - for (i = 0; i < arg->num_chan; i++) { - if (test_bit(WMI_TLV_SERVICE_SCAN_CONFIG_PER_CHANNEL, - ar->ab->wmi_ab.svc_map)) { - arg->chan_list[i] = - u32_encode_bits(req->channels[i]->center_freq, - WMI_SCAN_CONFIG_PER_CHANNEL_MASK); - - /* If NL80211_SCAN_FLAG_COLOCATED_6GHZ is set in scan - * flags, then scan all PSC channels in 6 GHz band and - * those non-PSC channels where RNR IE is found during - * the legacy 2.4/5 GHz scan. - * If NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set, - * then all channels in 6 GHz will be scanned. - */ - if (req->channels[i]->band == NL80211_BAND_6GHZ && - req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ && - !cfg80211_channel_is_psc(req->channels[i])) - arg->chan_list[i] |= - WMI_SCAN_CH_FLAG_SCAN_ONLY_IF_RNR_FOUND; - } else { - arg->chan_list[i] = req->channels[i]->center_freq; - } + /* If NL80211_SCAN_FLAG_COLOCATED_6GHZ is set in scan + * flags, then scan all PSC channels in 6 GHz band and + * those non-PSC channels where RNR IE is found during + * the legacy 2.4/5 GHz scan. + * If NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set, + * then all channels in 6 GHz will be scanned. + */ + if (req->channels[i]->band == NL80211_BAND_6GHZ && + req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ && + !cfg80211_channel_is_psc(req->channels[i])) + arg->chan_list[i] |= + WMI_SCAN_CH_FLAG_SCAN_ONLY_IF_RNR_FOUND; + } else { + arg->chan_list[i] = req->channels[i]->center_freq; } } @@ -4335,7 +4325,6 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, exit: if (arg) { - kfree(arg->chan_list); kfree(arg->extraie.ptr); kfree(arg); } @@ -9735,19 +9724,14 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, scan_time_msec = ar->hw->wiphy->max_remain_on_channel_duration * 2; - arg = kzalloc_obj(*arg); + arg = kzalloc_flex(*arg, chan_list, 1); if (!arg) { ret = -ENOMEM; goto exit; } - ath11k_wmi_start_scan_init(ar, arg); + arg->num_chan = 1; - arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list), - GFP_KERNEL); - if (!arg->chan_list) { - ret = -ENOMEM; - goto free_arg; - } + ath11k_wmi_start_scan_init(ar, arg); arg->vdev_id = arvif->vdev_id; arg->scan_id = ATH11K_SCAN_ID; @@ -9768,7 +9752,7 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, spin_lock_bh(&ar->data_lock); ar->scan.state = ATH11K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); - goto free_chan_list; + goto free_arg; } ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ); @@ -9778,7 +9762,7 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, if (ret) ath11k_warn(ar->ab, "failed to stop scan: %d\n", ret); ret = -ETIMEDOUT; - goto free_chan_list; + goto free_arg; } ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, @@ -9786,8 +9770,6 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw, ret = 0; -free_chan_list: - kfree(arg->chan_list); free_arg: kfree(arg); exit: diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index baed501b640b..b2dade0516ac 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -3423,7 +3423,6 @@ struct scan_req_params { u32 num_bssid; u32 num_ssids; u32 n_probes; - u32 *chan_list; u32 notify_scan_events; struct wlan_ssid ssid[WLAN_SCAN_PARAMS_MAX_SSID]; struct wmi_mac_addr bssid_list[WLAN_SCAN_PARAMS_MAX_BSSID]; @@ -3436,6 +3435,7 @@ struct scan_req_params { struct hint_bssid hint_bssid[WLAN_SCAN_MAX_HINT_BSSID]; struct wmi_mac_addr mac_addr; struct wmi_mac_addr mac_mask; + u32 chan_list[] __counted_by(num_chan); }; struct wmi_ssid_arg { From 6b471e9aefee9ed73278eb1141e0d8530a56fae9 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 12 May 2026 10:23:50 +0800 Subject: [PATCH 103/285] wifi: ath11k: fix invalid data access in ath11k_dp_rx_h_undecap_nwifi In certain cases, hardware might provide packets with a length greater than the maximum native Wi-Fi header length. This can lead to accessing and modifying fields in the header within the ath11k_dp_rx_h_undecap_nwifi() function for the DP_RX_DECAP_TYPE_NATIVE_WIFI decap type and potentially result in invalid data access and memory corruption. Kernel stack is corrupted in: ath11k_dp_rx_h_undecap+0x6b0/0x6b0 [ath11k] Call trace: ath11k_dp_rx_h_mpdu+0x0/0x2e8 [ath11k] ath11k_dp_rx_h_mpdu+0x1e0/0x2e8 [ath11k] ath11k_dp_rx_wbm_err+0x1e0/0x450 [ath11k] ath11k_dp_rx_process_wbm_err+0x2fc/0x460 [ath11k] ath11k_dp_service_srng+0x2e0/0x348 [ath11k] Add a sanity check before processing the SKB to prevent invalid data access in the undecap native Wi-Fi function for the DP_RX_DECAP_TYPE_NATIVE_WIFI decap type. This adapted from the discussion/patch of the ath12k driver [1]. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Link: https://lore.kernel.org/linux-wireless/20250211090302.4105141-1-tamizh.raja@oss.qualcomm.com/ # [1] Signed-off-by: Miaoqing Pan Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512022351.2033155-2-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/dp_rx.c | 50 +++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 2a413e3a07a7..923421281254 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -2503,6 +2503,29 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap ieee80211_rx_napi(ar->hw, pubsta, msdu, napi); } +static bool ath11k_dp_rx_check_nwifi_hdr_len_valid(struct ath11k_base *ab, + struct hal_rx_desc *rx_desc, + struct sk_buff *msdu) +{ + struct ieee80211_hdr *hdr; + u8 decap_type; + u32 hdr_len; + + decap_type = ath11k_dp_rx_h_msdu_start_decap_type(ab, rx_desc); + if (decap_type != DP_RX_DECAP_TYPE_NATIVE_WIFI) + return true; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (likely(hdr_len <= DP_MAX_NWIFI_HDR_LEN)) + return true; + + ab->soc_stats.invalid_rbm++; + WARN_ON_ONCE(1); + return false; +} + static int ath11k_dp_rx_process_msdu(struct ath11k *ar, struct sk_buff *msdu, struct sk_buff_head *msdu_list, @@ -2573,6 +2596,11 @@ static int ath11k_dp_rx_process_msdu(struct ath11k *ar, } } + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu))) { + ret = -EINVAL; + goto free_out; + } + ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status); ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status); @@ -3262,6 +3290,12 @@ static int ath11k_dp_rx_h_verify_tkip_mic(struct ath11k *ar, struct ath11k_peer RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED; skb_pull(msdu, hal_rx_desc_sz); + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ar->ab, rx_desc, + msdu))) { + dev_kfree_skb_any(msdu); + return -EINVAL; + } + ath11k_dp_rx_h_ppdu(ar, rx_desc, rxs); ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, HAL_ENCRYPT_TYPE_TKIP_MIC, rxs, true); @@ -3954,6 +3988,10 @@ static int ath11k_dp_rx_h_null_q_desc(struct ath11k *ar, struct sk_buff *msdu, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); } + + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ar->ab, desc, msdu))) + return -EINVAL; + ath11k_dp_rx_h_ppdu(ar, desc, status); ath11k_dp_rx_h_mpdu(ar, msdu, desc, status); @@ -3998,7 +4036,7 @@ static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu, return drop; } -static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, +static bool ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, struct ieee80211_rx_status *status) { u16 msdu_len; @@ -4006,6 +4044,7 @@ static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, u8 l3pad_bytes; struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz; + struct ath11k_base *ab = ar->ab; rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(ar->ab, desc); rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(ar->ab, desc); @@ -4015,6 +4054,9 @@ static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); + if (unlikely(!ath11k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu))) + return true; + ath11k_dp_rx_h_ppdu(ar, desc, status); status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | @@ -4022,19 +4064,21 @@ static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, ath11k_dp_rx_h_undecap(ar, msdu, desc, HAL_ENCRYPT_TYPE_TKIP_MIC, status, false); + + return false; } static bool ath11k_dp_rx_h_rxdma_err(struct ath11k *ar, struct sk_buff *msdu, struct ieee80211_rx_status *status) { struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); - bool drop = false; + bool drop; ar->ab->soc_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: - ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status); + drop = ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status); break; default: /* TODO: Review other rxdma error code to check if anything is From 4d8af936b4fe377f3d7700540f301d8e45e8759b Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 12 May 2026 10:23:51 +0800 Subject: [PATCH 104/285] wifi: ath11k: add MSDU length validation for TKIP MIC error In the WBM error path, while processing TKIP MIC errors, MSDU length is fetched from the hal_rx_desc's msdu_end. This MSDU length is directly passed to skb_put() without validation. In stress test scenarios, the WBM error ring may receive invalid descriptors, which could lead to an invalid MSDU length. To fix this, add a check to drop the skb when the calculated MSDU length is greater than the skb size. This is adapted from the discussion/patch of the ath12k driver [1]. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Link: https://lore.kernel.org/linux-wireless/20250416021903.3178962-1-nithyanantham.paramasivam@oss.qualcomm.com/ # [1] Signed-off-by: Miaoqing Pan Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512022351.2033155-3-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/dp_rx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 923421281254..9e90d8e3f155 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4051,6 +4051,15 @@ static bool ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu, l3pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, desc); msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(ar->ab, desc); + + if (unlikely(hal_rx_desc_sz + l3pad_bytes + msdu_len > DP_RX_BUFFER_SIZE)) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "invalid msdu len in tkip mic err %u\n", msdu_len); + ath11k_dbg_dump(ab, ATH11K_DBG_DATA, NULL, "", desc, + sizeof(*desc)); + return true; + } + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); From 209887467581116a93490e6122b87b6fe0787627 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Sun, 24 May 2026 19:07:11 -0700 Subject: [PATCH 105/285] wifi: ath11k: raise max vdevs to 4 on hardware with P2P and dual-station support When P2P support is enabled, wpa_supplicant creates a p2p-device interface by default, which implicitly consumes one vdev. On systems managed by NetworkManager, this interface cannot be reliably disabled, leaving only two usable interfaces for user configurations. Increase num_vdevs to four for QCA6390 hw2.0, WCN6855 hw2.0/hw2.1, QCA2066 hw2.1, and QCA6698AQ hw2.1 to account for the implicit p2p-device and enable common concurrency scenarios such as AP + AP + STA. This change increases interface concurrency in the two-channel scenario by raising the maximum vdev limit, while keeping other combination rules unchanged. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-05266-QCAHSTSWPLZ_V2_TO_X86-1 Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.41 Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9 Tested-on: QCA6698AQ hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Signed-off-by: Wei Zhang Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525020711.2590815-1-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 3f6f4db5b7ee..8dacc878c006 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -267,7 +267,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), @@ -445,7 +445,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -535,7 +535,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -797,7 +797,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), @@ -886,7 +886,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .coldboot_cal_ftm = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, - .num_vdevs = 2 + 1, + .num_vdevs = 4, .num_peers = 512, .supports_suspend = true, .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), From 10085a654a4c2331d5f0cdc20bfc839a49fbb886 Mon Sep 17 00:00:00 2001 From: Kwan Lai Chee Hou Date: Thu, 7 May 2026 09:53:35 +0800 Subject: [PATCH 106/285] wifi: ath12k: fix incorrect HT/VHT/HE/EHT MCS reporting in monitor mode In monitor mode, the driver incorrectly assigns the legacy rate to the rate_idx field of the radiotap header for HT/VHT/HE/EHT frames, ignoring the actual MCS value parsed from the hardware. This causes packet analyzers (like Wireshark) to display incorrect MCS values (e.g., legacy base rates instead of the true MCS). Fix this by assigning ppdu_info->mcs as the default rate_mcs in ath12k_dp_mon_fill_rx_rate(), and remove rate_idx assignments in ath12k_dp_mon_update_radiotap() to preserve the previously calculated MCS values (including the HT NSS offset). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ Fixes: 5393dcb45209 ("wifi: ath12k: change the status update in the monitor Rx") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220864 Signed-off-by: Kwan Lai Chee Hou Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260507015336.14636-1-laicheehou9@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_mon.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 737287a9aa46..44c5cff75f16 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -115,13 +115,14 @@ ath12k_dp_mon_fill_rx_rate(struct ath12k_pdev_dp *dp_pdev, bool is_cck; pkt_type = ppdu_info->preamble_type; - rate_mcs = ppdu_info->rate; + rate_mcs = ppdu_info->mcs; nss = ppdu_info->nss; sgi = ppdu_info->gi; switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: + rate_mcs = ppdu_info->rate; is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); if (rx_status->band < NUM_NL80211_BANDS) { struct ath12k *ar = ath12k_pdev_dp_to_ar(dp_pdev); @@ -471,13 +472,10 @@ void ath12k_dp_mon_update_radiotap(struct ath12k_pdev_dp *dp_pdev, rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he)); ath12k_dp_mon_rx_update_radiotap_he(ppduinfo, ptr); - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->vht_flags) { rxs->encoding = RX_ENC_VHT; - rxs->rate_idx = ppduinfo->rate; } else if (ppduinfo->ht_flags) { rxs->encoding = RX_ENC_HT; - rxs->rate_idx = ppduinfo->rate; } else { struct ath12k *ar; From 98d4f92ab6a1af2ea2ab590d7e2801b203110981 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Tue, 12 May 2026 10:11:08 +0800 Subject: [PATCH 107/285] wifi: ath12k: fix memory leak in ath12k_wifi7_dp_rx_h_verify_tkip_mic() In ath12k_wifi7_dp_rx_h_verify_tkip_mic(), the call to ath12k_dp_rx_check_nwifi_hdr_len_valid() may return false when the NWIFI header length is invalid, causing the function to abort early with -EINVAL. When this happens, the error propagates to ath12k_wifi7_dp_rx_h_defrag(), which clears first_frag by setting it to NULL. As a result, the corresponding MSDU is no longer referenced by the defragmentation path and is never freed. This leads to a memory leak for the affected MSDU on this error path. Proper cleanup is required to ensure the MSDU is released when header validation fails during TKIP MIC verification. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 9a0dddfb30f1 ("wifi: ath12k: Fix invalid data access in ath12k_dp_rx_h_undecap_nwifi") Signed-off-by: Miaoqing Pan Reviewed-by: Tamizh Chelvam Raja Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512021108.2031651-1-miaoqing.pan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 945680b3ebdf..a5e290edaa89 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -1028,8 +1028,10 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, skb_pull(msdu, hal_rx_desc_sz); if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, - rx_info))) + rx_info))) { + dev_kfree_skb_any(msdu); return -EINVAL; + } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true, From 51d510589f5cb54b9251f4ecf4c13f219dead928 Mon Sep 17 00:00:00 2001 From: Hangtian Zhu Date: Tue, 12 May 2026 10:57:32 +0800 Subject: [PATCH 108/285] wifi: ath12k: allow peer_id 0 in dp peer lookup For some chipsets, firmware can report HTT_T2H_MSG_TYPE_PEER_MAP2 with peer_id 0 as a valid value for mapping ath12k_dp_link_peer to ath12k_dp_peer. ath12k_dp_peer_find_by_peerid() currently treats peer_id 0 as invalid. When firmware assigns peer_id 0, peer lookup fails. As a result, DHCP OFFER packets are dropped in __ieee80211_rx_handle_packet() because pubsta is NULL. ath12k_dp_rx_deliver_msdu() <- rx_info->peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL ieee80211_rx_napi <- pubsta NULL ieee80211_rx_list __ieee80211_rx_handle_packet <- pubsta NULL, skb undelivered The following error in the TX completion path is caused by the same issue: ath12k_wifi7_pci 0000:04:00.0: dp_tx: failed to find the peer with peer_id 0 The error message is triggered by: ath12k_wifi7_dp_tx_complete_msdu ath12k_dp_link_peer_find_by_peerid <- ts->peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL ath12k_dp_tx_htt_tx_complete_buf ath12k_dp_link_peer_find_by_peerid <- peer_id 0 ath12k_dp_peer_find_by_peerid -> peer NULL Fix this by allowing peer_id 0 in ath12k_dp_peer_find_by_peerid() and rejecting only values >= ATH12K_DP_PEER_ID_INVALID. Also update peer_id 0 handling in monitor path: Always call ath12k_dp_link_peer_find_by_peerid() in ath12k_dp_rx_h_find_link_peer() to fetch the peer, including when peer_id is 0. Always store peer_id in ppdu_info->peer_id in ath12k_wifi7_dp_mon_rx_parse_status_tlv(), including peer_id 0. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Hangtian Zhu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260512025732.1297849-1-hangtian.zhu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_peer.c | 2 +- drivers/net/wireless/ath/ath12k/dp_rx.c | 3 +-- drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c index a1100782d45e..47d009a0d61f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.c +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c @@ -419,7 +419,7 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "ath12k dp peer find by peerid index called without rcu lock"); - if (!peer_id || peer_id >= ATH12K_DP_PEER_ID_INVALID) + if (peer_id >= ATH12K_DP_PEER_ID_INVALID) return NULL; index = ath12k_dp_peer_get_peerid_index(dp, peer_id); diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index a68b28aa1f4b..8b69afc69ad4 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1202,8 +1202,7 @@ ath12k_dp_rx_h_find_link_peer(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *ms lockdep_assert_held(&dp->dp_lock); - if (rxcb->peer_id) - peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); + peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id); if (peer) return peer; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index 77f5d23be78d..7dd4a49d64d5 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -1779,8 +1779,7 @@ ath12k_wifi7_dp_mon_rx_parse_status_tlv(struct ath12k_pdev_dp *dp_pdev, info[1] = __le32_to_cpu(mpdu_start->info1); peer_id = u32_get_bits(info[1], HAL_RX_MPDU_START_INFO1_PEERID); - if (peer_id) - ppdu_info->peer_id = peer_id; + ppdu_info->peer_id = peer_id; ppdu_info->mpdu_len += u32_get_bits(info[1], HAL_RX_MPDU_START_INFO2_MPDU_LEN); From c972636efc63f0f43d725b59805dd1ae5bc4b31e Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:04 -0700 Subject: [PATCH 109/285] wifi: ath12k: fix inconsistent arvif state in vdev_create error paths ath12k_mac_vdev_create() has three error path issues that leave arvif in an inconsistent state: 1. When ath12k_wmi_vdev_create() fails, the function returns directly without clearing arvif->ar, which was already set before the WMI call. Subsequent code checking arvif->ar to determine vdev readiness will see a non-NULL value despite no vdev existing in firmware. 2. When ath12k_wmi_send_peer_delete_cmd() fails in err_peer_del, the code jumped to err: skipping the DP peer cleanup and vdev rollback, leaving num_created_vdevs, vdev maps and arvif list membership live. 3. When ath12k_wait_for_peer_delete_done() fails, the code jumped to err_vdev_del: skipping the DP peer cleanup. Fix by changing the ath12k_wmi_vdev_create() failure to goto err instead of returning directly, routing both err_peer_del failure paths through err_dp_peer_del: for proper DP peer and vdev rollback, and consolidating the arvif state cleanup at err:. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: 477cabfdb776 ("wifi: ath12k: modify link arvif creation and removal for MLO") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-2-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 87b27f7cff5d..b9b95f7f1f60 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10302,7 +10302,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ab, "failed to create WMI vdev %d: %d\n", arvif->vdev_id, ret); - return ret; + goto err; } ar->num_created_vdevs++; @@ -10449,13 +10449,13 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) if (ret) { ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", arvif->vdev_id, arvif->bssid); - goto err; + goto err_dp_peer_del; } ret = ath12k_wait_for_peer_delete_done(ar, arvif->vdev_id, arvif->bssid); if (ret) - goto err_vdev_del; + goto err_dp_peer_del; ar->num_peers--; } @@ -10472,8 +10472,6 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) ath12k_wmi_vdev_delete(ar, arvif->vdev_id); ar->num_created_vdevs--; - arvif->is_created = false; - arvif->ar = NULL; ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); ab->free_vdev_map |= 1LL << arvif->vdev_id; ab->free_vdev_stats_id_map &= ~(1LL << arvif->vdev_stats_id); @@ -10482,6 +10480,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) spin_unlock_bh(&ar->data_lock); err: + arvif->is_created = false; arvif->ar = NULL; return ret; } From 47809a7c8348bc4a332ccc26a37c7145a5f609f8 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 11 May 2026 21:49:05 -0700 Subject: [PATCH 110/285] wifi: ath12k: fix NULL deref in change_sta_links for unready link _ieee80211_set_active_links() calls _ieee80211_link_use_channel() for each newly-added link and WARN_ON_ONCE()s if it fails. The call uses assign_on_failure=true, which allows mac80211 to continue despite driver failures, but when a mac80211-level channel validation fails (e.g., combinations check, DFS, or no available radio), drv_assign_vif_chanctx() is never reached. Since ath12k_mac_vdev_create() is only called from that path, arvif->is_created remains false and arvif->ar remains NULL for the failed link. The subsequent drv_change_sta_links() call reaches ath12k_mac_op_change_sta_links(), which allocates an arsta and sets ahsta->links_map |= BIT(link_id) for the broken link before checking whether the link is ready. When the vdev was never created, only station_add() is skipped, but the link remains in links_map. Any subsequent operation iterating links_map and dereferencing arvif->ar without a NULL check will crash. Two observed examples are NULL deref in ath12k_mac_ml_station_remove() on disconnect and in ath12k_mac_op_set_key() when wpa_supplicant installs PTK keys. BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] Call trace: ath12k_mac_station_post_remove+0x40/0xe8 [ath12k] ath12k_mac_op_sta_state+0xb60/0x1720 [ath12k] drv_sta_state+0x100/0xbd8 [mac80211] __sta_info_destroy_part2+0x148/0x178 [mac80211] ieee80211_set_disassoc+0x500/0x678 [mac80211] BUG: Unable to handle kernel NULL pointer dereference at 0x00000000 pc : ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] Call trace: ath12k_mac_op_set_key+0x1f8/0x2c0 [ath12k] drv_set_key+0x70/0x100 [mac80211] ieee80211_key_enable_hw_accel+0x78/0x260 [mac80211] ieee80211_add_key+0x16c/0x2ac [mac80211] nl80211_new_key+0x138/0x280 [cfg80211] Fix this by checking arvif->is_created before calling ath12k_mac_alloc_assign_link_sta(). This prevents the broken link from entering links_map, so all subsequent operations iterating the bitmap are protected. The reliability of arvif->is_created across all error paths is ensured by the preceding patch. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Fixes: a27fa6148dac ("wifi: ath12k: support change_sta_links() mac80211 op") Signed-off-by: Wei Zhang Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260512044906.1735821-3-wei.zhang@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index b9b95f7f1f60..58deddab5f50 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8049,16 +8049,16 @@ int ath12k_mac_op_change_sta_links(struct ieee80211_hw *hw, continue; arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); - arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arvif || !arvif->is_created) + continue; - if (!arvif || !arsta) { + arsta = ath12k_mac_alloc_assign_link_sta(ah, ahsta, ahvif, link_id); + if (!arsta) { ath12k_hw_warn(ah, "Failed to alloc/assign link sta"); continue; } ar = arvif->ar; - if (!ar) - continue; ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret) { From 05337d0b9c5a7ab3b60473490705ebe90d5316aa Mon Sep 17 00:00:00 2001 From: Aaradhana Sahu Date: Fri, 15 May 2026 08:39:09 +0530 Subject: [PATCH 111/285] wifi: ath12k: add hardware parameters for maximum supported clients Currently, the driver uses memory profile parameters to determine the maximum number of supported clients, with a default limit of 512 for single-radio and 128 for DBS and DBS+SBS configurations. However, some devices have lower hardware limits depending on the radio configuration. Exceeding these hardware-specific limits can lead to firmware crashes. Add hardware parameters in ath12k_hw_params to define the maximum supported clients for each radio configuration. The driver uses the minimum of the memory profile limit and the hardware capability limit to prevent exceeding hardware constraints. Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aaradhana Sahu Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260515030909.3312511-1-aaradhana.sahu@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/hw.h | 25 +++++++++++++++--- drivers/net/wireless/ath/ath12k/wifi7/hw.c | 30 ++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index a9888e0521a1..d135b2936378 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -19,14 +19,28 @@ #define TARGET_NUM_VDEVS(ab) ((ab)->profile_param->num_vdevs) /* Max num of stations for Single Radio mode */ -#define TARGET_NUM_STATIONS_SINGLE(ab) ((ab)->profile_param->max_client_single) +#define TARGET_NUM_STATIONS_SINGLE(ab) \ +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_single, \ + _ab->profile_param->max_client_single); \ +}) /* Max num of stations for DBS */ -#define TARGET_NUM_STATIONS_DBS(ab) ((ab)->profile_param->max_client_dbs) +#define TARGET_NUM_STATIONS_DBS(ab) \ +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_dbs, \ + _ab->profile_param->max_client_dbs); \ +}) /* Max num of stations for DBS_SBS */ #define TARGET_NUM_STATIONS_DBS_SBS(ab) \ - ((ab)->profile_param->max_client_dbs_sbs) +({ \ + typeof(ab) _ab = (ab); \ + min_not_zero(_ab->hw_params->client.max_client_dbs_sbs, \ + _ab->profile_param->max_client_dbs_sbs); \ +}) #define TARGET_NUM_STATIONS(ab, x) TARGET_NUM_STATIONS_##x(ab) @@ -213,6 +227,11 @@ struct ath12k_hw_params { /* setup REO queue, frag etc only for primary link peer */ bool dp_primary_link_only:1; + struct { + u32 max_client_single; + u32 max_client_dbs; + u32 max_client_dbs_sbs; + } client; }; struct ath12k_hw_ops { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index cb3185850439..98bf9293dd33 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -434,6 +434,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "wcn7850 hw2.0", @@ -520,6 +525,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = true, .dp_primary_link_only = false, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "qcn9274 hw2.0", @@ -602,6 +612,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "ipq5332 hw1.0", @@ -677,6 +692,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .bdf_addr_offset = 0xC00000, .dp_primary_link_only = true, + .client = { + .max_client_single = 256, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "qcc2072 hw1.0", @@ -764,6 +784,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = true, .dp_primary_link_only = false, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, { .name = "ipq5424 hw1.0", @@ -843,6 +868,11 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .current_cc_support = false, .dp_primary_link_only = true, + .client = { + .max_client_single = 512, + .max_client_dbs = 128, + .max_client_dbs_sbs = 128, + }, }, }; From 675aa75bfc29fb18c6e4d58904a91c1d37228217 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Fri, 22 May 2026 14:48:28 +0530 Subject: [PATCH 112/285] wifi: ath12k: Prevent incorrect vif chanctx switch when handling multi-radio contexts When multiple links switch channel contexts around the same time, mac80211 may complete CSA for several links together and invoke ath12k_mac_op_switch_vif_chanctx() with an array of vifs spanning more than one underlying radio in a single-wiphy configuration. The driver currently assumes that all entries in the vifs array belong to the same radio and derives the radio context from the first element. On multi-radio hardware, this can lead to incorrect vdev selection/updates and may corrupt driver state when the number of vifs exceeds what a single radio supports. Fix this by validating each vif's switch request and then processing vifs grouped by their associated radio. For each vif, ensure the band does not change across the switch and that both old/new channel contexts resolve to a valid ath12k device. Reject attempts to move a vif between radios (not supported for now) and return -EOPNOTSUPP to upper layers. Then, iterate through the input vifs, collect all unprocessed entries that map to the same radio, and invoke ath12k_mac_update_vif_chan() separately for each radio group. This removes any reliance on mac80211 providing the array grouped by radio or sharing old_ctx pointers across vifs. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1 Signed-off-by: Aditya Kumar Singh Co-developed-by: Maharaja Kennadyrajan Signed-off-by: Maharaja Kennadyrajan Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260522091828.3199584-1-maharaja.kennadyrajan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 87 +++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 58deddab5f50..d13936be857d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -11479,6 +11479,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, continue; } + if (WARN_ON(!arvif)) + continue; + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n", arvif->vdev_id, @@ -12270,23 +12273,85 @@ ath12k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw, int n_vifs, enum ieee80211_chanctx_switch_mode mode) { - struct ath12k *ar; + struct ath12k *curr_ar, *new_ar, *group_ar; + struct ieee80211_vif_chanctx_switch *v; + int i, j, count = 0; lockdep_assert_wiphy(hw->wiphy); - ar = ath12k_get_ar_by_ctx(hw, vifs->old_ctx); - if (!ar) - return -EINVAL; + if (n_vifs == 0) + return 0; - /* Switching channels across radio is not allowed */ - if (ar != ath12k_get_ar_by_ctx(hw, vifs->new_ctx)) - return -EINVAL; + struct ath12k **ar_map __free(kfree) = kzalloc_objs(*ar_map, n_vifs); - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, - "mac chanctx switch n_vifs %d mode %d\n", - n_vifs, mode); - ath12k_mac_update_vif_chan(ar, vifs, n_vifs); + if (!ar_map) + return -ENOMEM; + for (i = 0; i < n_vifs; i++) { + v = &vifs[i]; + + if (v->old_ctx->def.chan->band != v->new_ctx->def.chan->band) { + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac chanctx switch band change not supported\n"); + return -EOPNOTSUPP; + } + + curr_ar = ath12k_get_ar_by_ctx(hw, v->old_ctx); + new_ar = ath12k_get_ar_by_ctx(hw, v->new_ctx); + + if (!curr_ar || !new_ar) { + ath12k_generic_dbg(ATH12K_DBG_MAC, + "unable to determine device for the passed channel ctx\n"); + ath12k_generic_dbg(ATH12K_DBG_MAC, + "Old freq %d MHz (device %s) to new freq %d MHz (device %s)\n", + v->old_ctx->def.chan->center_freq, + curr_ar ? "valid" : "invalid", + v->new_ctx->def.chan->center_freq, + new_ar ? "valid" : "invalid"); + return -EINVAL; + } + + /* Switching a vif between two radios is not allowed */ + if (curr_ar != new_ar) { + ath12k_dbg(curr_ar->ab, ATH12K_DBG_MAC, + "mac chanctx switch to another radio not supported\n"); + return -EOPNOTSUPP; + } + + ar_map[i] = curr_ar; + } + + /* Group vifs by radio (ar) and process each group independently. */ + bool *processed __free(kfree) = kzalloc_objs(*processed, n_vifs); + + if (!processed) + return -ENOMEM; + + struct ieee80211_vif_chanctx_switch *group_vifs __free(kfree) = + kzalloc_objs(*group_vifs, n_vifs); + + if (!group_vifs) + return -ENOMEM; + + for (i = 0; i < n_vifs; i++) { + if (processed[i]) + continue; + + group_ar = ar_map[i]; + + count = 0; + for (j = 0; j < n_vifs; j++) { + if (!processed[j] && ar_map[j] == group_ar) { + group_vifs[count++] = vifs[j]; + processed[j] = true; + } + } + + ath12k_dbg(group_ar->ab, ATH12K_DBG_MAC, + "mac chanctx switch n_vifs %d mode %d\n", + count, mode); + ath12k_mac_update_vif_chan(group_ar, group_vifs, count); + } return 0; } EXPORT_SYMBOL(ath12k_mac_op_switch_vif_chanctx); From e1125b0ab6fdda21dde19f7be631a477d14b684c Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:37 +0530 Subject: [PATCH 113/285] wifi: ath12k: Set WDS vdev parameter for 4-address station interface Set WDS vdev parameter during station interface creation to enable 4-address mode. Unlike AP interfaces that set peer-specific 4-address mode parameters after receiving 4-address frames from stations, station interfaces must send all data frames in 4-address mode immediately after association, including 4-address NULL frames. Firmware requires 4-address notification for station interfaces during vdev creation. Configure the WDS vdev parameter for station interfaces. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Co-developed-by: Ramya Gnanasekar Signed-off-by: Ramya Gnanasekar Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-2-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index d13936be857d..0f68b4beeb6b 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10235,6 +10235,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) struct ieee80211_hw *hw = ah->hw; struct ath12k_vif *ahvif = arvif->ahvif; struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); struct ath12k_wmi_vdev_create_arg vdev_arg = {}; struct ath12k_wmi_peer_create_arg peer_param = {}; struct ieee80211_bss_conf *link_conf = NULL; @@ -10403,6 +10404,25 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) goto err_peer_del; } + /* + * There could be race condition in firmware for the station + * interface between enabling 4-address peer WMI param and + * sending 4-address frame (NULL or EAPOL via TCL). + * Make the station as WDS while bringup itself + * to avoid the race condition + */ + if (vif->type == NL80211_IFTYPE_STATION && + (wdev && wdev->use_4addr)) { + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_WDS, + 1); + if (ret) { + ath12k_warn(ar->ab, "failed to set WDS vdev param: %d\n", + ret); + goto err_peer_del; + } + } + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && ahvif->vdev_type == WMI_VDEV_TYPE_STA && ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { From 2f57f737dbf3005951a045eb9d1daaff0095f6c1 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:38 +0530 Subject: [PATCH 114/285] wifi: ath12k: Add support for 4-address mode The current driver does not support enabling 4-address mode data traffic in WDS mode. Add the required functionality by introducing the sta_set_4addr() API, which is invoked when a 4-address AP/STA connects. This API sends the WMI_PEER_USE_4ADDR peer parameter to notify firmware about the 4-address peer, allowing firmware and hardware to transmit and receive frames in 4-address format for that peer. For 4-address multicast packet transmission, update the handling to set peer metadata values in HAL_TCL_DATA_CMD_INFO1_CMD_NUM instead of using vdev metadata values. Vdev metadata is used only for 3-address and 4-address unicast traffic and for 3-address multicast traffic. The peer metadata path embeds the correct peer_id, enabling proper multicast transmission in 4-address mode. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-3-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 6 ++ drivers/net/wireless/ath/ath12k/mac.c | 79 +++++++++++++++++++ drivers/net/wireless/ath/ath12k/mac.h | 3 + drivers/net/wireless/ath/ath12k/peer.c | 7 +- drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 10 ++- drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h | 4 +- drivers/net/wireless/ath/ath12k/wifi7/hw.c | 18 ++++- drivers/net/wireless/ath/ath12k/wmi.h | 5 ++ 8 files changed, 125 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index f6d8ec9ef7b0..70ad9742c21d 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -353,6 +353,8 @@ struct ath12k_link_vif { u16 num_stations; bool is_csa_in_progress; struct wiphy_work bcn_tx_work; + + bool set_wds_vdev_param; }; struct ath12k_vif { @@ -492,6 +494,8 @@ struct ath12k_link_sta { /* link address similar to ieee80211_link_sta */ u8 addr[ETH_ALEN]; + u16 tcl_metadata; + /* the following are protected by ar->data_lock */ u32 changed; /* IEEE80211_RC_* */ u32 bw; @@ -527,6 +531,8 @@ struct ath12k_sta { u16 free_logical_link_idx_map; enum ieee80211_sta_state state; + + bool enable_4addr; }; #define ATH12K_HALF_20MHZ_BW 10 diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 0f68b4beeb6b..616d13261305 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6571,6 +6571,62 @@ static int ath12k_mac_station_disassoc(struct ath12k *ar, return 0; } +static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahsta) +{ + struct ath12k_dp_link_peer *peer; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + struct ath12k_dp *dp; + unsigned long links; + struct ath12k *ar; + u8 link_id; + int ret; + + links = ahsta->links_map; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + arsta = wiphy_dereference(wiphy, ahsta->link[link_id]); + if (!arsta) + continue; + + arvif = arsta->arvif; + ar = arvif->ar; + + if (arvif->set_wds_vdev_param) + goto skip_use_4addr; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "setting USE_4ADDR for peer %pM\n", arsta->addr); + + ret = ath12k_wmi_set_peer_param(ar, arsta->addr, + arvif->vdev_id, + WMI_PEER_USE_4ADDR, + WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME); + if (ret) { + ath12k_warn(ar->ab, "failed to set peer %pM 4addr capability: %d\n", + arsta->addr, ret); + return ret; + } + +skip_use_4addr: + dp = ath12k_ab_to_dp(ar->ab); + spin_lock_bh(&dp->dp_lock); + peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id, + arsta->addr); + if (peer && peer->dp_peer) { + peer->dp_peer->ucast_ra_only = true; + } else { + spin_unlock_bh(&dp->dp_lock); + ath12k_warn(ar->ab, "failed to find DP peer for %pM\n", + arsta->addr); + return -ENOENT; + } + + spin_unlock_bh(&dp->dp_lock); + } + + return 0; +} + static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk) { struct ieee80211_link_sta *link_sta; @@ -7865,6 +7921,28 @@ int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ath12k_mac_op_sta_set_txpwr); +void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled) +{ + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + + lockdep_assert_wiphy(hw->wiphy); + + /* + * 4-address mode disabled option is available only for station + * interface from mac80211, and we have wds_vdev_param for station + * interface and target will not allow to disable the wds_vdev_param + * during run time. So, add support only for enable case, for + * disable case station interface needs to be reconnect. + */ + if (enabled && !ahsta->enable_4addr) { + if (!ath12k_mac_sta_set_4addr(hw->wiphy, ahsta)) + ahsta->enable_4addr = true; + } +} +EXPORT_SYMBOL(ath12k_mac_op_sta_set_4addr); + void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, @@ -10421,6 +10499,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) ret); goto err_peer_del; } + arvif->set_wds_vdev_param = true; } if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 7b50c5976384..aba98afd4365 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -255,6 +255,9 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); +void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled); void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta, diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 2e875176baaa..42488b465540 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -218,7 +218,12 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ahsta = ath12k_sta_to_ahsta(sta); arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, ahsta->link[link_id]); - + /* TODO: Split DP related field usage to DP peer structure */ + arsta->tcl_metadata = u16_encode_bits(0, HTT_TCL_META_DATA_TYPE) | + u16_encode_bits(peer->peer_id, + HTT_TCL_META_DATA_PEER_ID) | + u16_encode_bits(0, + HTT_TCL_META_DATA_VALID_HTT); peer->link_id = arsta->link_id; /* Fill ML info into created peer */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 629084aa36d8..5f298133dee9 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -59,8 +59,8 @@ static int ath12k_wifi7_dp_prepare_htt_metadata(struct sk_buff *skb) /* TODO: Remove the export once this file is built with wifi7 ko */ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, - struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, - bool is_mcast) + struct ath12k_link_sta *arsta, struct sk_buff *skb, + bool gsn_valid, int mcbc_gsn, bool is_mcast) { struct ath12k_dp *dp = dp_pdev->dp; struct ath12k_hal *hal = dp->hal; @@ -125,6 +125,12 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a ti.bank_id = dp_link_vif->bank_id; ti.meta_data_flags = dp_link_vif->tcl_metadata; + if (ieee80211_has_a4(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr3) && arsta) { + ti.meta_data_flags = arsta->tcl_metadata; + ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); + } + if (dp_vif->tx_encap_type == HAL_TCL_ENCAP_TYPE_RAW && test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) { if (skb_cb->flags & ATH12K_SKB_CIPHER_SET) { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h index 24cf7972d41b..86bc813878c0 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.h @@ -8,8 +8,8 @@ #define ATH12K_DP_TX_WIFI7_H int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, - struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, - bool is_mcast); + struct ath12k_link_sta *arsta, struct sk_buff *skb, + bool gsn_valid, int mcbc_gsn, bool is_mcast); void ath12k_wifi7_dp_tx_completion_handler(struct ath12k_dp *dp, int ring_id); u32 ath12k_wifi7_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, struct ath12k_link_vif *arvif); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index 98bf9293dd33..3d59fa452ec0 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -889,7 +889,9 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; struct ieee80211_sta *sta = control->sta; + struct ath12k_link_sta *arsta = NULL; struct ath12k_link_vif *tmp_arvif; + struct ath12k_sta *ahsta = NULL; u32 info_flags = info->flags; struct sk_buff *msdu_copied; struct ath12k *ar, *tmp_ar; @@ -971,6 +973,12 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) is_mcast = is_multicast_ether_addr(hdr->addr1); + if (sta) { + ahsta = ath12k_sta_to_ahsta(control->sta); + if (ahsta && ahsta->enable_4addr) + arsta = rcu_dereference(ahsta->link[link_id]); + } + /* This is case only for P2P_GO */ if (vif->type == NL80211_IFTYPE_AP && vif->p2p) ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp); @@ -991,7 +999,7 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, if (!vif->valid_links || !is_mcast || is_dvlan || (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) || test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) { - ret = ath12k_wifi7_dp_tx(dp_pdev, arvif, skb, false, 0, is_mcast); + ret = ath12k_wifi7_dp_tx(dp_pdev, arvif, arsta, skb, false, 0, is_mcast); if (unlikely(ret)) { ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); ieee80211_free_txskb(ar->ah->hw, skb); @@ -1029,6 +1037,11 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, skb_cb->vif = vif; skb_cb->ar = tmp_ar; + if (ahsta && ahsta->enable_4addr) + arsta = rcu_dereference(ahsta->link[link_id]); + else + arsta = NULL; + /* For open mode, skip peer find logic */ if (unlikely(!ahvif->dp_vif.key_cipher)) goto skip_peer_find; @@ -1060,7 +1073,7 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw, spin_unlock_bh(&tmp_dp->dp_lock); skip_peer_find: - ret = ath12k_wifi7_dp_tx(tmp_dp_pdev, tmp_arvif, + ret = ath12k_wifi7_dp_tx(tmp_dp_pdev, tmp_arvif, arsta, msdu_copied, true, mcbc_gsn, is_mcast); if (unlikely(ret)) { if (ret == -ENOMEM) { @@ -1105,6 +1118,7 @@ static const struct ieee80211_ops ath12k_ops_wifi7 = { .sta_state = ath12k_mac_op_sta_state, .sta_set_txpwr = ath12k_mac_op_sta_set_txpwr, .link_sta_rc_update = ath12k_mac_op_link_sta_rc_update, + .sta_set_4addr = ath12k_mac_op_sta_set_4addr, .conf_tx = ath12k_mac_op_conf_tx, .set_antenna = ath12k_mac_op_set_antenna, .get_antenna = ath12k_mac_op_get_antenna, diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 14b8dcdf881d..a827b3c99f8f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2334,6 +2334,11 @@ enum wmi_preamble { WMI_VDEV_PREAMBLE_SHORT = 2, }; +enum wmi_peer_4addr_allow_frame { + WMI_PEER_4ADDR_ALLOW_DATA_FRAME = 1, + WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME = 2, +}; + enum wmi_peer_smps_state { WMI_PEER_SMPS_PS_NONE = 0, WMI_PEER_SMPS_STATIC = 1, From 729cad3c3c9e09ca9900744fe2a02b25e23cdab5 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:39 +0530 Subject: [PATCH 115/285] wifi: ath12k: Add 4-address mode support for eth offload Currently driver does not enable the hardware/firmware support for handling 4-address multicast frames in the Tx/Rx path when 8023_ENCAP_OFFLOAD is enabled. Add the required support to ensure correct processing of multicast traffic in 4-address mode. Enable this functionality by setting the WMI_VDEV_PARAM_AP_ENABLE_NAWDS vdev parameter when the 8023_ENCAP_OFFLOAD feature is active. Override peer metadata values for 4-address multicast packet transmission by using the station's ast_hash and ast_idx instead of vdev-level metadata, and set HAL_TCL_DATA_CMD_INFO4_IDX_LOOKUP_OVERRIDE to indicate this override. Suppress firmware peer-map events for 4-address frames by setting the WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE flag during WMI initialization. This prevents inconsistencies in the host's peer list. Add the IEEE80211_OFFLOAD_ENCAP_4ADDR VIF offload flag to notify mac80211 that 4-address Ethernet encapsulation offload is supported. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-4-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 3 ++ drivers/net/wireless/ath/ath12k/mac.c | 28 +++++++++++--- drivers/net/wireless/ath/ath12k/peer.c | 2 + drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c | 37 +++++++++++++++---- .../net/wireless/ath/ath12k/wifi7/hal_tx.c | 4 +- .../net/wireless/ath/ath12k/wifi7/hal_tx.h | 1 + drivers/net/wireless/ath/ath12k/wmi.c | 3 +- drivers/net/wireless/ath/ath12k/wmi.h | 9 +++++ 8 files changed, 73 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 70ad9742c21d..fc5127b5c1a3 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -355,6 +355,7 @@ struct ath12k_link_vif { struct wiphy_work bcn_tx_work; bool set_wds_vdev_param; + bool nawds_enabled; }; struct ath12k_vif { @@ -495,6 +496,8 @@ struct ath12k_link_sta { u8 addr[ETH_ALEN]; u16 tcl_metadata; + u16 ast_hash; + u16 ast_idx; /* the following are protected by ar->data_lock */ u32 changed; /* IEEE80211_RC_* */ diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 616d13261305..2c1eadf548ac 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6576,6 +6576,7 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst struct ath12k_dp_link_peer *peer; struct ath12k_link_vif *arvif; struct ath12k_link_sta *arsta; + struct ath12k_vif *ahvif; struct ath12k_dp *dp; unsigned long links; struct ath12k *ar; @@ -6589,10 +6590,11 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst continue; arvif = arsta->arvif; + ahvif = arvif->ahvif; ar = arvif->ar; if (arvif->set_wds_vdev_param) - goto skip_use_4addr; + goto skip_nawds; ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "setting USE_4ADDR for peer %pM\n", arsta->addr); @@ -6607,7 +6609,21 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst return ret; } -skip_use_4addr: + if (ahvif->dp_vif.tx_encap_type != ATH12K_HW_TXRX_ETHERNET) + goto skip_nawds; + + ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + WDS_EXT_ENABLE); + if (ret) { + ath12k_warn(ar->ab, "failed to set vdev %d nawds parameter: %d\n", + arvif->vdev_id, ret); + return ret; + } + + arvif->nawds_enabled = true; + +skip_nawds: dp = ath12k_ab_to_dp(ar->ab); spin_lock_bh(&dp->dp_lock); peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id, @@ -10098,12 +10114,14 @@ static void ath12k_mac_update_vif_offload(struct ath12k_link_vif *arvif) vif->offload_flags &= ~(IEEE80211_OFFLOAD_ENCAP_ENABLED | IEEE80211_OFFLOAD_DECAP_ENABLED); - if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) + if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_ETHERNET; - else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) + vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + } else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_RAW; - else + } else { ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_NATIVE_WIFI; + } ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param_id, ahvif->dp_vif.tx_encap_type); diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 42488b465540..f7f0f613b393 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -224,6 +224,8 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, HTT_TCL_META_DATA_PEER_ID) | u16_encode_bits(0, HTT_TCL_META_DATA_VALID_HTT); + arsta->ast_hash = peer->ast_hash; + arsta->ast_idx = peer->hw_peer_id; peer->link_id = arsta->link_id; /* Fill ML info into created peer */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 5f298133dee9..d2749de44553 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -57,6 +57,8 @@ static int ath12k_wifi7_dp_prepare_htt_metadata(struct sk_buff *skb) return 0; } +#define ATH12K_AST_HASH_MASK 0xF + /* TODO: Remove the export once this file is built with wifi7 ko */ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *arvif, struct ath12k_link_sta *arsta, struct sk_buff *skb, @@ -78,6 +80,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a struct ath12k_dp_vif *dp_vif = &ahvif->dp_vif; struct ath12k_dp_link_vif *dp_link_vif; struct dp_tx_ring *tx_ring; + struct ethhdr *eth = NULL; u8 pool_id; u8 hal_ring_id; int ret; @@ -96,6 +99,9 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a !ieee80211_is_data(hdr->frame_control)) return -EOPNOTSUPP; + if (skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) + eth = (struct ethhdr *)skb->data; + pool_id = skb_get_queue_mapping(skb) & (ATH12K_HW_MAX_QUEUES - 1); /* Let the default ring selection be based on current processor @@ -124,9 +130,16 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a ti.bank_id = dp_link_vif->bank_id; ti.meta_data_flags = dp_link_vif->tcl_metadata; + ti.bss_ast_hash = dp_link_vif->ast_hash; + ti.bss_ast_idx = dp_link_vif->ast_idx; - if (ieee80211_has_a4(hdr->frame_control) && - is_multicast_ether_addr(hdr->addr3) && arsta) { + if (eth && is_multicast_ether_addr(eth->h_dest) && arsta) { + ti.meta_data_flags = arsta->tcl_metadata; + ti.bss_ast_hash = arsta->ast_hash & ATH12K_AST_HASH_MASK; + ti.bss_ast_idx = arsta->ast_idx; + ti.lookup_override = true; + } else if (!eth && ieee80211_has_a4(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr3) && arsta) { ti.meta_data_flags = arsta->tcl_metadata; ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); } @@ -146,7 +159,7 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a msdu_ext_desc = true; } - if (gsn_valid) { + if (gsn_valid && !ti.lookup_override) { /* Reset and Initialize meta_data_flags with Global Sequence * Number (GSN) info. */ @@ -154,6 +167,14 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a u32_encode_bits(HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM, HTT_TCL_META_DATA_TYPE) | u32_encode_bits(mcbc_gsn, HTT_TCL_META_DATA_GLOBAL_SEQ_NUM); + + /* + * Since NAWDS enabled for this vdev firmware expects + * this flag to be set for sending 3-address multicast frame. + */ + ti.meta_data_flags |= + u32_encode_bits(arvif->nawds_enabled, + HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED); } ti.encap_type = ath12k_dp_tx_get_encap_type(ab, skb); @@ -164,11 +185,13 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a ti.lmac_id = dp_link_vif->lmac_id; ti.vdev_id = dp_link_vif->vdev_id; - if (gsn_valid) - ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; - ti.bss_ast_hash = dp_link_vif->ast_hash; - ti.bss_ast_idx = dp_link_vif->ast_idx; + if (gsn_valid && !ti.lookup_override) + ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; + else if (arvif->nawds_enabled && is_mcast && !ti.lookup_override) + ti.meta_data_flags |= + u32_encode_bits(1, HTT_TCL_META_DATA_HOST_INSPECTED_MISSION); + ti.dscp_tid_tbl_idx = 0; if (skb->ip_summed == CHECKSUM_PARTIAL && diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c index 02d3cadf03fe..eeabe9db776e 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c @@ -59,7 +59,9 @@ void ath12k_wifi7_hal_tx_cmd_desc_setup(struct ath12k_base *ab, le32_encode_bits(ti->lmac_id, HAL_TCL_DATA_CMD_INFO3_PMAC_ID) | le32_encode_bits(ti->vdev_id, HAL_TCL_DATA_CMD_INFO3_VDEV_ID); - tcl_cmd->info4 = le32_encode_bits(ti->bss_ast_idx, + tcl_cmd->info4 = le32_encode_bits(ti->lookup_override, + HAL_TCL_DATA_CMD_INFO4_IDX_LOOKUP_OVERRIDE) | + le32_encode_bits(ti->bss_ast_idx, HAL_TCL_DATA_CMD_INFO4_SEARCH_INDEX) | le32_encode_bits(ti->bss_ast_hash, HAL_TCL_DATA_CMD_INFO4_CACHE_SET_NUM); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h index 9d2b1552c2f5..c548a0fbf1bb 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.h @@ -34,6 +34,7 @@ struct hal_tx_info { u8 dscp_tid_tbl_idx; bool enable_mesh; int bank_id; + bool lookup_override; }; /* TODO: Check if the actual desc macros can be used instead */ diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 18f91051199c..d9f1b4615575 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -4192,7 +4192,8 @@ ath12k_wmi_copy_resource_config(struct ath12k_base *ab, cpu_to_le32(1 << WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT); wmi_cfg->ema_max_vap_cnt = cpu_to_le32(tg_cfg->ema_max_vap_cnt); wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period); - wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET); + wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET | + WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE); } static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index a827b3c99f8f..13d82f706d79 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2334,6 +2334,14 @@ enum wmi_preamble { WMI_VDEV_PREAMBLE_SHORT = 2, }; +/* + * This will be used to set for WMI_VDEV_PARAM_AP_ENABLE_NAWDS + * whenever 4addr station connects in wds offload case. + * This is for enabling multicast to unicast conversion support in + * firmware + */ +#define WDS_EXT_ENABLE 1 + enum wmi_peer_4addr_allow_frame { WMI_PEER_4ADDR_ALLOW_DATA_FRAME = 1, WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME = 2, @@ -2559,6 +2567,7 @@ struct wmi_init_cmd { #define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4) #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) #define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9) +#define WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE BIT(18) #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) struct ath12k_wmi_resource_config_params { From 6d0572f61539c5d4e2971139e7b501e37b7632d6 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:40 +0530 Subject: [PATCH 116/285] wifi: ath12k: Add support for 4-address NULL frame handling Currently, the firmware processes all NULL frames internally and does not forward them to the host. As a result, the host never receives 4-address NULL frames sent by a 4-address station. These 4-address NULL frames are sent by the station to indicate to the AP that it is operating in 4-address mode. Enable WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT flag during WMI initialization after verifying the WMI_SERVICE_WDS_NULL_FRAME_SUPPORT service capability. This enables the firmware to forward all NULL frames to the host. Add host-side handling to parse 4-address NULL frames and forward them to mac80211 to support proper AP_VLAN interface creation. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Co-developed-by: Sarika Sharma Signed-off-by: Sarika Sharma Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-5-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/peer.c | 7 ++-- drivers/net/wireless/ath/ath12k/wmi.c | 44 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 3 ++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index f7f0f613b393..b5a0ba149a28 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -220,10 +220,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ahsta->link[link_id]); /* TODO: Split DP related field usage to DP peer structure */ arsta->tcl_metadata = u16_encode_bits(0, HTT_TCL_META_DATA_TYPE) | - u16_encode_bits(peer->peer_id, - HTT_TCL_META_DATA_PEER_ID) | - u16_encode_bits(0, - HTT_TCL_META_DATA_VALID_HTT); + u16_encode_bits(peer->peer_id, + HTT_TCL_META_DATA_PEER_ID) | + u16_encode_bits(0, HTT_TCL_META_DATA_VALID_HTT); arsta->ast_hash = peer->ast_hash; arsta->ast_idx = peer->hw_peer_id; peer->link_id = arsta->link_id; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index d9f1b4615575..84a31b953db8 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -4194,6 +4194,10 @@ ath12k_wmi_copy_resource_config(struct ath12k_base *ab, wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period); wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET | WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE); + + if (tg_cfg->is_wds_null_frame_supported) + wmi_cfg->flags2 |= + cpu_to_le32(WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT); } static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, @@ -4403,6 +4407,9 @@ int ath12k_wmi_cmd_init(struct ath12k_base *ab) ab->wmi_ab.svc_map)) arg.res_cfg.is_reg_cc_ext_event_supported = true; + if (test_bit(WMI_TLV_SERVICE_WDS_NULL_FRAME_SUPPORT, ab->wmi_ab.svc_map)) + arg.res_cfg.is_wds_null_frame_supported = true; + ab->hw_params->wmi_init(ab, &arg.res_cfg); ab->wow.wmi_conf_rx_decap_mode = arg.res_cfg.rx_decap_mode; @@ -7224,7 +7231,11 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) struct ath12k_wmi_mgmt_rx_arg rx_ev = {}; struct ath12k *ar; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_sta *pubsta = NULL; + struct ath12k_dp_link_peer *peer; struct ieee80211_hdr *hdr; + bool is_4addr_null_pkt; + struct ath12k_dp *dp; u16 fc; struct ieee80211_supported_band *sband; s32 noise_floor; @@ -7299,6 +7310,38 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); + is_4addr_null_pkt = (ieee80211_is_nullfunc(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) && + ieee80211_has_a4(hdr->frame_control); + + /* + * Add check to drop frames other than 4-address NULL frame. Since + * firmware sends all NULL frames in this path (3-address and 4-address) + */ + if (ieee80211_is_data(hdr->frame_control) && !is_4addr_null_pkt) { + dev_kfree_skb(skb); + goto exit; + } + + if (is_4addr_null_pkt) { + dp = ath12k_ab_to_dp(ar->ab); + spin_lock_bh(&dp->dp_lock); + peer = ath12k_dp_link_peer_find_by_pdev_and_addr(dp, ar->pdev_idx, + hdr->addr2); + if (!peer) { + spin_unlock_bh(&dp->dp_lock); + dev_kfree_skb(skb); + goto exit; + } + pubsta = peer->sta; + if (pubsta && pubsta->valid_links) { + status->link_valid = 1; + status->link_id = peer->link_id; + } + spin_unlock_bh(&dp->dp_lock); + goto send_rx; + } + /* Firmware is guaranteed to report all essential management frames via * WMI while it can deliver some extra via HTT. Since there can be * duplicates split the reporting wrt monitor/sniffing. @@ -7322,6 +7365,7 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) if (ieee80211_is_beacon(hdr->frame_control)) ath12k_mac_handle_beacon(ar, skb); +send_rx: ath12k_dbg(ab, ATH12K_DBG_MGMT, "event mgmt rx skb %p len %d ftype %02x stype %02x\n", skb, skb->len, diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 13d82f706d79..c452e3d57a29 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2278,6 +2278,7 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365, WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION = 410, + WMI_TLV_SERVICE_WDS_NULL_FRAME_SUPPORT = 421, WMI_TLV_SERVICE_IS_TARGET_IPA = 425, WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK = 426, WMI_TLV_SERVICE_THERM_THROT_5_LEVELS = 429, @@ -2511,6 +2512,7 @@ struct ath12k_wmi_resource_config_arg { u32 ema_max_vap_cnt; u32 ema_max_profile_period; bool is_reg_cc_ext_event_supported; + bool is_wds_null_frame_supported; }; struct ath12k_wmi_init_cmd_arg { @@ -2568,6 +2570,7 @@ struct wmi_init_cmd { #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) #define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9) #define WMI_RSRC_CFG_FLAGS2_FW_AST_INDICATION_DISABLE BIT(18) +#define WMI_RSRC_CFG_FLAGS2_WDS_NULL_FRAME_SUPPORT BIT(22) #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18) struct ath12k_wmi_resource_config_params { From f818260ac66b2971a2a587ea08b171b135a2c1e6 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:41 +0530 Subject: [PATCH 117/285] wifi: ath12k: Add support for 4-address frame notification mac80211 currently relies on receiving 4-address frames from connected stations to trigger AP_VLAN interface creation. However, when ethernet encapsulation offload is enabled, mac80211 only receives 802.3 frames and cannot differentiate between 3-address and 4-address formats, preventing AP_VLAN creation. Enable mac80211 to detect 4-address traffic by converting 802.3 frames back into 802.11 frames in the driver and setting the FROM_DS and TO_DS bits using the RX_MSDU_END_INFO5_FROM_DS and RX_MSDU_END_INFO5_TO_DS fields. This restores 4-address frame visibility to mac80211 and allows it to trigger AP_VLAN interface creation. Skip this frame conversion once the AP_VLAN interface is created and the station is attached to it. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-6-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_peer.h | 2 + drivers/net/wireless/ath/ath12k/dp_rx.c | 10 ++++- drivers/net/wireless/ath/ath12k/dp_rx.h | 3 +- drivers/net/wireless/ath/ath12k/hal.h | 4 +- drivers/net/wireless/ath/ath12k/mac.c | 1 + drivers/net/wireless/ath/ath12k/peer.c | 3 ++ drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 38 ++++++++++++++----- .../wireless/ath/ath12k/wifi7/hal_qcc2072.c | 16 ++++++++ .../wireless/ath/ath12k/wifi7/hal_qcn9274.c | 16 ++++++++ .../wireless/ath/ath12k/wifi7/hal_wcn7850.c | 16 ++++++++ 10 files changed, 97 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h index 113b8040010f..f5067e66f1e1 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.h +++ b/drivers/net/wireless/ath/ath12k/dp_peer.h @@ -143,6 +143,8 @@ struct ath12k_dp_peer { struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS]; struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; + + bool use_4addr; }; struct ath12k_dp_link_peer * diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 8b69afc69ad4..06e74124e57e 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1142,7 +1142,8 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev, void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, bool decrypted, - struct hal_rx_desc_data *rx_info) + struct hal_rx_desc_data *rx_info, + struct ath12k_dp_peer *peer) { enum ath12k_dp_rx_decap_type decap_type = rx_info->decap_type; struct ethhdr *ehdr; @@ -1166,6 +1167,13 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu break; } + if (peer && !peer->use_4addr && + rx_info->is_from_ds && rx_info->is_to_ds) { + ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info, + decap_type); + break; + } + /* PN for mcast packets will be validated in mac80211; * remove eth header and add 802.11 header. */ diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 55a31e669b3b..0660db070e92 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -190,7 +190,8 @@ void ath12k_dp_extract_rx_desc_data(struct ath12k_hal *hal, void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, bool decrypted, - struct hal_rx_desc_data *rx_info); + struct hal_rx_desc_data *rx_info, + struct ath12k_dp_peer *peer); void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi, struct sk_buff *msdu, struct hal_rx_desc_data *rx_info); diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index bf4f7dbae866..21c551d8b248 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -738,7 +738,9 @@ struct hal_rx_desc_data { addr2_present:1, is_mcbc:1, seq_ctl_valid:1, - fc_valid:1; + fc_valid:1, + is_to_ds:1, + is_from_ds:1; u16 msdu_len; u16 peer_id; u16 seq_no; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2c1eadf548ac..a6e4b660da81 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -6630,6 +6630,7 @@ static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahst arsta->addr); if (peer && peer->dp_peer) { peer->dp_peer->ucast_ra_only = true; + peer->dp_peer->use_4addr = true; } else { spin_unlock_bh(&dp->dp_lock); ath12k_warn(ar->ab, "failed to find DP peer for %pM\n", diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index b5a0ba149a28..c222bdaa333c 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -255,6 +255,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, ar->hw_link_id); } + if (vif->type == NL80211_IFTYPE_AP && peer->dp_peer) + peer->dp_peer->ucast_ra_only = true; + return ret; } diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index a5e290edaa89..5c7eaaf200f9 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -323,9 +323,9 @@ static void ath12k_wifi7_dp_rx_h_csum_offload(struct sk_buff *msdu, CHECKSUM_NONE : CHECKSUM_UNNECESSARY; } -static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, - struct sk_buff *msdu, - struct hal_rx_desc_data *rx_info) +static int ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, + struct sk_buff *msdu, + struct hal_rx_desc_data *rx_info) { struct ath12k_skb_rxcb *rxcb; enum hal_encrypt_type enctype; @@ -347,6 +347,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); if (peer) { + /* + * Drop the 3-address multicast packet from 4-address + * peer To avoid receiving the duplicate multicast packet + * Specifically from AP interface in 3-address format + */ + if (rxcb->is_mcbc && + rx_info->decap_type == DP_RX_DECAP_TYPE_ETHERNET2_DIX) { + if (peer->use_4addr && + !(rx_info->is_from_ds && rx_info->is_to_ds)) + return -EINVAL; + } + /* resetting mcbc bit because mcbc packets are unicast * packets only for AP as STA sends unicast packets. */ @@ -387,15 +399,18 @@ static void ath12k_wifi7_dp_rx_h_mpdu(struct ath12k_pdev_dp *dp_pdev, } ath12k_wifi7_dp_rx_h_csum_offload(msdu, rx_info); - ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info); + ath12k_dp_rx_h_undecap(dp_pdev, msdu, enctype, is_decrypted, rx_info, + peer); if (!is_decrypted || rx_info->is_mcbc) - return; + return 0; if (rx_info->decap_type != DP_RX_DECAP_TYPE_ETHERNET2_DIX) { hdr = (void *)msdu->data; hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); } + + return 0; } static int ath12k_wifi7_dp_rx_msdu_coalesce(struct ath12k_hal *hal, @@ -554,7 +569,9 @@ static int ath12k_wifi7_dp_rx_process_msdu(struct ath12k_pdev_dp *dp_pdev, } ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + goto free_out; rx_info->rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; @@ -1035,7 +1052,7 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, true, - rx_info); + rx_info, NULL); ieee80211_rx(ath12k_pdev_dp_to_hw(dp_pdev), msdu); return -EINVAL; } @@ -1590,6 +1607,7 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, u8 l3pad_bytes = rx_info->l3_pad_bytes; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); u32 hal_rx_desc_sz = dp->ab->hal.hal_desc_sz; + int ret; if (!rxcb->is_frag && ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE)) { /* First buffer will be freed by the caller, so deduct it's length */ @@ -1634,7 +1652,9 @@ static int ath12k_wifi7_dp_rx_h_null_q_desc(struct ath12k_pdev_dp *dp_pdev, return -EINVAL; ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); - ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + return ret; rxcb->tid = rx_info->tid; @@ -1680,7 +1700,7 @@ static bool ath12k_wifi7_dp_rx_h_tkip_mic_err(struct ath12k_pdev_dp *dp_pdev, RX_FLAG_DECRYPTED); ath12k_dp_rx_h_undecap(dp_pdev, msdu, HAL_ENCRYPT_TYPE_TKIP_MIC, false, - rx_info); + rx_info, NULL); return false; } diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c index 1eefb931a853..8cebb229ebed 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c @@ -184,6 +184,20 @@ static u8 ath12k_hal_rx_desc_get_l3_pad_bytes_qcc2072(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_L3_HDR_PADDING); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_qcc2072(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_qcc2072(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static u32 ath12k_hal_rx_desc_get_mpdu_start_tag_qcc2072(struct hal_rx_desc *desc) { return le32_get_bits(desc->u.qcc2072.mpdu_start_tag, @@ -397,6 +411,8 @@ static void ath12k_hal_extract_rx_desc_data_qcc2072(struct hal_rx_desc_data *rx_ rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcc2072(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcc2072(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcc2072(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcc2072(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcc2072(rx_desc); rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_qcc2072(rx_desc); rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_qcc2072(rx_desc); rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_qcc2072(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c index ba9ce1e718e8..9d5180ef83b4 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c @@ -699,6 +699,20 @@ u8 ath12k_hal_rx_desc_get_mpdu_tid_qcn9274(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_TID); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_qcn9274(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_qcn9274(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.qcn9274_compact.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static inline u16 ath12k_hal_rx_desc_get_mpdu_peer_id_qcn9274(struct hal_rx_desc *desc) { @@ -914,6 +928,8 @@ void ath12k_hal_extract_rx_desc_data_qcn9274(struct hal_rx_desc_data *rx_desc_da rx_desc_data->seq_ctl_valid = ath12k_hal_rx_desc_get_mpdu_seq_ctl_vld_qcn9274(rx_desc); rx_desc_data->fc_valid = ath12k_hal_rx_desc_get_mpdu_fc_valid_qcn9274(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_qcn9274(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_qcn9274(rx_desc); rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_qcn9274(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_qcn9274(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_qcn9274(rx_desc); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c index e64e512cac7d..efbbc1cbd3e4 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_wcn7850.c @@ -280,6 +280,20 @@ u8 ath12k_hal_rx_desc_get_l3_pad_bytes_wcn7850(struct hal_rx_desc *desc) RX_MSDU_END_INFO5_L3_HDR_PADDING); } +static inline +u8 ath12k_wifi7_hal_rx_h_from_ds_wcn7850(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.wcn7850.msdu_end.info5, + RX_MSDU_END_INFO5_FROM_DS); +} + +static inline +u8 ath12k_wifi7_hal_rx_h_to_ds_wcn7850(struct hal_rx_desc *desc) +{ + return le16_get_bits(desc->u.wcn7850.msdu_end.info5, + RX_MSDU_END_INFO5_TO_DS); +} + static inline bool ath12k_hal_rx_desc_encrypt_valid_wcn7850(struct hal_rx_desc *desc) { @@ -599,6 +613,8 @@ void ath12k_hal_extract_rx_desc_data_wcn7850(struct hal_rx_desc_data *rx_desc_da rx_desc_data->seq_no = ath12k_hal_rx_desc_get_mpdu_start_seq_no_wcn7850(rx_desc); rx_desc_data->msdu_len = ath12k_hal_rx_desc_get_msdu_len_wcn7850(ldesc); rx_desc_data->sgi = ath12k_hal_rx_desc_get_msdu_sgi_wcn7850(rx_desc); + rx_desc_data->is_from_ds = ath12k_wifi7_hal_rx_h_from_ds_wcn7850(rx_desc); + rx_desc_data->is_to_ds = ath12k_wifi7_hal_rx_h_to_ds_wcn7850(rx_desc); rx_desc_data->rate_mcs = ath12k_hal_rx_desc_get_msdu_rate_mcs_wcn7850(rx_desc); rx_desc_data->bw = ath12k_hal_rx_desc_get_msdu_rx_bw_wcn7850(rx_desc); rx_desc_data->phy_meta_data = ath12k_hal_rx_desc_get_msdu_freq_wcn7850(rx_desc); From 565257a857690244211d85593b2cd490ce86783a Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Mon, 25 May 2026 16:39:42 +0530 Subject: [PATCH 118/285] wifi: ath12k: Handle 4-address EAPOL frames from WBM error path Whenever hardware receives 4-address EAPOL frames from an unauthorized station it is routed through WBM/RXDMA error path with the HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR error code. But, the current driver does not handle the 4-address EAPOL frames in the WBM error path. As a result, these frames are dropped, causing authentication failures and connectivity issues for 4-address stations. Add support to correctly process these frames and forward them to mac80211 for proper handling. This prevents the loss of 4-address EAPOL frames and ensures reliable connectivity for WDS/4-address clients. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Co-developed-by: Sathishkumar Muruganandam Signed-off-by: Sathishkumar Muruganandam Signed-off-by: Tamizh Chelvam Raja Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260525110942.2890212-7-tamizh.raja@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 5c7eaaf200f9..cb9dd8cb28b6 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -1704,6 +1704,56 @@ static bool ath12k_wifi7_dp_rx_h_tkip_mic_err(struct ath12k_pdev_dp *dp_pdev, return false; } +static bool ath12k_wifi7_dp_rx_h_unauth_wds_err(struct ath12k_pdev_dp *dp_pdev, + struct sk_buff *msdu, + struct hal_rx_desc_data *rx_info) +{ + struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); + struct ath12k_dp *dp = dp_pdev->dp; + u32 hdr_len, hal_rx_desc_sz = dp->ab->hal.hal_desc_sz; + u8 l3pad_bytes = rx_info->l3_pad_bytes; + struct ath12k_dp_rx_rfc1042_hdr *llc; + u16 msdu_len = rx_info->msdu_len; + struct ath12k_dp_peer *peer; + struct ieee80211_hdr *hdr; + int ret; + + guard(rcu)(); + peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); + if (!peer) { + ath12k_dbg(dp->ab, ATH12K_DBG_DATA, + "failed to find the peer to process unauth wds err handling peer_id %d\n", + rxcb->peer_id); + return true; + } + + if ((hal_rx_desc_sz + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) + return true; + + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); + skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); + + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(dp, msdu, + rx_info))) + return true; + + ath12k_dp_rx_h_ppdu(dp_pdev, rx_info); + + ret = ath12k_wifi7_dp_rx_h_mpdu(dp_pdev, msdu, rx_info); + if (ret) + return true; + + rxcb->tid = rx_info->tid; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + hdr_len); + if (llc->snap_type != cpu_to_be16(ETH_P_PAE)) + return true; + + return false; +} + static bool ath12k_wifi7_dp_rx_h_rxdma_err(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, struct hal_rx_desc_data *rx_info) @@ -1715,6 +1765,9 @@ static bool ath12k_wifi7_dp_rx_h_rxdma_err(struct ath12k_pdev_dp *dp_pdev, dp->device_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { + case HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR: + drop = ath12k_wifi7_dp_rx_h_unauth_wds_err(dp_pdev, msdu, rx_info); + break; case HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR: case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: if (rx_info->err_bitmap & HAL_RX_MPDU_ERR_TKIP_MIC) { From 575e6a72bd68008b6795345113d3e94f3dccbf5d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 25 May 2026 09:17:28 +0800 Subject: [PATCH 119/285] wifi: rtw89: 8851bu: add Mercusys MA60XNB (2c4e:0128) Add the specific USB device ID which adapter tested fully functional on Fedora 44 with kernel 7.0.8-200.fc44.x86_64 and linux-firmware 20260410-1.fc44. Reported-by: Guillermo Servera Negre Tested-by: Guillermo Servera Negre Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260525011728.6836-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8851bu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 6a8d31544314..14f1b703902f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -44,6 +44,9 @@ static const struct usb_device_id rtw_8851bu_id_table[] = { /* D-Link AX9U rev. A1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x332a, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, + /* Mercusys MA60XNB */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2C4E, 0x0128, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, /* TP-Link Archer TX10UB Nano */ { USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010b, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, From bf4a37f516f0382832c10a9d04414944d0d96591 Mon Sep 17 00:00:00 2001 From: David Lee Date: Mon, 25 May 2026 16:26:36 +0800 Subject: [PATCH 120/285] wifi: rtw89: usb: skip ACPI capability check for USB devices Skip the ACPI capability check for all USB devices by default, allowing them to use their default configurations. For USB dongles, customers will manage their own compliance and certification. This initial patch focuses on the generic USB skip infrastructure; specific customer certifications and localized configurations will be handled by quirks afterward. Signed-off-by: David Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260525082636.31105-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/regd.c | 24 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/sar.c | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 28466cb35ea2..14fab6b1a74a 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -360,6 +360,7 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_chip_info *chip = rtwdev->chip; struct ieee80211_supported_band *sband; struct rtw89_acpi_dsm_result res = {}; @@ -377,6 +378,9 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, return; } + if (skip_acpi_dsm) + return; + bitmap_fill(regulatory->block_unii4, RTW89_REGD_MAX_COUNTRY_NUM); ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_UNII4_SUP, &res); @@ -426,6 +430,7 @@ static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block, static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_country_code *country; const struct rtw89_acpi_policy_6ghz *ptr; struct rtw89_acpi_dsm_result res = {}; @@ -433,6 +438,9 @@ static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) int i, j; int ret; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_BP, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -478,12 +486,16 @@ static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev) static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_6ghz_sp *ptr; struct rtw89_acpi_dsm_result res = {}; bool enable; u8 index; int ret; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -524,6 +536,7 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) static void rtw89_regd_setup_policy_6ghz_vlp(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_6ghz_vlp *ptr = NULL; struct rtw89_acpi_dsm_result res = {}; bool enable; @@ -531,6 +544,9 @@ static void rtw89_regd_setup_policy_6ghz_vlp(struct rtw89_dev *rtwdev) int ret; u8 val; + if (skip_acpi_dsm) + return; + /* By default, allow 6 GHz VLP on all countries except US and CA. */ val = ~(RTW89_ACPI_CONF_6GHZ_VLP_US | RTW89_ACPI_CONF_6GHZ_VLP_CA); @@ -574,6 +590,7 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { const struct rtw89_chip_info *chip = rtwdev->chip; bool chip_support_6ghz = chip->support_bands & BIT(NL80211_BAND_6GHZ); + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; bool regd_allow_6ghz = chip_support_6ghz; struct ieee80211_supported_band *sband; struct rtw89_acpi_dsm_result res = {}; @@ -583,6 +600,9 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy) if (!chip_support_6ghz) goto bottom; + if (skip_acpi_dsm) + goto bottom; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6G_DIS, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -661,10 +681,14 @@ const char *rtw89_regd_get_string(enum rtw89_regulation_type regd) static void rtw89_regd_setup_reg_rules(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_acpi_policy_reg_rules *ptr; struct rtw89_acpi_dsm_result res = {}; int ret; + if (skip_acpi_dsm) + return; + regulatory->txpwr_uk_follow_etsi = true; ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_REG_RULES_EN, &res); diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 7886ffaf5695..6fe3822eb598 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -693,6 +693,7 @@ static bool rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) static void rtw89_tas_init(struct rtw89_dev *rtwdev) { + bool skip_acpi_dsm = rtwdev->hci.type == RTW89_HCI_TYPE_USB; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_tas_info *tas = &rtwdev->tas; const struct rtw89_acpi_policy_tas *ptr; @@ -702,6 +703,9 @@ static void rtw89_tas_init(struct rtw89_dev *rtwdev) if (!chip->support_tas) return; + if (skip_acpi_dsm) + return; + ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res); if (ret) { rtw89_debug(rtwdev, RTW89_DBG_SAR, From 7554f6602a80471549df2c4e7b90b4d095b1d816 Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Fri, 29 May 2026 15:50:31 +0800 Subject: [PATCH 121/285] wifi: rtw89: add dev_id_quirks to driver_info for per-device quirk control Add a dev_id_quirks field to rtw89_driver_info so that per-device (VID/PID) quirks can be expressed independently of chip-level default_quirks. Apply the bitmap in rtw89_alloc_ieee80211_hw() so both USB and PCI probes benefit automatically. All existing driver_info structs initialize dev_id_quirks to 0; no behavior change. Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260529075032.16807-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 9 +++++++-- drivers/net/wireless/realtek/rtw89/core.h | 4 ++-- drivers/net/wireless/realtek/rtw89/pci.c | 3 +-- drivers/net/wireless/realtek/rtw89/rtw8851be.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8851bu.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852ae.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852au.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852be.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bte.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bu.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852ce.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922ae.c | 2 ++ drivers/net/wireless/realtek/rtw89/rtw8922au.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922de.c | 2 ++ drivers/net/wireless/realtek/rtw89/usb.c | 3 +-- 16 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c1df3e2ba11e..68dad6090f87 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7532,9 +7532,11 @@ EXPORT_SYMBOL(rtw89_core_unregister); struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, u32 bus_data_size, - const struct rtw89_chip_info *chip, - const struct rtw89_chip_variant *variant) + const struct rtw89_driver_info *info) { + const unsigned long *dev_id_quirks = &info->dev_id_quirks; + const struct rtw89_chip_variant *variant = info->variant; + const struct rtw89_chip_info *chip = info->chip; struct rtw89_fw_info early_fw = {}; const struct firmware *firmware; struct ieee80211_hw *hw; @@ -7600,6 +7602,9 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, rtwdev->fw.fw_format = fw_format; rtwdev->support_mlo = support_mlo; + bitmap_or(rtwdev->quirks, rtwdev->quirks, dev_id_quirks, + NUM_OF_RTW89_QUIRKS); + rtw89_debug(rtwdev, RTW89_DBG_CHAN, "probe driver %s chanctx\n", no_chanctx ? "without" : "with"); rtw89_debug(rtwdev, RTW89_DBG_CHAN, "probe driver %s MLO cap\n", diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index fb32d2bd9cb0..4c638c2bdc4f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4803,6 +4803,7 @@ struct rtw89_driver_info { const struct rtw89_chip_info *chip; const struct rtw89_chip_variant *variant; const struct dmi_system_id *quirks; + unsigned long dev_id_quirks; /* bitmap of rtw89_quirks */ union rtw89_bus_info bus; }; @@ -8129,8 +8130,7 @@ int rtw89_core_register(struct rtw89_dev *rtwdev); void rtw89_core_unregister(struct rtw89_dev *rtwdev); struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, u32 bus_data_size, - const struct rtw89_chip_info *chip, - const struct rtw89_chip_variant *variant); + const struct rtw89_driver_info *info); void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev); u8 rtw89_acquire_mac_id(struct rtw89_dev *rtwdev); void rtw89_release_mac_id(struct rtw89_dev *rtwdev, u8 mac_id); diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index fe1152c560bd..102bae488180 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4772,8 +4772,7 @@ int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) info = (const struct rtw89_driver_info *)id->driver_data; rtwdev = rtw89_alloc_ieee80211_hw(&pdev->dev, - sizeof(struct rtw89_pci), - info->chip, info->variant); + sizeof(struct rtw89_pci), info); if (!rtwdev) { dev_err(&pdev->dev, "failed to allocate hw\n"); return -ENOMEM; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c index ce59ac9f56ba..640672eb0d26 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8851be_info = { .chip = &rtw8851b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8851b_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 14f1b703902f..34ba5661d771 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -31,6 +31,7 @@ static const struct rtw89_driver_info rtw89_8851bu_info = { .chip = &rtw8851b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8851b_usb_info, } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c index 9e05e831569d..64306cdc1ee4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c @@ -71,6 +71,7 @@ static const struct rtw89_driver_info rtw89_8852ae_info = { .chip = &rtw8852a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852a_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852au.c b/drivers/net/wireless/realtek/rtw89/rtw8852au.c index 4cced4619b7d..29b7f7769370 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852au.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852au.c @@ -33,6 +33,7 @@ static const struct rtw89_driver_info rtw89_8852au_info = { .chip = &rtw8852a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8852a_usb_info, } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c index 12db0d0be547..5bc0a6a99d1d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8852be_info = { .chip = &rtw8852b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852b_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c index 8c995aa95325..49a72ca835ac 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c @@ -79,6 +79,7 @@ static const struct rtw89_driver_info rtw89_8852bte_info = { .chip = &rtw8852bt_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852bt_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index 37111fed276f..308d3d570ff3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -31,6 +31,7 @@ static const struct rtw89_driver_info rtw89_8852bu_info = { .chip = &rtw8852b_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8852b_usb_info, } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c index 150fed189414..3c64c0539205 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c @@ -102,6 +102,7 @@ static const struct rtw89_driver_info rtw89_8852ce_info = { .chip = &rtw8852c_chip_info, .variant = NULL, .quirks = rtw8852c_pci_quirks, + .dev_id_quirks = 0, .bus = { .pci = &rtw8852c_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 790fd1dec66d..8f89f9a31455 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -33,6 +33,7 @@ static const struct rtw89_driver_info rtw89_8852cu_info = { .chip = &rtw8852c_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8852c_usb_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c index 90c62b757c57..5527a8db393b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c @@ -77,6 +77,7 @@ static const struct rtw89_driver_info rtw89_8922ae_info = { .chip = &rtw8922a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922a_pci_info, }, @@ -86,6 +87,7 @@ static const struct rtw89_driver_info rtw89_8922ae_vs_info = { .chip = &rtw8922a_chip_info, .variant = &rtw8922ae_vs_variant, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922a_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922au.c b/drivers/net/wireless/realtek/rtw89/rtw8922au.c index 347bde171391..2b81de501d62 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922au.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922au.c @@ -32,6 +32,7 @@ static const struct rtw89_driver_info rtw89_8922au_info = { .chip = &rtw8922a_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .usb = &rtw8922a_usb_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c index f144e7fc76de..a1a81c338be3 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922de.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c @@ -73,6 +73,7 @@ static const struct rtw89_driver_info rtw89_8922de_vs_info = { .chip = &rtw8922d_chip_info, .variant = &rtw8922de_vs_variant, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922d_pci_info, }, @@ -82,6 +83,7 @@ static const struct rtw89_driver_info rtw89_8922de_info = { .chip = &rtw8922d_chip_info, .variant = NULL, .quirks = NULL, + .dev_id_quirks = 0, .bus = { .pci = &rtw8922d_pci_info, }, diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 198378018062..67ebf2d9bb7d 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -1161,8 +1161,7 @@ int rtw89_usb_probe(struct usb_interface *intf, info = (const struct rtw89_driver_info *)id->driver_info; rtwdev = rtw89_alloc_ieee80211_hw(&intf->dev, - sizeof(struct rtw89_usb), - info->chip, info->variant); + sizeof(struct rtw89_usb), info); if (!rtwdev) { dev_err(&intf->dev, "failed to allocate hw\n"); return -ENOMEM; From 384cc80f30977d2ff095615f2241b21904499255 Mon Sep 17 00:00:00 2001 From: Johnson Tsai Date: Fri, 29 May 2026 15:50:32 +0800 Subject: [PATCH 122/285] wifi: rtw89: usb: add serial_number and uuid sysfs attributes for 0x28de:0x2432 Expose the device's Serial Number (SN) and UUID from EFUSE via two read-only sysfs attributes, `serial_number` and `uuid`, on the ieee80211 phy device under the `rtw89_usb` attribute group. This hardware identification information is essential for user-space applications to uniquely identify, track, and manage specific Wi-Fi adapters. For example, in automated factory provisioning or device management systems, user-space tools rely on the EFUSE serial number and UUID to bind configurations to specific physical adapters. Currently, standard wireless APIs do not expose this low-level hardware information, making these sysfs nodes the only viable solution for user space to extract this data. The attributes are gated behind a new RTW89_QUIRK_HW_INFO_SYSFS quirk, enabled only for the VID 0x28de / PID 0x2432 device via the dev_id_quirks field in rtw89_driver_info. Example usage from user-space: $ cat /sys/class/ieee80211/phy0/rtw89_usb/serial_number 3642000123 $ cat /sys/class/ieee80211/phy0/rtw89_usb/uuid aaec2b7c-0a55-4727-8de0-b30febccbbaa Cc: Elliot Saba Cc: Charles Lohr Signed-off-by: Johnson Tsai Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260529075032.16807-3-pkshih@realtek.com --- .../ABI/testing/sysfs-class-ieee80211-rtw89 | 24 +++++++++ drivers/net/wireless/realtek/rtw89/core.h | 6 +++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 10 ++++ drivers/net/wireless/realtek/rtw89/rtw8852c.h | 6 ++- .../net/wireless/realtek/rtw89/rtw8852cu.c | 12 ++++- drivers/net/wireless/realtek/rtw89/usb.c | 52 +++++++++++++++++++ 6 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 diff --git a/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 b/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 new file mode 100644 index 000000000000..7dfdce08a42f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-ieee80211-rtw89 @@ -0,0 +1,24 @@ +What: /sys/class/ieee80211/phyX/rtw89_usb/serial_number +Date: May 2026 +Contact: Johnson Tsai , linux-wireless@vger.kernel.org +Description: (Read) Serial number burned into EFUSE of the RTL8852CU-based + USB Wi-Fi adapter. Only present on devices that set the + RTW89_QUIRK_HW_INFO_SYSFS quirk (currently VID 0x28de / + PID 0x2432). + + Format: %10phN (5 raw bytes printed as 10 lowercase hex + digits, no separators). + + Example: 3642000123 + +What: /sys/class/ieee80211/phyX/rtw89_usb/uuid +Date: May 2026 +Contact: Johnson Tsai , linux-wireless@vger.kernel.org +Description: (Read) UUID burned into EFUSE of the RTL8852CU-based USB Wi-Fi + adapter. Only present on devices that set the + RTW89_QUIRK_HW_INFO_SYSFS quirk (currently VID 0x28de / + PID 0x2432). + + Format: %pUb (RFC 4122 UUID in lowercase with hyphens). + + Example: aaec2b7c-0a55-4727-8de0-b30febccbbaa diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4c638c2bdc4f..5547888d7e67 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3618,6 +3618,9 @@ struct rtw89_sta_link { u32 data_tx_cnt_lmt:6; }; +#define RTW89_EFUSE_SN_LEN 5 +#define RTW89_EFUSE_UUID_LEN 16 + struct rtw89_efuse { bool valid; bool power_k_valid; @@ -3628,6 +3631,8 @@ struct rtw89_efuse { u8 adc_td; u8 bt_setting_2; u8 bt_setting_3; + u8 sn[RTW89_EFUSE_SN_LEN]; + u8 uuid[RTW89_EFUSE_UUID_LEN]; }; struct rtw89_phy_rate_pattern { @@ -5376,6 +5381,7 @@ enum rtw89_quirks { RTW89_QUIRK_PCI_BER, RTW89_QUIRK_THERMAL_PROT_120C, RTW89_QUIRK_THERMAL_PROT_110C, + RTW89_QUIRK_HW_INFO_SYSFS, NUM_OF_RTW89_QUIRKS, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 7bb1264bcaef..3861cce42b1b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -621,6 +621,15 @@ static void rtw8852c_efuse_parsing_gain_offset(struct rtw89_dev *rtwdev, gain->offset_valid = valid; } +static void rtw8852c_efuse_copy_sn_uuid_usb(struct rtw89_dev *rtwdev, + const struct rtw8852c_efuse *map) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + + memcpy(efuse->sn, map->u.sn, sizeof(efuse->sn)); + memcpy(efuse->uuid, map->u.uuid, sizeof(efuse->uuid)); +} + static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, enum rtw89_efuse_block block) { @@ -640,6 +649,7 @@ static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, break; case RTW89_HCI_TYPE_USB: ether_addr_copy(efuse->addr, map->u.mac_addr); + rtw8852c_efuse_copy_sn_uuid_usb(rtwdev, map); break; default: return -ENOTSUPP; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.h b/drivers/net/wireless/realtek/rtw89/rtw8852c.h index 8585921ac6c4..b1d7c354c18e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.h @@ -13,7 +13,11 @@ struct rtw8852c_u_efuse { u8 rsvd[0x88]; u8 mac_addr[ETH_ALEN]; -}; + u8 rsvd1[8]; + u8 sn[RTW89_EFUSE_SN_LEN]; + u8 rsvd2[29]; + u8 uuid[RTW89_EFUSE_UUID_LEN]; +} __packed; struct rtw8852c_e_efuse { u8 mac_addr[ETH_ALEN]; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 8f89f9a31455..81ee96b0a048 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -39,6 +39,16 @@ static const struct rtw89_driver_info rtw89_8852cu_info = { }, }; +static const struct rtw89_driver_info rtw89_8852cu_valve_info = { + .chip = &rtw8852c_chip_info, + .variant = NULL, + .quirks = NULL, + .dev_id_quirks = BIT(RTW89_QUIRK_HW_INFO_SYSFS), + .bus = { + .usb = &rtw8852c_usb_info, + }, +}; + static const struct usb_device_id rtw_8852cu_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03a6, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, @@ -53,7 +63,7 @@ static const struct usb_device_id rtw_8852cu_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x991d, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x28de, 0x2432, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + .driver_info = (kernel_ulong_t)&rtw89_8852cu_valve_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x8206, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff), diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 67ebf2d9bb7d..aafa8b9e93df 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -1150,6 +1150,56 @@ static int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev) return rtw89_usb_switch_mode_be(rtwdev); } +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_efuse *efuse = &rtwdev->efuse; + + return sysfs_emit(buf, "%*phN\n", + (int)sizeof(efuse->sn), efuse->sn); +} +static DEVICE_ATTR_RO(serial_number); + +static ssize_t uuid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + struct rtw89_efuse *efuse = &rtwdev->efuse; + + return sysfs_emit(buf, "%pUb\n", efuse->uuid); +} +static DEVICE_ATTR_RO(uuid); + +static struct attribute *rtw89_usb_attrs[] = { + &dev_attr_serial_number.attr, + &dev_attr_uuid.attr, + NULL, +}; + +static bool rtw89_usb_group_visible(struct kobject *kobj) +{ + struct device *dev = kobj_to_dev(kobj); + struct wiphy *wiphy = container_of(dev, struct wiphy, dev); + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct rtw89_dev *rtwdev = hw->priv; + + return test_bit(RTW89_QUIRK_HW_INFO_SYSFS, rtwdev->quirks); +} + +DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(rtw89_usb); + +static const struct attribute_group rtw89_usb_group = { + .name = "rtw89_usb", + .attrs = rtw89_usb_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(rtw89_usb), +}; +__ATTRIBUTE_GROUPS(rtw89_usb); + int rtw89_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1171,6 +1221,8 @@ int rtw89_usb_probe(struct usb_interface *intf, rtwusb->rtwdev = rtwdev; rtwusb->info = info->bus.usb; + rtwdev->hw->wiphy->dev.groups = rtw89_usb_groups; + rtwdev->hci.ops = &rtw89_usb_ops; rtwdev->hci.type = RTW89_HCI_TYPE_USB; rtwdev->hci.tx_rpt_enabled = true; From 5f508715e960613572fe231837b6b4e250a64f8d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 28 May 2026 10:50:42 +0000 Subject: [PATCH 123/285] wifi: mac80211: report assoc_link_id in station info for non-MLD STAs on MLD AP When a non-MLD station associates with an MLD AP, it does so on a specific link. However, sta_set_sinfo() never sets mlo_params_valid, so nl80211 never emits NL80211_ATTR_MLO_LINK_ID in get_station / dump_station responses. Userspace has no way to determine which link a non-MLD STA is associated on. Set mlo_params_valid to 1 and assoc_link_id to sta->deflink.link_id, when valid_links is set. Also set the mld_addr copy only for MLD STAs, so that non-MLD STAs get a zeroed mld_addr as documented. Signed-off-by: Felix Fietkau Link: https://patch.msgid.link/20260528105042.835284-1-nbd@nbd.name Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 0ea37016cd4f..4c86a3793804 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -3258,7 +3258,10 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, struct link_sta_info *link_sta; int link_id; - ether_addr_copy(sinfo->mld_addr, sta->addr); + sinfo->mlo_params_valid = true; + sinfo->assoc_link_id = sta->deflink.link_id; + if (sta->sta.mlo) + ether_addr_copy(sinfo->mld_addr, sta->addr); /* assign valid links first for iteration */ sinfo->valid_links = sta->sta.valid_links; From 1a15bf9708ba3bf80410065e113aa17cd6a18dcf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 08:40:27 +0200 Subject: [PATCH 124/285] wifi: cfg80211: remove 5/10 MHz channel support Remove WIPHY_FLAG_SUPPORTS_5_10_MHZ and 5/10 MHz channel width support. We contemplated this back in early 2023 and didn't do it yet, but nobody stepped up to maintain it. It's already _mostly_ dead code since it can really only be used for AP and maybe IBSS and monitor, but not on a client since there's no way to scan (and hasn't been in a very long time, if ever), so the only thing that ever could really happen with it was run syzbot and trip over assumptions in the code. Signed-off-by: Johannes Berg Reviewed-by: Lachlan Hodges Link: https://patch.msgid.link/20260529084502.080c5885f0b7.I77cc94485b523c3c006005b9233db13cd4e077b3@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath5k/base.c | 2 -- drivers/net/wireless/ath/ath9k/init.c | 1 - .../wireless/virtual/mac80211_hwsim_main.c | 1 - include/net/cfg80211.h | 2 -- net/wireless/chan.c | 33 ++++--------------- net/wireless/core.c | 4 +-- net/wireless/nl80211.c | 14 ++------ net/wireless/util.c | 3 -- 8 files changed, 11 insertions(+), 49 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 6ca31d4ea437..610a3321b497 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2600,8 +2600,6 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) /* SW support for IBSS_RSN is provided by mac80211 */ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; - /* both antennas can be configured as RX or TX */ hw->wiphy->available_antennas_tx = 0x3; hw->wiphy->available_antennas_rx = 0x3; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index e1a67e8ed09f..2f0c4ef86b7e 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -957,7 +957,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 6ea082157307..4926122d6648 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -5903,7 +5903,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_SUPPORTS_5_10_MHZ | WIPHY_FLAG_HAS_CHANNEL_SWITCH; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR | diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 69dc9a978861..f91a71c7f4db 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5676,7 +5676,6 @@ struct cfg80211_ops { * responds to probe-requests in hardware. * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX. * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call. - * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels. * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in * beaconing mode (AP, IBSS, Mesh, ...). * @WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK: The device supports bigger kek and kck keys @@ -5716,7 +5715,6 @@ enum wiphy_flags { WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD = BIT(19), WIPHY_FLAG_OFFCHAN_TX = BIT(20), WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21), - WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22), WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23), WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER = BIT(24), WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON = BIT(25), diff --git a/net/wireless/chan.c b/net/wireless/chan.c index f0811efb5d0f..7f6af1790736 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -280,12 +280,6 @@ int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width) case NL80211_CHAN_WIDTH_16: mhz = 16; break; - case NL80211_CHAN_WIDTH_5: - mhz = 5; - break; - case NL80211_CHAN_WIDTH_10: - mhz = 10; - break; case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: mhz = 20; @@ -347,8 +341,6 @@ cfg80211_chandef_valid_control_freq(const struct cfg80211_chan_def *chandef, u32 control_freq) { switch (chandef->width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_1: @@ -415,8 +407,6 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) return false; switch (chandef->width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: if (ieee80211_chandef_to_khz(chandef) != @@ -803,18 +793,16 @@ _cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, return NULL; /* - * can't be compatible if one of them is 5/10 MHz or S1G + * can't be compatible if one of them is S1G * but they don't have the same width. */ -#define NARROW_OR_S1G(width) ((width) == NL80211_CHAN_WIDTH_5 || \ - (width) == NL80211_CHAN_WIDTH_10 || \ - (width) == NL80211_CHAN_WIDTH_1 || \ - (width) == NL80211_CHAN_WIDTH_2 || \ - (width) == NL80211_CHAN_WIDTH_4 || \ - (width) == NL80211_CHAN_WIDTH_8 || \ - (width) == NL80211_CHAN_WIDTH_16) +#define IS_S1G(width) ((width) == NL80211_CHAN_WIDTH_1 || \ + (width) == NL80211_CHAN_WIDTH_2 || \ + (width) == NL80211_CHAN_WIDTH_4 || \ + (width) == NL80211_CHAN_WIDTH_8 || \ + (width) == NL80211_CHAN_WIDTH_16) - if (NARROW_OR_S1G(c1->width) || NARROW_OR_S1G(c2->width)) + if (IS_S1G(c1->width) || IS_S1G(c2->width)) return NULL; /* @@ -1506,13 +1494,6 @@ bool _cfg80211_chandef_usable(struct wiphy *wiphy, control_freq = chandef->chan->center_freq; switch (chandef->width) { - case NL80211_CHAN_WIDTH_5: - width = 5; - break; - case NL80211_CHAN_WIDTH_10: - prohibited_flags |= IEEE80211_CHAN_NO_10MHZ; - width = 10; - break; case NL80211_CHAN_WIDTH_20: if (!ht_cap->ht_supported && chandef->chan->band != NL80211_BAND_6GHZ) diff --git a/net/wireless/core.c b/net/wireless/core.c index 62ab5e4639be..3dcf63b04c41 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -917,9 +917,7 @@ int wiphy_register(struct wiphy *wiphy) BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_80P80) | BIT(NL80211_CHAN_WIDTH_160) | - BIT(NL80211_CHAN_WIDTH_320) | - BIT(NL80211_CHAN_WIDTH_5) | - BIT(NL80211_CHAN_WIDTH_10)))) + BIT(NL80211_CHAN_WIDTH_320)))) return -EINVAL; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cdb5e9b77143..19244708f5c3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3450,11 +3450,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (nl80211_send_coalesce(msg, rdev)) goto nla_put_failure; - if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) && - (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) || - nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ))) - goto nla_put_failure; - if (rdev->wiphy.max_ap_assoc_sta && nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA, rdev->wiphy.max_ap_assoc_sta)) @@ -4085,10 +4080,9 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev, return -EINVAL; } - if ((chandef->width == NL80211_CHAN_WIDTH_5 || - chandef->width == NL80211_CHAN_WIDTH_10) && - !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ)) { - NL_SET_ERR_MSG(extack, "5/10 MHz not supported"); + if (chandef->width == NL80211_CHAN_WIDTH_5 || + chandef->width == NL80211_CHAN_WIDTH_10) { + NL_SET_ERR_MSG(extack, "5/10 MHz not supported any more"); return -EINVAL; } @@ -13410,8 +13404,6 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) return -EINVAL; switch (ibss.chandef.width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: break; case NL80211_CHAN_WIDTH_20: diff --git a/net/wireless/util.c b/net/wireless/util.c index 8dd7545b9097..24527bf321b2 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -2304,9 +2304,6 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, case NL80211_CHAN_WIDTH_80P80: vht_opclass = 130; break; - case NL80211_CHAN_WIDTH_10: - case NL80211_CHAN_WIDTH_5: - return false; /* unsupported for now */ default: vht_opclass = 0; break; From f9ad6c160224a871e9437363709c1ba6e8604359 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 08:40:28 +0200 Subject: [PATCH 125/285] wifi: mac80211: remove 5/10 MHz channel code Now that cfg80211 refuses all attempts to use 5/10 MHz channels, all of this code is unreachable; remove it. Signed-off-by: Johannes Berg Reviewed-by: Lachlan Hodges Link: https://patch.msgid.link/20260529084502.4e5a9350206c.I2f6169a067ddd1b5e234668fcb6e07957fafacf2@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 -- net/mac80211/chan.c | 4 +--- net/mac80211/ibss.c | 31 ++----------------------------- net/mac80211/mesh.c | 36 +++++++++--------------------------- net/mac80211/mesh_plink.c | 2 -- net/mac80211/mlme.c | 6 ++---- net/mac80211/offchannel.c | 4 +--- net/mac80211/rate.c | 8 +------- net/mac80211/spectmgmt.c | 10 ---------- net/mac80211/util.c | 7 ------- 10 files changed, 16 insertions(+), 94 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fb4c1c298159..1554e31f0029 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4591,8 +4591,6 @@ static int ieee80211_set_csa_beacon(struct ieee80211_link_data *link_data, cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) return -EINVAL; break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: break; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 23d46cd57137..5152b84a3357 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -642,9 +642,7 @@ __ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, lockdep_assert_wiphy(local->hw.wiphy); /* don't optimize non-20MHz based and radar_enabled confs */ - if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || - ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || - ctx->conf.def.width == NL80211_CHAN_WIDTH_1 || + if (ctx->conf.def.width == NL80211_CHAN_WIDTH_1 || ctx->conf.def.width == NL80211_CHAN_WIDTH_2 || ctx->conf.def.width == NL80211_CHAN_WIDTH_4 || ctx->conf.def.width == NL80211_CHAN_WIDTH_8 || diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 08690342cfaa..d0fd6054f182 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -167,8 +167,6 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, /* add HT capability and information IEs */ if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT && - chandef->width != NL80211_CHAN_WIDTH_5 && - chandef->width != NL80211_CHAN_WIDTH_10 && sband->ht_cap.ht_supported) { struct ieee80211_sta_ht_cap ht_cap; @@ -259,9 +257,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, chan = chanreq.oper.chan; if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chanreq.oper, NL80211_IFTYPE_ADHOC)) { - if (chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - chanreq.oper.width == NL80211_CHAN_WIDTH_10 || - chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || + if (chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || chanreq.oper.width == NL80211_CHAN_WIDTH_20) { sdata_info(sdata, "Failed to join IBSS, beacons forbidden\n"); @@ -405,12 +401,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, chan_type = cfg80211_get_chandef_type(&sdata->u.ibss.chandef); cfg80211_chandef_create(&chandef, cbss->channel, chan_type); break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - cfg80211_chandef_create(&chandef, cbss->channel, - NL80211_CHAN_NO_HT); - chandef.width = sdata->u.ibss.chandef.width; - break; case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_160: @@ -762,8 +752,6 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, lockdep_assert_wiphy(sdata->local->hw.wiphy); switch (ifibss->chandef.width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20_NOHT: conn.mode = IEEE80211_CONN_MODE_LEGACY; fallthrough; @@ -811,19 +799,6 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, cfg80211_chandef_create(¶ms.chandef, params.chandef.chan, ch_type); break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - if (params.chandef.width != ifibss->chandef.width) { - sdata_info(sdata, - "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", - ifibss->bssid, - params.chandef.chan->center_freq, - params.chandef.width, - params.chandef.center_freq1, - params.chandef.center_freq2); - goto disconnect; - } - break; default: /* should not happen, conn_flags should prevent VHT modes. */ WARN_ON(1); @@ -1005,9 +980,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, } if (sta && elems->ht_operation && elems->ht_cap_elem && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) { + sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { /* we both use HT */ struct ieee80211_ht_cap htcap_ie; struct cfg80211_chan_def chandef; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 04578447df9b..d4507e4e6ec1 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -439,9 +439,7 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!sband->ht_cap.ht_supported || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) @@ -480,9 +478,7 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!ht_cap->ht_supported || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation)) @@ -511,9 +507,7 @@ int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!sband->vht_cap.vht_supported || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_cap)) @@ -552,9 +546,7 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata, return 0; if (!vht_cap->vht_supported || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_vht_operation)) @@ -576,9 +568,7 @@ int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata, if (!sband) return -EINVAL; - if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; return ieee80211_put_he_cap(skb, sdata, sband, NULL); @@ -598,9 +588,7 @@ int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata, he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); if (!he_cap || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; len = 2 + 1 + sizeof(struct ieee80211_he_operation); @@ -648,9 +636,7 @@ int mesh_add_eht_cap_ie(struct ieee80211_sub_if_data *sdata, if (!sband) return -EINVAL; - if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + if (sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; return ieee80211_put_eht_cap(skb, sdata, sband, NULL); @@ -669,9 +655,7 @@ int mesh_add_eht_oper_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *sk eht_cap = ieee80211_get_eht_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT); if (!eht_cap || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; len = 2 + 1 + offsetof(struct ieee80211_eht_operation, optional) + @@ -729,9 +713,7 @@ ieee80211_mesh_update_bss_params(struct ieee80211_sub_if_data *sdata, return; if (!ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT) || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_10) + sdata->vif.bss_conf.chanreq.oper.width == NL80211_CHAN_WIDTH_20_NOHT) return; sdata->vif.bss_conf.he_support = true; diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 37adb053213e..268857467f29 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -165,8 +165,6 @@ static u64 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) switch (sdata->vif.bss_conf.chanreq.oper.width) { case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: return 0; default: break; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e8d6f6a95c0a..cfb5bc4eac69 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6376,10 +6376,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ret = ieee80211_link_use_channel(link, &chanreq, IEEE80211_CHANCTX_SHARED); - /* don't downgrade for 5/10/S1G MHz channels, though. */ - if (chanreq.oper.width == NL80211_CHAN_WIDTH_5 || - chanreq.oper.width == NL80211_CHAN_WIDTH_10 || - cfg80211_chandef_is_s1g(&chanreq.oper)) + /* don't downgrade for S1G channels, though. */ + if (cfg80211_chandef_is_s1g(&chanreq.oper)) return ret; while (ret && chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) { diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 8bec39b099a0..2bceb73717c6 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -355,9 +355,7 @@ static void _ieee80211_start_next_roc(struct ieee80211_local *local) * Note: scan can't run, tmp_channel is what we use, so this * must be the currently active channel. */ - roc->on_channel = roc->chan == local->hw.conf.chandef.chan && - local->hw.conf.chandef.width != NL80211_CHAN_WIDTH_5 && - local->hw.conf.chandef.width != NL80211_CHAN_WIDTH_10; + roc->on_channel = roc->chan == local->hw.conf.chandef.chan; /* start this ROC */ ieee80211_recalc_idle(local); diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index ba1a3aa3f5d4..64768abb0a5f 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -601,14 +601,8 @@ static void rate_idx_match_mask(s8 *rate_idx, u16 *rate_flags, return; /* if HT BSS, and we handle a data frame, also try HT rates */ - switch (chan_width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: + if (chan_width == NL80211_CHAN_WIDTH_20_NOHT) return; - default: - break; - } *rate_idx = 0; /* keep protection flags */ diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index e2eaf8d8d7ff..ec622750e1c9 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -329,16 +329,6 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, case -1: cfg80211_chandef_create(&csa_ie->chanreq.oper, new_chan, NL80211_CHAN_NO_HT); - /* keep width for 5/10 MHz channels */ - switch (sdata->vif.bss_conf.chanreq.oper.width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - csa_ie->chanreq.oper.width = - sdata->vif.bss_conf.chanreq.oper.width; - break; - default: - break; - } break; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2a7ab269687a..f6d4ae4127c8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3816,13 +3816,6 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c, conn->mode = IEEE80211_CONN_MODE_S1G; conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - WARN_ON_ONCE(1); - /* keep c->width */ - conn->mode = IEEE80211_CONN_MODE_LEGACY; - conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; - break; } if (new_primary_width != NL80211_CHAN_WIDTH_20_NOHT) { From 4ac20bd40b7db8568aea5ba1d390241842e41ed8 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:43 +0900 Subject: [PATCH 126/285] wifi: mac80211: Use struct instead of macro for PREQ frame The existing PREQ_IE_* macros access HWMP PREQ frame fields via hardcoded byte offsets. When the AE (Address Extension) flag is set, an additional 6 bytes appear mid-frame, and the macros handle this with conditional arithmetic (e.g., AE_F_SET(x) ? x + N+6 : x + N). This approach obscures the frame layout and is prone to miscalculation. Introduce typed packed C structs to represent the PREQ frame layout: - ieee80211_mesh_hwmp_preq_top: fixed fields before the optional AE address - ieee80211_mesh_hwmp_preq_bottom: fields after the optional AE address - ieee80211_mesh_hwmp_preq_target: per-target fields Add ieee80211_mesh_hwmp_preq_get_bottom() to locate the bottom struct correctly based on whether the AE flag is set. This preparatory refactoring is needed to fix a 2-byte overread of target_addr in hwmp_preq_frame_process() when AE is enabled, which is addressed in a subsequent patch. Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-1-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- include/linux/ieee80211-mesh.h | 42 ++++++++++++++++++++++ net/mac80211/mesh_hwmp.c | 65 ++++++++++++++++------------------ 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h index 4b829bcb38b6..bf4a544aed00 100644 --- a/include/linux/ieee80211-mesh.h +++ b/include/linux/ieee80211-mesh.h @@ -28,12 +28,40 @@ struct ieee80211s_hdr { u8 eaddr2[ETH_ALEN]; } __packed __aligned(2); +struct ieee80211_mesh_hwmp_preq_target { + u8 flags; + u8 addr[ETH_ALEN]; + __le32 sn; +} __packed; + +struct ieee80211_mesh_hwmp_preq_top { + u8 flags; + u8 hopcount; + u8 ttl; + __le32 preq_id; + u8 orig_addr[ETH_ALEN]; + __le32 orig_sn; + + /* optional AE, lifetime, metric, target */ + u8 variable[]; +} __packed; + +struct ieee80211_mesh_hwmp_preq_bottom { + __le32 lifetime; + __le32 metric; + u8 target_count; + struct ieee80211_mesh_hwmp_preq_target targets[]; +} __packed; + /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 #define MESH_FLAGS_AE_A5_A6 0x2 #define MESH_FLAGS_AE 0x3 #define MESH_FLAGS_PS_DEEP 0x4 +/* HWMP IE processing macros */ +#define AE_F (1<<6) + /** * enum ieee80211_preq_flags - mesh PREQ element flags * @@ -227,4 +255,18 @@ enum ieee80211_root_mode_identifier { IEEE80211_PROACTIVE_RANN = 4, }; +static inline bool ieee80211_mesh_preq_prep_ae_enabled(const u8 *ie) +{ + return ie[0] & AE_F; +} + +static inline struct ieee80211_mesh_hwmp_preq_bottom * +ieee80211_mesh_hwmp_preq_get_bottom(const u8 *ie) +{ + struct ieee80211_mesh_hwmp_preq_top *top = (void *)ie; + + return (void *)&top->variable[ + ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0]; +} + #endif /* LINUX_IEEE80211_MESH_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 9d89ebcce1c1..1a6a22b185d9 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -35,24 +35,11 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae) } /* HWMP IE processing macros */ -#define AE_F (1<<6) #define AE_F_SET(x) (*x & AE_F) -#define PREQ_IE_FLAGS(x) (*(x)) -#define PREQ_IE_HOPCOUNT(x) (*(x + 1)) -#define PREQ_IE_TTL(x) (*(x + 2)) -#define PREQ_IE_PREQ_ID(x) u32_field_get(x, 3, 0) -#define PREQ_IE_ORIG_ADDR(x) (x + 7) -#define PREQ_IE_ORIG_SN(x) u32_field_get(x, 13, 0) -#define PREQ_IE_LIFETIME(x) u32_field_get(x, 17, AE_F_SET(x)) -#define PREQ_IE_METRIC(x) u32_field_get(x, 21, AE_F_SET(x)) -#define PREQ_IE_TARGET_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26)) -#define PREQ_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27) -#define PREQ_IE_TARGET_SN(x) u32_field_get(x, 33, AE_F_SET(x)) - -#define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x) -#define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x) -#define PREP_IE_TTL(x) PREQ_IE_TTL(x) +#define PREP_IE_FLAGS(x) (*(x)) +#define PREP_IE_HOPCOUNT(x) (*(x + 1)) +#define PREP_IE_TTL(x) (*(x + 2)) #define PREP_IE_ORIG_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21) #define PREP_IE_ORIG_SN(x) u32_field_get(x, 27, AE_F_SET(x)) #define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x)) @@ -415,11 +402,16 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, switch (action) { case MPATH_PREQ: - orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie); - orig_sn = PREQ_IE_ORIG_SN(hwmp_ie); - orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie); - orig_metric = PREQ_IE_METRIC(hwmp_ie); - hopcount = PREQ_IE_HOPCOUNT(hwmp_ie) + 1; + struct ieee80211_mesh_hwmp_preq_top *preq_elem_top = + (void *)hwmp_ie; + struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = + ieee80211_mesh_hwmp_preq_get_bottom(hwmp_ie); + + orig_addr = preq_elem_top->orig_addr; + orig_sn = le32_to_cpu(preq_elem_top->orig_sn); + orig_lifetime = le32_to_cpu(preq_elem_bottom->lifetime); + orig_metric = le32_to_cpu(preq_elem_bottom->metric); + hopcount = preq_elem_top->hopcount + 1; break; case MPATH_PREP: /* Originator here refers to the MP that was the target in the @@ -579,6 +571,11 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, const u8 *preq_elem, u32 orig_metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_mesh_hwmp_preq_top *preq_elem_top = (void *)preq_elem; + struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = + ieee80211_mesh_hwmp_preq_get_bottom(preq_elem); + struct ieee80211_mesh_hwmp_preq_target *target = + preq_elem_bottom->targets; struct mesh_path *mpath = NULL; const u8 *target_addr, *orig_addr; const u8 *da; @@ -589,13 +586,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, bool root_is_gate; /* Update target SN, if present */ - target_addr = PREQ_IE_TARGET_ADDR(preq_elem); - orig_addr = PREQ_IE_ORIG_ADDR(preq_elem); - target_sn = PREQ_IE_TARGET_SN(preq_elem); - orig_sn = PREQ_IE_ORIG_SN(preq_elem); - target_flags = PREQ_IE_TARGET_F(preq_elem); + target_addr = target[0].addr; + orig_addr = preq_elem_top->orig_addr; + target_sn = le32_to_cpu(target[0].sn); + orig_sn = le32_to_cpu(preq_elem_top->orig_sn); + target_flags = target[0].flags; /* Proactive PREQ gate announcements */ - flags = PREQ_IE_FLAGS(preq_elem); + flags = preq_elem_top->flags; root_is_gate = !!(flags & RANN_FLAG_IS_GATE); mhwmp_dbg(sdata, "received PREQ from %pM\n", orig_addr); @@ -655,7 +652,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, } if (reply) { - lifetime = PREQ_IE_LIFETIME(preq_elem); + lifetime = le32_to_cpu(preq_elem_bottom->lifetime); ttl = ifmsh->mshcfg.element_ttl; if (ttl != 0) { mhwmp_dbg(sdata, "replying to the PREQ\n"); @@ -673,22 +670,22 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, u32 preq_id; u8 hopcount; - ttl = PREQ_IE_TTL(preq_elem); - lifetime = PREQ_IE_LIFETIME(preq_elem); + ttl = preq_elem_top->ttl; + lifetime = le32_to_cpu(preq_elem_bottom->lifetime); if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; return; } mhwmp_dbg(sdata, "forwarding the PREQ from %pM\n", orig_addr); --ttl; - preq_id = PREQ_IE_PREQ_ID(preq_elem); - hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1; + preq_id = le32_to_cpu(preq_elem_top->preq_id); + hopcount = preq_elem_top->hopcount + 1; da = (mpath && mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr; if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) { - target_addr = PREQ_IE_TARGET_ADDR(preq_elem); - target_sn = PREQ_IE_TARGET_SN(preq_elem); + target_addr = target[0].addr; + target_sn = le32_to_cpu(target[0].sn); } mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr, From a91c65cb99d1e03c8d8f0244258cbdd2d60faa86 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:44 +0900 Subject: [PATCH 127/285] wifi: mac80211: Use struct instead of macro for PREP frame The existing PREP_IE_* macros access HWMP PREP frame fields via hardcoded byte offsets. When the AE (Address Extension) flag is set, an additional 6 bytes appear mid-frame, making the offset arithmetic error-prone. Introduce typed packed C structs to represent the PREP frame layout: - ieee80211_mesh_hwmp_prep_top: fixed fields before the optional AE address - ieee80211_mesh_hwmp_prep_bottom: fields after the optional AE address Add ieee80211_mesh_hwmp_prep_get_bottom() to locate the bottom struct correctly based on whether the AE flag is set. This preparatory refactoring is needed to fix a 2-byte overread of orig_addr in hwmp_prep_frame_process() when AE is enabled, which is addressed in a subsequent patch. Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-2-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- include/linux/ieee80211-mesh.h | 27 ++++++++++++++++++++ net/mac80211/mesh_hwmp.c | 46 ++++++++++++++++------------------ 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h index bf4a544aed00..4ce4e47d6d01 100644 --- a/include/linux/ieee80211-mesh.h +++ b/include/linux/ieee80211-mesh.h @@ -53,6 +53,24 @@ struct ieee80211_mesh_hwmp_preq_bottom { struct ieee80211_mesh_hwmp_preq_target targets[]; } __packed; +struct ieee80211_mesh_hwmp_prep_top { + u8 flags; + u8 hopcount; + u8 ttl; + u8 target_addr[ETH_ALEN]; + __le32 target_sn; + + /* optional Target External Address */ + u8 variable[]; +} __packed; + +struct ieee80211_mesh_hwmp_prep_bottom { + __le32 lifetime; + __le32 metric; + u8 orig_addr[ETH_ALEN]; + __le32 orig_sn; +} __packed; + /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 #define MESH_FLAGS_AE_A5_A6 0x2 @@ -269,4 +287,13 @@ ieee80211_mesh_hwmp_preq_get_bottom(const u8 *ie) ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0]; } +static inline struct ieee80211_mesh_hwmp_prep_bottom * +ieee80211_mesh_hwmp_prep_get_bottom(const u8 *ie) +{ + struct ieee80211_mesh_hwmp_prep_top *top = (void *)ie; + + return (void *)&top->variable[ + ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0]; +} + #endif /* LINUX_IEEE80211_MESH_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 1a6a22b185d9..39b782370df0 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -37,16 +37,6 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae) /* HWMP IE processing macros */ #define AE_F_SET(x) (*x & AE_F) -#define PREP_IE_FLAGS(x) (*(x)) -#define PREP_IE_HOPCOUNT(x) (*(x + 1)) -#define PREP_IE_TTL(x) (*(x + 2)) -#define PREP_IE_ORIG_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21) -#define PREP_IE_ORIG_SN(x) u32_field_get(x, 27, AE_F_SET(x)) -#define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x)) -#define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x)) -#define PREP_IE_TARGET_ADDR(x) (x + 3) -#define PREP_IE_TARGET_SN(x) u32_field_get(x, 9, 0) - #define PERR_IE_TTL(x) (*(x)) #define PERR_IE_TARGET_FLAGS(x) (*(x + 2)) #define PERR_IE_TARGET_ADDR(x) (x + 3) @@ -419,11 +409,16 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, * so that we can easily use a single function to gather path * information from both PREQ and PREP frames. */ - orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie); - orig_sn = PREP_IE_TARGET_SN(hwmp_ie); - orig_lifetime = PREP_IE_LIFETIME(hwmp_ie); - orig_metric = PREP_IE_METRIC(hwmp_ie); - hopcount = PREP_IE_HOPCOUNT(hwmp_ie) + 1; + struct ieee80211_mesh_hwmp_prep_top *prep_elem_top = + (void *)hwmp_ie; + struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom = + ieee80211_mesh_hwmp_prep_get_bottom(hwmp_ie); + + orig_addr = prep_elem_top->target_addr; + orig_sn = le32_to_cpu(prep_elem_top->target_sn); + orig_lifetime = le32_to_cpu(prep_elem_bottom->lifetime); + orig_metric = le32_to_cpu(prep_elem_bottom->metric); + hopcount = prep_elem_top->hopcount + 1; break; default: rcu_read_unlock(); @@ -714,6 +709,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, const u8 *prep_elem, u32 metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_mesh_hwmp_prep_top *prep_elem_top = (void *)prep_elem; + struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom = + ieee80211_mesh_hwmp_prep_get_bottom(prep_elem); struct mesh_path *mpath; const u8 *target_addr, *orig_addr; u8 ttl, hopcount, flags; @@ -721,9 +719,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, u32 target_sn, orig_sn, lifetime; mhwmp_dbg(sdata, "received PREP from %pM\n", - PREP_IE_TARGET_ADDR(prep_elem)); + prep_elem_top->target_addr); - orig_addr = PREP_IE_ORIG_ADDR(prep_elem); + orig_addr = prep_elem_bottom->orig_addr; if (ether_addr_equal(orig_addr, sdata->vif.addr)) /* destination, no forwarding required */ return; @@ -731,7 +729,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, if (!ifmsh->mshcfg.dot11MeshForwarding) return; - ttl = PREP_IE_TTL(prep_elem); + ttl = prep_elem_top->ttl; if (ttl <= 1) { sdata->u.mesh.mshstats.dropped_frames_ttl++; return; @@ -750,12 +748,12 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN); spin_unlock_bh(&mpath->state_lock); --ttl; - flags = PREP_IE_FLAGS(prep_elem); - lifetime = PREP_IE_LIFETIME(prep_elem); - hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1; - target_addr = PREP_IE_TARGET_ADDR(prep_elem); - target_sn = PREP_IE_TARGET_SN(prep_elem); - orig_sn = PREP_IE_ORIG_SN(prep_elem); + flags = prep_elem_top->flags; + lifetime = le32_to_cpu(prep_elem_bottom->lifetime); + hopcount = prep_elem_top->hopcount + 1; + target_addr = prep_elem_top->target_addr; + target_sn = le32_to_cpu(prep_elem_top->target_sn); + orig_sn = le32_to_cpu(prep_elem_bottom->orig_sn); mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, orig_sn, 0, target_addr, target_sn, next_hop, hopcount, From 68511e16320b19387a8593c578a7ebe307c05166 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:45 +0900 Subject: [PATCH 128/285] wifi: mac80211: Use struct instead of macro for PERR frame The existing PERR_IE_* macros access HWMP PERR frame fields via hardcoded byte offsets. Each PERR destination entry contains an optional 6-byte AE (Address Extension) address followed by a reason code, making offset-based access error-prone. Introduce typed packed C structs to represent the PERR frame layout: - ieee80211_mesh_hwmp_perr: top-level frame containing TTL and destination count - ieee80211_mesh_hwmp_perr_dst: per-destination entry with optional AE address and variable-position reason code Add ieee80211_mesh_hwmp_perr_get_rcode() to locate the reason code in each destination entry depending on whether the AE flag is set. This refactoring makes the PERR processing code consistent with the struct-based approach adopted for PREQ and PREP in preceding patches. Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-3-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- include/linux/ieee80211-mesh.h | 62 ++++++++++++++++++++++++++++++++++ net/mac80211/mesh_hwmp.c | 31 +++-------------- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h index 4ce4e47d6d01..f709263c310b 100644 --- a/include/linux/ieee80211-mesh.h +++ b/include/linux/ieee80211-mesh.h @@ -71,6 +71,21 @@ struct ieee80211_mesh_hwmp_prep_bottom { __le32 orig_sn; } __packed; +struct ieee80211_mesh_hwmp_perr_dst { + u8 flags; + u8 addr[ETH_ALEN]; + __le32 sn; + /* optional Destination External Address */ + u8 variable[]; +} __packed; + +struct ieee80211_mesh_hwmp_perr { + u8 ttl; + u8 number_of_dst; + /* Destinations */ + u8 variable[]; +} __packed; + /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 #define MESH_FLAGS_AE_A5_A6 0x2 @@ -296,4 +311,51 @@ ieee80211_mesh_hwmp_prep_get_bottom(const u8 *ie) ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0]; } +static inline struct ieee80211_mesh_hwmp_perr_dst * +ieee80211_mesh_hwmp_perr_get_dst(const u8 *ie, u8 dst_idx) +{ + struct ieee80211_mesh_hwmp_perr *perr_ie = (void *)ie; + struct ieee80211_mesh_hwmp_perr_dst *dst; + u8 *pos = perr_ie->variable; + int i; + + for (i = 0; i < dst_idx + 1; i++) { + dst = (void *)pos; + pos += sizeof(struct ieee80211_mesh_hwmp_perr_dst) + + ((dst->flags & AE_F) ? ETH_ALEN : 0) + /* Destination External Address */ + + 2 /* Reason Code */; + } + + return dst; +} + +static inline u8 * +ieee80211_mesh_hwmp_perr_get_addr(const u8 *ie, u8 dst_idx) +{ + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx); + + return dst->addr; +} + +static inline u32 +ieee80211_mesh_hwmp_perr_get_sn(const u8 *ie, u8 dst_idx) +{ + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx); + + return le32_to_cpu(dst->sn); +} + +static inline u16 +ieee80211_mesh_hwmp_perr_get_rcode(const u8 *ie, u8 dst_idx) +{ + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx); + + return get_unaligned_le16(&dst->variable[ + (dst->flags & AE_F) ? ETH_ALEN : 0]); +} + #endif /* LINUX_IEEE80211_MESH_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 39b782370df0..378338778a23 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -20,29 +20,7 @@ static void mesh_queue_preq(struct mesh_path *, u8); -static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae) -{ - if (ae) - offset += 6; - return get_unaligned_le32(preq_elem + offset); -} - -static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae) -{ - if (ae) - offset += 6; - return get_unaligned_le16(preq_elem + offset); -} - /* HWMP IE processing macros */ -#define AE_F_SET(x) (*x & AE_F) - -#define PERR_IE_TTL(x) (*(x)) -#define PERR_IE_TARGET_FLAGS(x) (*(x + 2)) -#define PERR_IE_TARGET_ADDR(x) (x + 3) -#define PERR_IE_TARGET_SN(x) u32_field_get(x, 9, 0) -#define PERR_IE_TARGET_RCODE(x) u16_field_get(x, 13, 0) - #define MSEC_TO_TU(x) (x*1000/1024) #define SN_GT(x, y) ((s32)(y - x) < 0) #define SN_LT(x, y) ((s32)(x - y) < 0) @@ -774,6 +752,7 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, const u8 *perr_elem) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_mesh_hwmp_perr *perr_elem_s = (void *)perr_elem; struct mesh_path *mpath; u8 ttl; const u8 *ta, *target_addr; @@ -781,15 +760,15 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, u16 target_rcode; ta = mgmt->sa; - ttl = PERR_IE_TTL(perr_elem); + ttl = perr_elem_s->ttl; if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; return; } ttl--; - target_addr = PERR_IE_TARGET_ADDR(perr_elem); - target_sn = PERR_IE_TARGET_SN(perr_elem); - target_rcode = PERR_IE_TARGET_RCODE(perr_elem); + target_addr = ieee80211_mesh_hwmp_perr_get_addr(perr_elem, 0); + target_sn = ieee80211_mesh_hwmp_perr_get_sn(perr_elem, 0); + target_rcode = ieee80211_mesh_hwmp_perr_get_rcode(perr_elem, 0); rcu_read_lock(); mpath = mesh_path_lookup(sdata, target_addr); From 8b40b1d24a6099fe9fac8e207d4cb04ab5e0baae Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:46 +0900 Subject: [PATCH 129/285] wifi: mac80211: Fix overread in PREQ frame processing When the AF flag is enabled, hwmp_preq_frame_process() overreads target_addr by 2 bytes. Since this occurs within the socket buffer, it does not read across memory boundaries and therefore poses no security risk; however, we will fix it as a precaution. In this fix, a new function mesh_path_parse_request_frame() is established to separate the implementation of frame format validation and the check for unsupported features. This is intended to facilitate future work when implementing the currently unsupported parts. Assisted-by: Claude:Sonnet 4.6 Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-4-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- include/linux/ieee80211-mesh.h | 29 +++++++++++++++++++++++++++++ net/mac80211/mesh_hwmp.c | 12 ++++++++++-- net/mac80211/parse.c | 9 +++++++-- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h index f709263c310b..8fbd31d9538d 100644 --- a/include/linux/ieee80211-mesh.h +++ b/include/linux/ieee80211-mesh.h @@ -358,4 +358,33 @@ ieee80211_mesh_hwmp_perr_get_rcode(const u8 *ie, u8 dst_idx) (dst->flags & AE_F) ? ETH_ALEN : 0]); } +/* IEEE Std 802.11-2016 9.4.2.113 PREQ element */ +static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen) +{ + struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = + ieee80211_mesh_hwmp_preq_get_bottom(pos); + u8 target_count; + int needed; + + /* Check if the element contains flags */ + needed = sizeof(struct ieee80211_mesh_hwmp_preq_top); + if (elen < needed) + return false; + + /* Check if the element contains target_count */ + needed += (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0) + /* Originator External Address */ + + sizeof(struct ieee80211_mesh_hwmp_preq_bottom); + if (elen < needed) + return false; + + target_count = preq_elem_bottom->target_count; + /* IEEE Std 802.11-2016 Table 14-10 to 14-16 */ + if (target_count < 1) + return false; + + needed += target_count * sizeof(struct ieee80211_mesh_hwmp_preq_target); + return elen == needed; +} + #endif /* LINUX_IEEE80211_MESH_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 378338778a23..ef6eff52f32a 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -929,9 +929,17 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, return; if (elems->preq) { - if (elems->preq_len != 37) - /* Right now we support just 1 destination and no AE */ + struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = + ieee80211_mesh_hwmp_preq_get_bottom(elems->preq); + + /* Right now we do not support AE (Address Extension) */ + if (ieee80211_mesh_preq_prep_ae_enabled(elems->preq)) goto free; + + /* Right now we only support 1 target */ + if (preq_elem_bottom->target_count != 1) + goto free; + path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq, MPATH_PREQ); if (path_metric) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 8b30e361b622..3d441ff9593d 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -563,8 +563,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, elems->awake_window = (void *)pos; break; case WLAN_EID_PREQ: - elems->preq = pos; - elems->preq_len = elen; + if (ieee80211_mesh_preq_size_ok(pos, elen)) { + elems->preq = pos; + elems->preq_len = elen; + } else { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } break; case WLAN_EID_PREP: elems->prep = pos; From d158e54476ea9667c33dfa2c8d87c7cc32b40f1b Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:47 +0900 Subject: [PATCH 130/285] wifi: mac80211: Fix overread in PREP frame processing When the AF flag is enabled, hwmp_prep_frame_process() overreads orig_addr by 2 bytes. Since this occurs within the socket buffer, it does not read across memory boundaries and therefore poses no security risk; however, we will fix it as a precaution. In this fix, a new function mesh_path_parse_reply_frame() is established to separate the implementation of frame format validation and the check for unsupported features. This is intended to facilitate future work when implementing the currently unsupported parts. Assisted-by: Claude:Sonnet 4.6 Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-5-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- include/linux/ieee80211-mesh.h | 16 ++++++++++++++++ net/mac80211/mesh_hwmp.c | 4 ++-- net/mac80211/parse.c | 9 +++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h index 8fbd31d9538d..482ac0c6d759 100644 --- a/include/linux/ieee80211-mesh.h +++ b/include/linux/ieee80211-mesh.h @@ -387,4 +387,20 @@ static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen) return elen == needed; } +/* IEEE Std 802.11-2016 9.4.2.114 PREP element */ +static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen) +{ + u8 needed; + + /* Check if the element contains flags */ + needed = sizeof(struct ieee80211_mesh_hwmp_prep_top); + if (elen < needed) + return false; + + needed += (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0) + /* Target External Address */ + + sizeof(struct ieee80211_mesh_hwmp_prep_bottom); + return elen == needed; +} + #endif /* LINUX_IEEE80211_MESH_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index ef6eff52f32a..f07e57d5568a 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -947,8 +947,8 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, path_metric); } if (elems->prep) { - if (elems->prep_len != 31) - /* Right now we support no AE */ + /* Right now we do not support AE (Address Extension) */ + if (ieee80211_mesh_preq_prep_ae_enabled(elems->prep)) goto free; path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep, MPATH_PREP); diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 3d441ff9593d..97508b141b8c 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -572,8 +572,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, } break; case WLAN_EID_PREP: - elems->prep = pos; - elems->prep_len = elen; + if (ieee80211_mesh_prep_size_ok(pos, elen)) { + elems->prep = pos; + elems->prep_len = elen; + } else { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } break; case WLAN_EID_PERR: elems->perr = pos; From 4ec9ea8a58859c260ae32e097aa643975572e58b Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:48 +0900 Subject: [PATCH 131/285] wifi: mac80211: Fix PERR frame processing There are no issues with the PERR processing itself; however, to maintain consistency with the previous PREQ/PREP code modifications, I will create a new mesh_path_parse_error_frame() function to separately implement the frame format validation and the "not supported" check. Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-6-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- include/linux/ieee80211-mesh.h | 36 ++++++++++++++++++++++++++++++++++ net/mac80211/mesh_hwmp.c | 18 +++++++++++++++-- net/mac80211/parse.c | 9 +++++++-- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h index 482ac0c6d759..7eb15834531c 100644 --- a/include/linux/ieee80211-mesh.h +++ b/include/linux/ieee80211-mesh.h @@ -403,4 +403,40 @@ static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen) return elen == needed; } +/* IEEE Std 802.11-2016 9.4.2.115 PERR element */ +static inline bool ieee80211_mesh_perr_size_ok(const u8 *pos, u8 elen) +{ + struct ieee80211_mesh_hwmp_perr *perr_elem = (void *)pos; + const u8 *start = pos; + u8 number_of_dst; + int needed; + int i; + + needed = sizeof(struct ieee80211_mesh_hwmp_perr); + + /* Check if the element contains number of dst */ + if (elen < needed) + return false; + + pos += sizeof(struct ieee80211_mesh_hwmp_perr); + number_of_dst = perr_elem->number_of_dst; + + for (i = 0; i < number_of_dst; i++) { + struct ieee80211_mesh_hwmp_perr_dst *dst = (void *)pos; + u8 dst_len = sizeof(struct ieee80211_mesh_hwmp_perr_dst); + + /* Check if the element contains flags */ + if (elen < pos - start + dst_len) + return false; + + dst_len += ((dst->flags & AE_F) ? ETH_ALEN : 0) + /* Destination External Address */ + + 2 /* Reason Code */; + needed += dst_len; + pos += dst_len; + } + + return elen == needed; +} + #endif /* LINUX_IEEE80211_MESH_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index f07e57d5568a..84903737271d 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -957,9 +957,23 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, path_metric); } if (elems->perr) { - if (elems->perr_len != 15) - /* Right now we support only one destination per PERR */ + struct ieee80211_mesh_hwmp_perr *perr_elem = + (struct ieee80211_mesh_hwmp_perr *)elems->perr; + int i; + + /* Right now we support only one destination per PERR */ + if (perr_elem->number_of_dst != 1) goto free; + + /* Right now we do not support AE (Address Extension) */ + for (i = 0; i < perr_elem->number_of_dst; i++) { + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst(elems->perr, i); + + if (dst->flags & AE_F) + goto free; + } + hwmp_perr_frame_process(sdata, mgmt, elems->perr); } if (elems->rann) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 97508b141b8c..c44e81a2f80d 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -581,8 +581,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, } break; case WLAN_EID_PERR: - elems->perr = pos; - elems->perr_len = elen; + if (ieee80211_mesh_perr_size_ok(pos, elen)) { + elems->perr = pos; + elems->perr_len = elen; + } else { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } break; case WLAN_EID_RANN: if (elen >= sizeof(struct ieee80211_rann_ie)) From 42d02dce881ed328a4637b22623deaeebeef9a41 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:49 +0900 Subject: [PATCH 132/285] wifi: mac80211: Add KUnit test for ieee80211_mesh_preq_size_ok Add a kunit test for ieee80211_mesh_preq_size_ok(), checking various success and failure cases. Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-7-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/tests/elems.c | 105 +++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c index 1039794a0183..576ba746a526 100644 --- a/net/mac80211/tests/elems.c +++ b/net/mac80211/tests/elems.c @@ -9,6 +9,94 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); +static const struct mesh_preq_parse_test_case { + const char *desc; + u8 len; + bool ae_enabled; + u8 target_count; + bool result; +} mesh_preq_parse_cases[] = { + { + .desc = "shorter than header", + .len = 16, + .ae_enabled = false, + .target_count = 1, + .result = false, + }, + { + .desc = "too short non AE, target count is not included", + .len = 29, + .ae_enabled = false, + .target_count = 1, + .result = false, + }, + { + .desc = "too short non AE, target count is 1", + .len = 36, + .ae_enabled = false, + .target_count = 1, + .result = false, + }, + { + .desc = "too short AE, target count is not included", + .len = 35, + .ae_enabled = true, + .target_count = 1, + .result = false, + }, + { + .desc = "too short AE, target count is 1", + .len = 42, + .ae_enabled = true, + .target_count = 1, + .result = false, + }, + { + .desc = "target count is zero", + .len = 26, + .ae_enabled = false, + .target_count = 0, + .result = false, + }, + { + .desc = "target count is 21", + .len = 255, + .ae_enabled = false, + .target_count = 21, + .result = false, + }, + { + .desc = "non AE, target count is 1", + .len = 37, + .ae_enabled = false, + .target_count = 1, + .result = true, + }, + { + .desc = "non AE, target count is 20", + .len = 246, + .ae_enabled = false, + .target_count = 20, + .result = true, + }, + { + .desc = "AE, target count is 1", + .len = 43, + .ae_enabled = true, + .target_count = 1, + .result = true, + }, + { + .desc = "AE, target count is 20", + .len = 252, + .ae_enabled = true, + .target_count = 20, + .result = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(mesh_preq_parse, mesh_preq_parse_cases, desc); + static void mle_defrag(struct kunit *test) { struct ieee80211_elems_parse_params parse_params = { @@ -91,8 +179,25 @@ static void mle_defrag(struct kunit *test) kfree_skb(skb); } +static void mesh_preq_parse(struct kunit *test) +{ + const struct mesh_preq_parse_test_case *params = test->param_value; + u8 data[64] = {}; + struct ieee80211_mesh_hwmp_preq_top *top = (void *)data; + struct ieee80211_mesh_hwmp_preq_bottom *bottom; + + top->flags = params->ae_enabled ? AE_F : 0; + bottom = ieee80211_mesh_hwmp_preq_get_bottom(data); + bottom->target_count = params->target_count; + + KUNIT_EXPECT_EQ(test, + ieee80211_mesh_preq_size_ok(data, params->len), + params->result); +} + static struct kunit_case element_parsing_test_cases[] = { KUNIT_CASE(mle_defrag), + KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params), {} }; From 80da5c43057c9a8fc381121213447124abd9ee59 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:50 +0900 Subject: [PATCH 133/285] wifi: mac80211: Add KUnit test for ieee80211_mesh_prep_size_ok Add a kunit test for ieee80211_mesh_prep_size_ok(), checking various success and failure cases. Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-8-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/tests/elems.c | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c index 576ba746a526..b96424d5d025 100644 --- a/net/mac80211/tests/elems.c +++ b/net/mac80211/tests/elems.c @@ -97,6 +97,46 @@ static const struct mesh_preq_parse_test_case { KUNIT_ARRAY_PARAM_DESC(mesh_preq_parse, mesh_preq_parse_cases, desc); +static const struct mesh_prep_parse_test_case { + const char *desc; + u8 len; + bool ae_enabled; + bool result; +} mesh_prep_parse_cases[] = { + { + .desc = "shorter than header", + .len = 12, + .ae_enabled = false, + .result = false, + }, + { + .desc = "non AE short", + .len = 30, + .ae_enabled = false, + .result = false, + }, + { + .desc = "non AE", + .len = 31, + .ae_enabled = false, + .result = true, + }, + { + .desc = "AE short", + .len = 36, + .ae_enabled = true, + .result = false, + }, + { + .desc = "AE", + .len = 37, + .ae_enabled = true, + .result = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(mesh_prep_parse, mesh_prep_parse_cases, desc); + static void mle_defrag(struct kunit *test) { struct ieee80211_elems_parse_params parse_params = { @@ -195,9 +235,22 @@ static void mesh_preq_parse(struct kunit *test) params->result); } +static void mesh_prep_parse(struct kunit *test) +{ + const struct mesh_prep_parse_test_case *params = test->param_value; + u8 data[64] = {}; + struct ieee80211_mesh_hwmp_prep_top *top = (void *)data; + top->flags = params->ae_enabled ? AE_F : 0; + + KUNIT_EXPECT_EQ(test, + ieee80211_mesh_prep_size_ok(data, params->len), + params->result); +} + static struct kunit_case element_parsing_test_cases[] = { KUNIT_CASE(mle_defrag), KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params), + KUNIT_CASE_PARAM(mesh_prep_parse, mesh_prep_parse_gen_params), {} }; From 58b14058ede0476ff746190873522ab176b9de38 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Sat, 30 May 2026 08:09:51 +0900 Subject: [PATCH 134/285] wifi: mac80211: Add KUnit test for ieee80211_mesh_perr_size_ok Add a kunit test for ieee80211_mesh_perr_size_ok(), checking various success and failure cases. Signed-off-by: Masashi Honma Link: https://patch.msgid.link/20260529230952.124754-9-masashi.honma@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/tests/elems.c | 124 +++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c index b96424d5d025..4b13a5aad875 100644 --- a/net/mac80211/tests/elems.c +++ b/net/mac80211/tests/elems.c @@ -137,6 +137,109 @@ static const struct mesh_prep_parse_test_case { KUNIT_ARRAY_PARAM_DESC(mesh_prep_parse, mesh_prep_parse_cases, desc); +static const struct mesh_perr_parse_test_case { + const char *desc; + u8 len; + u8 number_of_dst; + int ae_enabled_idx; + bool result; +} mesh_perr_parse_cases[] = { + { + .desc = "shorter than header", + .len = 1, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 0", + .len = 2, + .number_of_dst = 0, + .ae_enabled_idx = -1, + .result = true, + }, + { + .desc = "number_of_dst is 20", + .len = 255, + .number_of_dst = 20, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 1, non AE, short", + .len = 14, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 1, non AE", + .len = 15, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = true, + }, + { + .desc = "number_of_dst is 1, non AE, extra short dst header", + .len = 25, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 1, non AE, extra dst header", + .len = 26, + .number_of_dst = 1, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 1, AE, short", + .len = 20, + .number_of_dst = 1, + .ae_enabled_idx = 0, + .result = false, + }, + { + .desc = "number_of_dst is 1, AE", + .len = 21, + .number_of_dst = 1, + .ae_enabled_idx = 0, + .result = true, + }, + { + .desc = "number_of_dst is 19, non AE, short", + .len = 2 + 13 * 19 - 1, + .number_of_dst = 19, + .ae_enabled_idx = -1, + .result = false, + }, + { + .desc = "number_of_dst is 19, non AE", + .len = 2 + 13 * 19, + .number_of_dst = 19, + .ae_enabled_idx = -1, + .result = true, + }, + { + .desc = "number_of_dst is 19, AE, short", + .len = 2 + 13 * 19 + 6 - 1, + .number_of_dst = 19, + .ae_enabled_idx = 18, + .result = false, + }, + { + .desc = "number_of_dst is 19, AE", + .len = 2 + 13 * 19 + 6, + .number_of_dst = 19, + .ae_enabled_idx = 18, + .result = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(mesh_perr_parse, mesh_perr_parse_cases, desc); + + static void mle_defrag(struct kunit *test) { struct ieee80211_elems_parse_params parse_params = { @@ -247,10 +350,31 @@ static void mesh_prep_parse(struct kunit *test) params->result); } +static void mesh_perr_parse(struct kunit *test) +{ + const struct mesh_perr_parse_test_case *params = test->param_value; + u8 data[256] = {}; + struct ieee80211_mesh_hwmp_perr *perr = (void *)data; + + perr->number_of_dst = params->number_of_dst; + if (params->ae_enabled_idx > -1) { + struct ieee80211_mesh_hwmp_perr_dst *dst = + ieee80211_mesh_hwmp_perr_get_dst( + data, params->ae_enabled_idx); + + dst->flags = AE_F; + } + + KUNIT_EXPECT_EQ(test, + ieee80211_mesh_perr_size_ok(data, params->len), + params->result); +} + static struct kunit_case element_parsing_test_cases[] = { KUNIT_CASE(mle_defrag), KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params), KUNIT_CASE_PARAM(mesh_prep_parse, mesh_prep_parse_gen_params), + KUNIT_CASE_PARAM(mesh_perr_parse, mesh_perr_parse_gen_params), {} }; From 5a9c554ff86d7f3750d95ce72ad0666e02e37b92 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Sun, 31 May 2026 22:54:35 +0800 Subject: [PATCH 135/285] wifi: qtnfmac: topaz: defer IRQ enabling until IPC init qtnf_pcie_topaz_probe() currently calls devm_request_irq() and only then disable_irq(). request_irq() installs the action in the irq core immediately, so qtnf_pcie_topaz_interrupt() can run before the Topaz private IRQ consumers are initialized, if the hardware misbehaves. This window is reachable on a running system as soon as probe has successfully registered pdev->irq but before qtnf_pcie_init_shm_ipc() sets shm_ipc_ep_in/out.irq_handler. If an interrupt is delivered in this interval, qtnf_pcie_topaz_interrupt() calls qtnf_shm_ipc_irq_handler() for shm_ipc_ep_in/out while their irq_handler callbacks are still unset, so the driver can observe an early IRQ before its IPC consumer state is ready. The issue was found on Linux v6.18.21 by our static analysis tool while scanning request_irq()/disable_irq() registration-order bugs in wireless PCIe drivers, and then manually reviewed. Request the IRQ with IRQF_NO_AUTOEN instead and keep the existing enable_irq() in qtnf_post_init_ep() as the point where interrupts become visible. This closes the early-IRQ window while preserving the intended bring-up order. Signed-off-by: Runyu Xiao Link: https://patch.msgid.link/20260531145435.701703-1-runyu.xiao@seu.edu.cn Signed-off-by: Johannes Berg --- drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c index ef5c069542d4..a3a285f17dca 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c +++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c @@ -1132,9 +1132,9 @@ static int qtnf_pcie_topaz_probe(struct qtnf_bus *bus, /* assign host msi irq before card init */ if (ts->base.msi_enabled) - irqflags = IRQF_NOBALANCING; + irqflags = IRQF_NOBALANCING | IRQF_NO_AUTOEN; else - irqflags = IRQF_NOBALANCING | IRQF_SHARED; + irqflags = IRQF_NOBALANCING | IRQF_SHARED | IRQF_NO_AUTOEN; ret = devm_request_irq(&pdev->dev, pdev->irq, &qtnf_pcie_topaz_interrupt, @@ -1144,8 +1144,6 @@ static int qtnf_pcie_topaz_probe(struct qtnf_bus *bus, return ret; } - disable_irq(pdev->irq); - ret = qtnf_pre_init_ep(bus); if (ret) { pr_err("failed to init card\n"); From b3d519330554b0b9e662cefebba854e0a7c1a592 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Tue, 2 Jun 2026 16:22:24 +1000 Subject: [PATCH 136/285] wifi: mac80211: basic S1G rx rate reporting support Introduce basic rate encoding/decoding for S1G stas such that the usermode rx reporting is relevant as it currently uses VHT calculations which are obviously wildy different to S1G. Sample iw output (with the associated iw patches applied): Connected to 0c:bf:74:00:21:c4 (on wlan0) SSID: wifi_halow freq: 923.500 RX: 7325230 bytes (4756 packets) TX: 190044 bytes (2238 packets) signal: -38 dBm rx bitrate: 43.3 MBit/s S1G-MCS 9 8MHz short GI S1G-NSS 1 tx bitrate: 43.3 MBit/s S1G-MCS 9 8MHz short GI S1G-NSS 1 bss flags: dtim period: 1 beacon int: 100 Signed-off-by: Lachlan Hodges Link: https://patch.msgid.link/20260602062224.1792985-1-lachlan.hodges@morsemicro.com Signed-off-by: Johannes Berg --- include/net/mac80211.h | 1 + net/mac80211/rx.c | 8 ++++++++ net/mac80211/sta_info.c | 7 +++++++ net/mac80211/sta_info.h | 11 ++++++++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4fb579805e0f..7dd558f4025b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1704,6 +1704,7 @@ enum mac80211_rx_encoding { RX_ENC_HE, RX_ENC_EHT, RX_ENC_UHR, + RX_ENC_S1G, }; /** diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index ef6086b183f7..91b4f6cbfce8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -5621,6 +5621,14 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, status->rate_idx, status->nss)) goto drop; break; + case RX_ENC_S1G: + if (WARN_ONCE(status->rate_idx > 12 || + !status->nss || + status->nss > 4, + "Rate marked as an S1G rate but data is invalid: MCS: %d, NSS: %d\n", + status->rate_idx, status->nss)) + goto drop; + break; default: WARN_ON_ONCE(1); fallthrough; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4c86a3793804..857c7d3355c7 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2605,6 +2605,13 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate, if (STA_STATS_GET(UHR_IM, rate)) rinfo->flags |= RATE_INFO_FLAGS_UHR_IM; break; + case STA_STATS_RATE_TYPE_S1G: + rinfo->flags = RATE_INFO_FLAGS_S1G_MCS; + rinfo->mcs = STA_STATS_GET(S1G_MCS, rate); + rinfo->nss = STA_STATS_GET(S1G_NSS, rate); + if (STA_STATS_GET(SGI, rate)) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; + break; } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 39608a0abbb5..e1837e986837 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -1042,7 +1042,7 @@ enum sta_stats_type { #define STA_STATS_FIELD_VHT_MCS 0x0000F000 #define STA_STATS_FIELD_VHT_NSS 0x000F0000 -/* HT & VHT */ +/* HT, VHT & S1G */ #define STA_STATS_FIELD_SGI 0x00100000 /* STA_STATS_RATE_TYPE_HE */ @@ -1066,6 +1066,9 @@ enum sta_stats_type { #define STA_STATS_FIELD_UHR_ELR 0x08000000 #define STA_STATS_FIELD_UHR_IM 0x10000000 +/* STA_STATS_RATE_TYPE_S1G */ +#define STA_STATS_FIELD_S1G_MCS 0x0000F000 +#define STA_STATS_FIELD_S1G_NSS 0x000F0000 #define STA_STATS_FIELD(_n, _v) FIELD_PREP(STA_STATS_FIELD_ ## _n, _v) #define STA_STATS_GET(_n, _v) FIELD_GET(STA_STATS_FIELD_ ## _n, _v) @@ -1081,6 +1084,7 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s) switch (s->encoding) { case RX_ENC_HT: case RX_ENC_VHT: + case RX_ENC_S1G: if (s->enc_flags & RX_ENC_FLAG_SHORT_GI) r |= STA_STATS_FIELD(SGI, 1); break; @@ -1127,6 +1131,11 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s) r |= STA_STATS_FIELD(UHR_ELR, s->uhr.elr); r |= STA_STATS_FIELD(UHR_IM, s->uhr.im); break; + case RX_ENC_S1G: + r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_S1G); + r |= STA_STATS_FIELD(S1G_NSS, s->nss); + r |= STA_STATS_FIELD(S1G_MCS, s->rate_idx); + break; default: WARN_ON(1); return STA_STATS_RATE_INVALID; From 50aa66ac0162aac818285a4a5d7c2c553b6bfb65 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:24:54 +0200 Subject: [PATCH 137/285] wifi: ieee80211: define some UHR link reconfiguration frame types Define some values needed for UHR link reconfiguration frames, in particular to prepare for UHR mode change request/handling. Link: https://patch.msgid.link/20260529102644.03029bae6447.If22b0c1e10d9db712dca408a420469b3d385b4ea@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211-uhr.h | 51 +++++++++++++++++++++++++++++++++++ include/linux/ieee80211.h | 17 ++++++++++++ 2 files changed, 68 insertions(+) diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h index f4f4bd8256df..71faf4a6825e 100644 --- a/include/linux/ieee80211-uhr.h +++ b/include/linux/ieee80211-uhr.h @@ -8,6 +8,7 @@ #define LINUX_IEEE80211_UHR_H #include +#include #include #define IEEE80211_UHR_OPER_PARAMS_DPS_ENA 0x0001 @@ -463,4 +464,54 @@ struct ieee80211_smd_info { __le16 timeout; } __packed; +enum ieee80211_protected_uhr_action { + IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST = 0, + IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_RESPONSE = 1, + IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY = 2, +}; + +enum ieee80211_uhr_link_reconfig_request_type { + IEEE80211_UHR_LINK_RECONFIG_REQUEST_ST_PREP = 0, + IEEE80211_UHR_LINK_RECONFIG_REQUEST_ST_EXEC = 1, + IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST = 3, +}; + +enum ieee80211_uhr_link_reconfig_response_type { + IEEE80211_UHR_LINK_RECONFIG_RESPONSE_ST_PREP = 0, + IEEE80211_UHR_LINK_RECONFIG_RESPONSE_ST_EXEC = 1, +}; + +enum ieee80211_uhr_link_reconfig_notify_type { + IEEE80211_UHR_LINK_RECONFIG_NOTIFY_DL_DRAINED = 2, + IEEE80211_UHR_LINK_RECONFIG_NOTIFY_OMP_RESPONSE = 3, +}; + +enum ieee80211_uhr_mode_change_control { + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID = 0x003f, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE = 0x0040, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_UPDATE = 0x0080, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_LENGTH = 0x0f00, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_SPECIFIC = 0xf000, +}; + +enum ieee80211_uhr_mode_change_mode_id { + IEEE80211_UHR_MODE_CHANGE_MODE_ID_DPS = 0, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_NPCA = 1, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_DUO = 2, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_DSO = 3, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_P_EDCA = 4, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_ELR_RX = 5, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_AOM = 6, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_LLI = 7, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_CO_BF = 8, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_CO_SR = 9, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_EMLSR = 10, + IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE = 11, +}; + +struct ieee80211_uhr_mode_change_tuple { + __le16 control; + u8 variable[]; +} __packed; + #endif /* LINUX_IEEE80211_UHR_H */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 11106589acc6..d40484451e9a 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1174,6 +1174,22 @@ struct ieee80211_mgmt { u8 control; u8 variable[]; } __packed eml_omn; + struct { + u8 dialog_token; + u8 type; + u8 variable[]; + } __packed uhr_link_reconf_req; + struct { + u8 dialog_token; + u8 type; + u8 count; + u8 variable[]; + } __packed uhr_link_reconf_resp; + struct { + u8 dialog_token; + u8 type; + u8 variable[]; + } __packed uhr_link_reconf_notif; }; } __packed action; DECLARE_FLEX_ARRAY(u8, body); /* Generic frame body */ @@ -1837,6 +1853,7 @@ enum ieee80211_category { WLAN_CATEGORY_VHT = 21, WLAN_CATEGORY_S1G = 22, WLAN_CATEGORY_PROTECTED_EHT = 37, + WLAN_CATEGORY_PROTECTED_UHR = 43, WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, WLAN_CATEGORY_VENDOR_SPECIFIC = 127, }; From 79fb99e16f60a77cbd2824695d98aa34ebbb9d69 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:24:55 +0200 Subject: [PATCH 138/285] wifi: mac80211: unify link STA removal in vif link removal There are multiple cases where interface links are removed and the station links need to be removed with them, e.g. in mlme.c we have both received and transmitted multi-link reconfiguration, doing the two things in different order, the former deleting STA links when the vif link change may still fail. It's also not clear that userspace (hostapd) couldn't, at least in theory, remove a link from an interface without removing the station links first, or even leave stations that aren't MLO-capable, using that link. Unify this code into ieee80211_vif_update_links() so that it always happens, always happens in the right order and is transactional (i.e. failures are handled correctly.) Link: https://patch.msgid.link/20260529102644.c352f73a4658.I7219a5d72dab2abcecea9b5c52e7eb7a50e68d9b@changeid Signed-off-by: Johannes Berg --- net/mac80211/link.c | 30 ++++++++++++++++++++++++++++++ net/mac80211/mlme.c | 19 ------------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index e81dd02de12e..d0535268962c 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -292,6 +292,7 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, u16 old_active = sdata->vif.active_links; unsigned long add = new_links & ~old_links; unsigned long rem = old_links & ~new_links; + unsigned long sta_rem = rem; unsigned int link_id; int ret; struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *link; @@ -299,6 +300,7 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS]; bool use_deflink = old_links == 0; /* set for error case */ bool non_sta = sdata->vif.type != NL80211_IFTYPE_STATION; + struct sta_info *sta; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -402,6 +404,34 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, goto free; } + /* try to remove links that are now invalid from (MLO) stations */ + list_for_each_entry(sta, &sdata->local->sta_list, list) { + unsigned long rem_links = sta->sta.valid_links & sta_rem; + + if (sta->sdata != sdata) + continue; + + /* + * skip stations that would have no links left, + * those will be removed completely later + */ + if (sta->sta.valid_links == rem_links) + continue; + + for_each_set_bit(link_id, &rem_links, + IEEE80211_MLD_MAX_NUM_LINKS) + ieee80211_sta_remove_link(sta, link_id); + } + + /* + * Remove stations using any removed links. Note that due + * to the above station link removal, this only removes + * stations that were skipped above because they'd have no + * links left after link removal. + */ + for_each_set_bit(link_id, &sta_rem, IEEE80211_MLD_MAX_NUM_LINKS) + sta_info_flush(sdata, link_id); + /* use deflink/bss_conf again if and only if there are no more links */ use_deflink = new_links == 0; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index cfb5bc4eac69..f55cb376cbd5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7169,7 +7169,6 @@ static void ieee80211_ml_reconf_work(struct wiphy *wiphy, container_of(work, struct ieee80211_sub_if_data, u.mgd.ml_reconf_work.work); u16 new_valid_links, new_active_links, new_dormant_links; - struct sta_info *sta; int ret; if (!sdata->u.mgd.removed_links) @@ -7205,16 +7204,6 @@ static void ieee80211_ml_reconf_work(struct wiphy *wiphy, } } - sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); - if (sta) { - unsigned long removed_links = sdata->u.mgd.removed_links; - unsigned int link_id; - - for_each_set_bit(link_id, &removed_links, - IEEE80211_MLD_MAX_NUM_LINKS) - ieee80211_sta_remove_link(sta, link_id); - } - new_dormant_links = sdata->vif.dormant_links & ~sdata->u.mgd.removed_links; ret = ieee80211_vif_set_links(sdata, new_valid_links, @@ -11071,14 +11060,6 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, goto err_free; } - for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; - link_id++) { - if (!(req->rem_links & BIT(link_id))) - continue; - - ieee80211_sta_remove_link(sta, link_id); - } - /* notify the driver and upper layers */ ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_MLD_VALID_LINKS); From 3b142c38b2c5e9885bb43fed394285c613b3975a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:24:56 +0200 Subject: [PATCH 139/285] wifi: mac80211: clean up return in ieee802_11_find_bssid_profile() There's no need to define 'profile_len' at the outer scope and initialize it, move it where needed and just return 0 if nothing can be found. Link: https://patch.msgid.link/20260529102644.46f25609ddef.I9e651a0018e66953f4fb508f784188e00351c07f@changeid Signed-off-by: Johannes Berg --- net/mac80211/parse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index c44e81a2f80d..1ed1fc25da12 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -828,10 +828,9 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, u8 *nontransmitted_profile) { const struct element *elem, *sub; - size_t profile_len = 0; if (!bss || !bss->transmitted_bss) - return profile_len; + return 0; for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { if (elem->datalen < 2) @@ -841,6 +840,7 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, for_each_element(sub, elem->data + 1, elem->datalen - 1) { u8 new_bssid[ETH_ALEN]; + size_t profile_len; const u8 *index; if (sub->id != 0 || sub->datalen < 4) { From e41dc5921e2413cefd627b270f63367ed67d3f28 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:24:57 +0200 Subject: [PATCH 140/285] wifi: mac80211: rename "multi_link_inner" variable This variable name seems a bit misleading now (I added it myself a year ago or so), it indicates that the parsing is happening on the inner elements of a multi-link element. Rename it to "inside_multilink" to clarify. Link: https://patch.msgid.link/20260529102644.7ccd55a411cf.I4101e1cfd133a2ce2374340712da8bb1f0292a40@changeid Signed-off-by: Johannes Berg --- net/mac80211/parse.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 1ed1fc25da12..2587284fa67b 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -59,7 +59,7 @@ struct ieee80211_elems_parse { struct ieee80211_elem_defrag ml_reconf, ml_epcs; - bool multi_link_inner; + bool inside_multilink; bool skip_vendor; /* @@ -167,7 +167,7 @@ ieee80211_parse_extension_element(u32 *crc, switch (le16_get_bits(mle->control, IEEE80211_ML_CONTROL_TYPE)) { case IEEE80211_ML_CONTROL_TYPE_BASIC: - if (elems_parse->multi_link_inner) { + if (elems_parse->inside_multilink) { elems->parse_error |= IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC; break; @@ -1061,7 +1061,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) const struct element *non_inherit = NULL; struct ieee802_11_elems *elems; size_t scratch_len = 3 * params->len; - bool multi_link_inner = false; + bool inside_multilink = false; BUILD_BUG_ON(sizeof(empty_non_inheritance) != empty_non_inheritance[1] + 2); BUILD_BUG_ON(offsetof(typeof(*elems_parse), elems) != 0); @@ -1129,7 +1129,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) /* must always parse to get elems_parse->ml_basic_elem */ non_inherit = ieee80211_prep_mle_link_parse(elems_parse, params, &sub); - multi_link_inner = true; + inside_multilink = true; } elems_parse->skip_vendor = @@ -1140,7 +1140,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) /* Override with nontransmitted/per-STA profile if found */ if (sub.len) { - elems_parse->multi_link_inner = multi_link_inner; + elems_parse->inside_multilink = inside_multilink; elems_parse->skip_vendor = false; _ieee802_11_parse_elems_full(&sub, elems_parse, NULL); } From 32e223198539bd8be5a84764df869f88bc56f25b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:24:58 +0200 Subject: [PATCH 141/285] wifi: mac80211: clarify beacon parsing with MBSSID/EMA When connected to a non-transmitting BSS of multiple BSSID set with EMA, the correct profile for the connection isn't always present in the beacon. Indicate this in the parser and use the information to not check everything in beacon processing, since the information might not be correct if taken only from the transmitted BSS. Link: https://patch.msgid.link/20260529102644.97527a7dfd7b.Iecd0ef578b85a5a0057538cfff5fdff41d19b7ea@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 9 +++++ net/mac80211/mlme.c | 83 ++++++++++++++++++++++++++++---------- net/mac80211/parse.c | 4 ++ 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 18a101710432..339faa7a0a0e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1898,6 +1898,15 @@ struct ieee802_11_elems { struct ieee80211_mle_per_sta_profile *prof; size_t sta_prof_len; + /* + * When parsing the beacon with MBSSID (from a transmitted BSS), this + * indicates that the profile the parser was instructed to look for + * (via the bss value in &struct ieee80211_elems_parse_params) couldn't + * be found (due to EMA, or perhaps broken AP) and the result cannot be + * considered complete. + */ + bool mbssid_nontx_profile_missing; + /* whether/which parse error occurred while retrieving these elements */ u8 parse_error; }; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f55cb376cbd5..2888a382677d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7666,8 +7666,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, struct link_sta_info *link_sta; struct sta_info *sta; u64 changed = 0; - bool erp_valid; - u8 erp_value = 0; u32 ncrc = 0; u8 *bssid, *variable = mgmt->u.beacon.variable; u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -7788,6 +7786,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, if (!elems) return; + /* + * Note: with MBSSID and an EMA (or broken) AP, we could fail to find + * the correct multi-BSSID profile for the non-transmitting AP we're + * connected to. The result's elems->mbssid_nontx_profile_missing is + * indicating that, but some things must happen regardless. + */ + if (rx_status->flag & RX_FLAG_DECRYPTED && ieee80211_mgd_ssid_mismatch(sdata, elems)) { sdata_info(sdata, "SSID mismatch for AP %pM, disconnect\n", @@ -7823,6 +7828,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, } } + /* + * P2P will almost certainly not have MBSSID, but this just + * assumes that it would at least always inherit NoA anyway + * since it's absent from the channel. + */ if (sdata->vif.p2p || sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) { struct ieee80211_p2p_noa_attr noa = {}; @@ -7880,23 +7890,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ieee80211_rx_bss_info(link, mgmt, len, rx_status); + /* + * This assumes that all members of a multiple BSS set must be + * switching together, so we can parse channel switch elements + * from the transmitted BSS even if our non-transmitted one is + * not present in this beacon (due to EMA.) + */ ieee80211_sta_process_chanswitch(link, rx_status->mactime, rx_status->device_timestamp, elems, elems, IEEE80211_CSA_SOURCE_BEACON); - /* note that after this elems->ml_basic can no longer be used fully */ - ieee80211_mgd_check_cross_link_csa(sdata, rx_status->link_id, elems); - - ieee80211_mgd_update_bss_param_ch_cnt(sdata, bss_conf, elems); - - if (!sdata->u.mgd.epcs.enabled && - !link->u.mgd.disable_wmm_tracking && - ieee80211_sta_wmm_params(local, link, elems->wmm_param, - elems->wmm_param_len, - elems->mu_edca_param_set)) - changed |= BSS_CHANGED_QOS; - /* * If we haven't had a beacon before, tell the driver about the * DTIM period (and beacon timing if desired) now. @@ -7913,17 +7917,53 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ieee80211_recalc_ps_vif(sdata); } - if (elems->erp_info) { - erp_valid = true; - erp_value = elems->erp_info[0]; - } else { - erp_valid = false; - } + /* RNR isn't inside an MBSSID profile */ + ieee80211_mgd_update_bss_param_ch_cnt(sdata, bss_conf, elems); + + /* assume ERP would be inherited anyway */ + if (!ieee80211_is_s1g_beacon(hdr->frame_control)) { + u8 erp_value = 0; + bool erp_valid; + + if (elems->erp_info) { + erp_valid = true; + erp_value = elems->erp_info[0]; + } else { + erp_valid = false; + } - if (!ieee80211_is_s1g_beacon(hdr->frame_control)) changed |= ieee80211_handle_bss_capability(link, le16_to_cpu(mgmt->u.beacon.capab_info), erp_valid, erp_value); + } + + /* + * There are some other things that we can only do when the + * real non-transmitted profile was actually parsed, so exit + * here before doing those. + */ + if (elems->mbssid_nontx_profile_missing) + goto apply; + + /* + * This requires multi-link element, which is from the MBSSID profile. + * Note that after this elems->ml_basic can no longer be used fully. + * + * Note also that currently the parsing is incorrect, so this will + * never actually do anything. + */ + ieee80211_mgd_check_cross_link_csa(sdata, rx_status->link_id, elems); + + /* + * EDCA parameters should be the same, but perhaps ACM can differ + * between BSSes in an MBSSID set. + */ + if (!sdata->u.mgd.epcs.enabled && + !link->u.mgd.disable_wmm_tracking && + ieee80211_sta_wmm_params(local, link, elems->wmm_param, + elems->wmm_param_len, + elems->mu_edca_param_set)) + changed |= BSS_CHANGED_QOS; sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); if (WARN_ON(!sta)) { @@ -7969,6 +8009,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ieee80211_process_adv_ttlm(sdata, elems, le64_to_cpu(mgmt->u.beacon.timestamp)); +apply: ieee80211_link_info_change_notify(sdata, link, changed); free: kfree(elems); diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 2587284fa67b..c7d882ccc4c6 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -1109,6 +1109,10 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) sub.type = params->type; sub.link_id = params->link_id; + /* indicate to consumer whether or not profile was found */ + if (params->bss->transmitted_bss && !nontx_len) + elems->mbssid_nontx_profile_missing = true; + /* consume the space used for non-transmitted profile */ elems_parse->scratch_pos += nontx_len; From c93db0bfff162b01fae17a674ff2bd3ae8ec37d3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:24:59 +0200 Subject: [PATCH 142/285] wifi: mac80211: use local ml_basic_elem in parsing There's no need to store this pointer on the heap, it's only used in a single function. Move it there. Also clarify the comment referencing it, ml_basic_elem is not actually relevant (any more.) Link: https://patch.msgid.link/20260529102644.50187b7a6ca2.Ifef23bda96651eed0f5cd2c3ecd4817d2fb08af4@changeid Signed-off-by: Johannes Berg --- net/mac80211/parse.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index c7d882ccc4c6..34f4c76ebc5d 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -54,9 +54,6 @@ struct ieee80211_elems_parse { /* must be first for kfree to work */ struct ieee802_11_elems elems; - /* The basic Multi-Link element in the original elements */ - const struct element *ml_basic_elem; - struct ieee80211_elem_defrag ml_reconf, ml_epcs; bool inside_multilink; @@ -947,6 +944,7 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, { struct ieee802_11_elems *elems = &elems_parse->elems; struct ieee80211_mle_per_sta_profile *prof; + const struct element *ml_basic_elem = NULL; const struct element *tmp, *ret; ssize_t ml_len; const u8 *end; @@ -966,12 +964,11 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, IEEE80211_ML_CONTROL_TYPE_BASIC) continue; - elems_parse->ml_basic_elem = tmp; + ml_basic_elem = tmp; break; } - ml_len = cfg80211_defragment_element(elems_parse->ml_basic_elem, - elems->ie_start, + ml_len = cfg80211_defragment_element(ml_basic_elem, elems->ie_start, elems->total_len, elems_parse->scratch_pos, elems_parse->scratch + @@ -1130,7 +1127,10 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) if (params->bss->transmitted_bss && !non_inherit) non_inherit = (const void *)empty_non_inheritance; } else { - /* must always parse to get elems_parse->ml_basic_elem */ + /* + * Find the multi-link element and the non-inherit element inside + * the applicable profile, if requested by params->link_id >= 0. + */ non_inherit = ieee80211_prep_mle_link_parse(elems_parse, params, &sub); inside_multilink = true; From e48223525a54d3a3182d2d9a497dca022b942b4d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:00 +0200 Subject: [PATCH 143/285] wifi: cfg80211: harden cfg80211_defragment_element() A previous commit changed mac80211 to no longer make wrong calls to cfg80211_defragment_element() with the element pointing outside of the buffer. Additionally, harden this function itself against that and always return -EINVAL in case the element isn't inside the source buffer. Reviewed-by: Miriam Rachel Korenblit Reviewed-by: Ilan Peer Link: https://patch.msgid.link/20260529102644.198945754054.I5ae8fdebf9008abc6e15d0b0f10c3a7b73d02eab@changeid Signed-off-by: Johannes Berg --- net/wireless/scan.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 358cbc9e43d8..17f0032844ab 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -5,7 +5,7 @@ * Copyright 2008 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include #include @@ -2603,7 +2603,9 @@ ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies, ssize_t copied; u8 elem_datalen; - if (!elem) + if (!elem || (const u8 *)elem < ies || + (const u8 *)elem + sizeof(*elem) > ies + ieslen || + (const u8 *)elem + sizeof(*elem) + elem->datalen > ies + ieslen) return -EINVAL; /* elem might be invalid after the memmove */ From d823fc25eaaf357be292bd9c827026985bd502b3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:01 +0200 Subject: [PATCH 144/285] wifi: mac80211: always expose multi-link element During beacon processing, the parser is always called with a BSS to find the correct multi-BSSID profile (if any) and therefore never attempts to parse a multi-link element. This means the code to handle cross-link CSA can effectively never do anything. Fix this by parsing the multi-link element in the regular parser as well. Fixes: 7ef8f6821d16 ("wifi: mac80211: mlme: handle cross-link CSA") Reviewed-by: Miriam Rachel Korenblit Reviewed-by: Benjamin Berg Reviewed-by: Ilan Peer Link: https://patch.msgid.link/20260529102644.2a74b2659f50.I8f9454bf5e05c419a9b1eb23ecad302a6bf63fbb@changeid Signed-off-by: Johannes Berg --- net/mac80211/parse.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index 34f4c76ebc5d..c2f2f78f2b4f 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -54,7 +54,7 @@ struct ieee80211_elems_parse { /* must be first for kfree to work */ struct ieee802_11_elems elems; - struct ieee80211_elem_defrag ml_reconf, ml_epcs; + struct ieee80211_elem_defrag ml_reconf, ml_epcs, ml_basic; bool inside_multilink; bool skip_vendor; @@ -169,6 +169,9 @@ ieee80211_parse_extension_element(u32 *crc, IEEE80211_PARSE_ERR_DUP_NEST_ML_BASIC; break; } + elems_parse->ml_basic.elem = elem; + elems_parse->ml_basic.start = params->start; + elems_parse->ml_basic.len = params->len; break; case IEEE80211_ML_CONTROL_TYPE_RECONF: elems_parse->ml_reconf.elem = elem; @@ -1155,6 +1158,10 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) elems->ml_epcs = ieee80211_mle_defrag(elems_parse, &elems_parse->ml_epcs, &elems->ml_epcs_len); + if (!elems->ml_basic) + elems->ml_basic = ieee80211_mle_defrag(elems_parse, + &elems_parse->ml_basic, + &elems->ml_basic_len); if (elems->tim && !elems->parse_error) { const struct ieee80211_tim_ie *tim_ie = elems->tim; From 29eb4dc882d3b681c699ed550b79129c01e8c80a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:02 +0200 Subject: [PATCH 145/285] wifi: mac80211: mlme: allow UHR only with MLO UHR requires MLO, not just formally but also in order for the client to understand AP BSS parameter changes, since the Critical Update Counter is inside the Multi- Link Element. Require MLO for UHR connections to avoid otherwise needed complexity such as not enabling any feature that would require tracking critical updates. Link: https://patch.msgid.link/20260529102644.43817ce87042.If4562ae9c5ca83339b397d9a344b68631cb17c4a@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2888a382677d..2b7bb371efee 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -389,7 +389,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, } check_uhr: - if (conn->mode < IEEE80211_CONN_MODE_UHR || !uhr_oper) + if (conn->mode < IEEE80211_CONN_MODE_UHR || !uhr_oper || !elems->ml_basic) return IEEE80211_CONN_MODE_EHT; if (elems->frame_type != (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON)) { From a5cbf19456f6f17503e1a7d8e23bcb4fb107133e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:03 +0200 Subject: [PATCH 146/285] wifi: mac80211: explain ieee80211_determine_chan_mode() parsing Looking through element parsing behaviour for multi-BSSID and multi-link, this one seemed odd. Add a comment that explains why it's written this way. Link: https://patch.msgid.link/20260529102644.25f75c4df338.I1f1f17cc0ae8e413659654d4bbaa34260ef68e2c@changeid Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2b7bb371efee..3553e39a7db4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1077,6 +1077,14 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, const struct cfg80211_bss_ies *ies = rcu_dereference(cbss->ies); struct ieee80211_bss *bss = (void *)cbss->priv; struct ieee80211_channel *channel = cbss->channel; + /* + * This is for parsing a beacon or probe response here, but it's + * using the *BSS* elements which are synthetic for multi-BSSID, + * created by cfg80211 based on multi-BSSID inheritance etc. As + * a result, this sets neither .bss (since multi-BSSID is parsed + * already) nor a valid .link_id (since it doesn't want to see + * the data from another link.) + */ struct ieee80211_elems_parse_params parse_params = { .link_id = -1, .from_ap = true, From 7ad8731738d7511c92f9ffc8c69fa8681986dd5d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:04 +0200 Subject: [PATCH 147/285] wifi: Update UHR PHY capabilities to D1.4 There are new capabilities in D1.4, and some reserved bits. Update the code accordingly. Link: https://patch.msgid.link/20260529102644.f146932b21e2.I12bad84157bf809fbe285b79420143b3c456d9d2@changeid Signed-off-by: Johannes Berg --- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 10 +++-- drivers/net/wireless/intel/iwlwifi/mld/tlc.c | 4 +- .../wireless/virtual/mac80211_hwsim_main.c | 24 +++++------ include/linux/ieee80211-uhr.h | 40 ++++++++++++++----- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 7027bca249a0..d47b4ae2f486 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -693,8 +693,9 @@ static const struct ieee80211_sband_iftype_data iwl_iftype_cap[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + /* Note: asymmetry is fixed later */ + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP | IEEE80211_UHR_MAC_CAP0_DPS_SUPP, @@ -801,8 +802,9 @@ static const struct ieee80211_sband_iftype_data iwl_iftype_cap[] = { }, .uhr_cap = { .has_uhr = true, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + /* Note: asymmetry is fixed later */ + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c index a03834d3ac65..edf9c735dd67 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -114,8 +114,8 @@ iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, } if (uhr_cap && uhr_cap->has_uhr && own_uhr_cap && - uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_RX && - own_uhr_cap->phy.cap & IEEE80211_UHR_PHY_CAP_ELR_TX) + uhr_cap->phy.cap & cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX) && + own_uhr_cap->phy.cap & cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_TX)) flags |= IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_1_5_MBPS_MSK | IWL_TLC_MNG_CFG_FLAGS_UHR_ELR_3_MBPS_MSK; diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 4926122d6648..de0bb4ce7456 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -4593,8 +4593,8 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, }, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, { @@ -4709,8 +4709,8 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, }, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, #ifdef CONFIG_MAC80211_MESH @@ -4886,8 +4886,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, }, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, { @@ -5019,8 +5018,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, }, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX), }, }, #ifdef CONFIG_MAC80211_MESH @@ -5220,8 +5218,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, }, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, { @@ -5374,8 +5371,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, }, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX), }, }, #ifdef CONFIG_MAC80211_MESH @@ -5473,8 +5469,8 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, }, - .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | - IEEE80211_UHR_PHY_CAP_ELR_TX, + .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | + IEEE80211_UHR_PHY_CAP_ELR_TX), }, }, #endif diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h index 71faf4a6825e..be51d91f5e03 100644 --- a/include/linux/ieee80211-uhr.h +++ b/include/linux/ieee80211-uhr.h @@ -401,17 +401,39 @@ struct ieee80211_uhr_cap_mac { u8 mac_cap[5]; } __packed; -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80 0x01 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80 0x02 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160 0x04 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160 0x08 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320 0x10 -#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_320 0x20 -#define IEEE80211_UHR_PHY_CAP_ELR_RX 0x40 -#define IEEE80211_UHR_PHY_CAP_ELR_TX 0x80 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80 0x00000001 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80 0x00000002 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160 0x00000004 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160 0x00000008 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320 0x00000010 +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_320 0x00000020 +#define IEEE80211_UHR_PHY_CAP_ELR_TX 0x00000040 +#define IEEE80211_UHR_PHY_CAP_ELR_RX 0x00000080 +#define IEEE80211_UHR_PHY_CAP_PART_BW_DL_MUMIMO 0x00000100 +#define IEEE80211_UHR_PHY_CAP_PART_BW_UL_MUMIMO 0x00000200 +#define IEEE80211_UHR_PHY_CAP_MCS15 0x00000400 +#define IEEE80211_UHR_PHY_CAP_2XLDPC_TX 0x00000800 +#define IEEE80211_UHR_PHY_CAP_2XLDPC_RX 0x00001000 +#define IEEE80211_UHR_PHY_CAP_UEQM_TX_MAX_NSS 0x00006000 +#define IEEE80211_UHR_PHY_CAP_UEQM_RX_MAX_NSS 0x00018000 +#define IEEE80211_UHR_PHY_CAP_CO_BF_JOINT_SOUNDING 0x00040000 +#define IEEE80211_UHR_PHY_CAP_IM_TX 0x00080000 +#define IEEE80211_UHR_PHY_CAP_IM_RX 0x00100000 +#define IEEE80211_UHR_PHY_CAP_CO_SR_MODE_1 0x00200000 +#define IEEE80211_UHR_PHY_CAP_CO_SR_MODE_2 0x00400000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_20_IN_PBW_20 0x00800000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_40_IN_PBW_40 0x01000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_80 0x02000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_160 0x04000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_320 0x08000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_20_IN_PBW_GE80 0x10000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_40_IN_PBW_GE80 0x20000000 +#define IEEE80211_UHR_PHY_CAP_DRU_DBW_60_IN_PBW_GE80 0x40000000 +#define IEEE80211_UHR_PHY_CAP_DRU_RRU_HYBRID_MODE 0x80000000 struct ieee80211_uhr_cap_phy { - u8 cap; + __le32 cap; + u8 reserved; } __packed; struct ieee80211_uhr_cap { From f576db12f1f0adafd8fd1c395e377b7b37a77b7b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:05 +0200 Subject: [PATCH 148/285] wifi: Update UHR MAC capabilities to D1.4 There are now 8 more reserved bits in D1.4, update the code accordingly. Link: https://patch.msgid.link/20260529102644.6e27c54cfceb.Id395c07ffde286011494fc75190dc6060117436e@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211-uhr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h index be51d91f5e03..c8dde0fbd896 100644 --- a/include/linux/ieee80211-uhr.h +++ b/include/linux/ieee80211-uhr.h @@ -398,7 +398,7 @@ enum ieee80211_uhr_dbe_max_supported_bw { }; struct ieee80211_uhr_cap_mac { - u8 mac_cap[5]; + u8 mac_cap[6]; } __packed; #define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80 0x00000001 From ece981f88e930b2071ab045eb248b5e2292c9327 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:06 +0200 Subject: [PATCH 149/285] wifi: mac80211: refactor link STA bandwidth update There's similar code in two places in HT and HE, and we need to add the same again for UHR. Rename ieee80211_link_sta_rc_update_omi() to ieee80211_link_sta_update_rc_bw() and move it to sta_info.c and update existing code that can use it to do so. Link: https://patch.msgid.link/20260529102644.577c2f304d33.I09df4fce83c4e3e6deddfecbea74ffdbeedb4927@changeid Signed-off-by: Johannes Berg --- net/mac80211/he.c | 25 ++----------------------- net/mac80211/ht.c | 24 ++++++++---------------- net/mac80211/sta_info.c | 23 +++++++++++++++++++++++ net/mac80211/sta_info.h | 3 +++ 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/net/mac80211/he.c b/net/mac80211/he.c index b7d9e4cb6ba6..5acf482c177a 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -262,27 +262,6 @@ ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif, } } -static void ieee80211_link_sta_rc_update_omi(struct ieee80211_link_data *link, - struct link_sta_info *link_sta) -{ - struct ieee80211_sub_if_data *sdata = link->sdata; - struct ieee80211_supported_band *sband; - enum ieee80211_sta_rx_bandwidth new_bw; - enum nl80211_band band; - - band = link->conf->chanreq.oper.chan->band; - sband = sdata->local->hw.wiphy->bands[band]; - - new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper, - IEEE80211_STA_BW_TX_TO_STA); - if (link_sta->pub->bandwidth == new_bw) - return; - - link_sta->pub->bandwidth = new_bw; - rate_control_rate_update(sdata->local, sband, link_sta, - IEEE80211_RC_BW_CHANGED); -} - bool ieee80211_prepare_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta, enum ieee80211_sta_rx_bandwidth bw) { @@ -323,7 +302,7 @@ bool ieee80211_prepare_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta, if (bw < link_sta->rx_omi_bw_staging) { link_sta->rx_omi_bw_tx = bw; - ieee80211_link_sta_rc_update_omi(link, link_sta); + ieee80211_link_sta_update_rc_bw(link, link_sta); } else { link_sta->rx_omi_bw_rx = bw; ieee80211_recalc_chanctx_min_def(local, chanctx); @@ -365,7 +344,7 @@ void ieee80211_finalize_rx_omi_bw(struct ieee80211_link_sta *pub_link_sta) /* rate control in finalize only when widening bandwidth */ WARN_ON(link_sta->rx_omi_bw_tx > link_sta->rx_omi_bw_staging); link_sta->rx_omi_bw_tx = link_sta->rx_omi_bw_staging; - ieee80211_link_sta_rc_update_omi(link, link_sta); + ieee80211_link_sta_update_rc_bw(link, link_sta); } if (link_sta->rx_omi_bw_rx != link_sta->rx_omi_bw_staging) { diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 6285ac15c16c..e1e1b7f82f3f 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -584,9 +584,10 @@ void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local, struct link_sta_info *link_sta, u8 chanwidth, enum nl80211_band band) { - enum ieee80211_sta_rx_bandwidth max_bw, new_bw; - struct ieee80211_supported_band *sband; - struct sta_opmode_info sta_opmode = {}; + enum ieee80211_sta_rx_bandwidth max_bw; + struct sta_opmode_info sta_opmode = { + .changed = STA_OPMODE_MAX_BW_CHANGED, + }; struct ieee80211_link_data *link; lockdep_assert_wiphy(local->hw.wiphy); @@ -602,21 +603,12 @@ void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local, /* set op_mode_bw and recalc sta bw */ link_sta->op_mode_bw = max_bw; - new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper, - IEEE80211_STA_BW_TX_TO_STA); - if (link_sta->pub->bandwidth == new_bw) + if (!ieee80211_link_sta_update_rc_bw(link, link_sta)) return; - link_sta->pub->bandwidth = new_bw; - sband = local->hw.wiphy->bands[band]; - sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(new_bw); - sta_opmode.changed = STA_OPMODE_MAX_BW_CHANGED; + sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(link_sta->pub->bandwidth); - rate_control_rate_update(local, sband, link_sta, - IEEE80211_RC_BW_CHANGED); - cfg80211_sta_opmode_change_notify(sdata->dev, - sta->addr, - &sta_opmode, - GFP_KERNEL); + cfg80211_sta_opmode_change_notify(sdata->dev, sta->addr, + &sta_opmode, GFP_KERNEL); } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 857c7d3355c7..205e8d5c0c72 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -3775,3 +3775,26 @@ ieee80211_sta_current_bw(struct link_sta_info *link_sta, /* unreachable */ return IEEE80211_STA_RX_BW_20; } + +bool ieee80211_link_sta_update_rc_bw(struct ieee80211_link_data *link, + struct link_sta_info *link_sta) +{ + struct ieee80211_sub_if_data *sdata = link->sdata; + struct ieee80211_supported_band *sband; + enum ieee80211_sta_rx_bandwidth new_bw; + enum nl80211_band band; + + band = link->conf->chanreq.oper.chan->band; + sband = sdata->local->hw.wiphy->bands[band]; + + new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper, + IEEE80211_STA_BW_TX_TO_STA); + if (link_sta->pub->bandwidth == new_bw) + return false; + + link_sta->pub->bandwidth = new_bw; + rate_control_rate_update(sdata->local, sband, link_sta, + IEEE80211_RC_BW_CHANGED); + + return true; +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index e1837e986837..3abac031d01c 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -1015,6 +1015,9 @@ ieee80211_sta_current_bw(struct link_sta_info *link_sta, struct cfg80211_chan_def *chandef, enum ieee80211_sta_bw_direction direction); +bool ieee80211_link_sta_update_rc_bw(struct ieee80211_link_data *link, + struct link_sta_info *link_sta); + enum sta_stats_type { STA_STATS_RATE_TYPE_INVALID = 0, STA_STATS_RATE_TYPE_LEGACY, From d3ea22270d7c02fe08606f609545cbe583e53060 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:07 +0200 Subject: [PATCH 150/285] wifi: mac80211: parse and apply UHR DBE channel When a UHR AP has DBE enabled, parse the channel and apply it to the chandef. Apply for TX only after the OMP response (or timeout) so that the AP doesn't receive frames with DBE width before the station completed transition to DBE. Link: https://patch.msgid.link/20260529102644.cb810f212128.Ife37c2673251346e84e4250b242b31f0895520ab@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211-eht.h | 18 +- include/linux/ieee80211-uhr.h | 92 +++++++ net/mac80211/ieee80211_i.h | 15 +- net/mac80211/mlme.c | 449 ++++++++++++++++++++++++++++++++- net/mac80211/rx.c | 14 + net/mac80211/sta_info.c | 16 ++ net/mac80211/sta_info.h | 4 + net/mac80211/status.c | 25 ++ net/mac80211/tests/chan-mode.c | 4 +- 9 files changed, 616 insertions(+), 21 deletions(-) diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index 87d92fb86fab..73e97fe30724 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -1038,13 +1038,17 @@ ieee80211_mle_basic_sta_prof_bss_param_ch_cnt(const struct ieee80211_mle_per_sta #define IEEE80211_MLE_STA_RECONF_CONTROL_COMPLETE_PROFILE 0x0010 #define IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT 0x0020 #define IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT 0x0040 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE 0x0780 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_AP_REM 0 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_OP_PARAM_UPDATE 1 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_ADD_LINK 2 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_DEL_LINK 3 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_NSTR_STATUS 4 -#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_PARAMS_PRESENT 0x0800 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE 0x0780 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_AP_REM 0 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_OP_PARAM_UPDATE 1 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_ADD_LINK 2 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_DEL_LINK 3 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_NSTR_STATUS 4 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_UHR_OMP_UPD 5 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_PARAMS_PRESENT 0x0800 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_NSTR_BMAP_SIZE 0x1000 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_NSTR_IND_BMAP_PRES 0x2000 +#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_DSO_INFO_PRESENT 0x4000 /** * ieee80211_mle_reconf_sta_prof_size_ok - validate reconfiguration multi-link diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h index c8dde0fbd896..1dd53c5f2954 100644 --- a/include/linux/ieee80211-uhr.h +++ b/include/linux/ieee80211-uhr.h @@ -10,11 +10,13 @@ #include #include #include +#include "ieee80211-eht.h" #define IEEE80211_UHR_OPER_PARAMS_DPS_ENA 0x0001 #define IEEE80211_UHR_OPER_PARAMS_NPCA_ENA 0x0002 #define IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA 0x0004 #define IEEE80211_UHR_OPER_PARAMS_DBE_ENA 0x0008 +#define IEEE80211_UHR_OPER_PARAMS_DBE_BW 0x0070 struct ieee80211_uhr_operation { __le16 params; @@ -177,6 +179,29 @@ enum ieee80211_uhr_dbe_oper_bw { IEEE80211_UHR_DBE_OPER_BW_320_2 = 5, }; +/** + * ieee80211_uhr_dbe_bw_mhz - get bandwidth in MHz from UHR DBE bandwidth + * @bw: UHR DBE bandwidth + * + * Return: the bandwidth in MHz, or -1 for invalid values + */ +static inline int ieee80211_uhr_dbe_bw_mhz(enum ieee80211_uhr_dbe_oper_bw bw) +{ + switch (bw) { + case IEEE80211_UHR_DBE_OPER_BW_40: + return 40; + case IEEE80211_UHR_DBE_OPER_BW_80: + return 80; + case IEEE80211_UHR_DBE_OPER_BW_160: + return 160; + case IEEE80211_UHR_DBE_OPER_BW_320_1: + case IEEE80211_UHR_DBE_OPER_BW_320_2: + return 320; + default: + return -1; + } +} + /** * struct ieee80211_uhr_dbe_info - DBE operation information * @@ -335,6 +360,35 @@ ieee80211_uhr_npca_dis_subch_bitmap(const struct ieee80211_uhr_operation *oper) return npca->dis_subch_bmap; } +/* + * Note: cannot call this on the element coming from a beacon, + * must ensure ieee80211_uhr_oper_size_ok(..., false) first + */ +static inline const struct ieee80211_uhr_dbe_info * +ieee80211_uhr_oper_dbe_info(const struct ieee80211_uhr_operation *oper) +{ + const u8 *pos = oper->variable; + + if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DBE_ENA))) + return NULL; + + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DPS_ENA)) + pos += sizeof(struct ieee80211_uhr_dps_info); + + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)) { + const struct ieee80211_uhr_npca_info *npca = (const void *)pos; + + pos += sizeof(*npca); + if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES)) + pos += sizeof(npca->dis_subch_bmap[0]); + } + + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA)) + pos += sizeof(struct ieee80211_uhr_p_edca_info); + + return (const void *)pos; +} + #define IEEE80211_UHR_MAC_CAP0_DPS_SUPP 0x01 #define IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP 0x02 #define IEEE80211_UHR_MAC_CAP0_DPS_AP_STATIC_HCM_SUPP 0x04 @@ -475,6 +529,44 @@ static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len, return len >= needed; } +#define IEEE80211_UHR_OM_PU_TO_128TU 11 + +/** + * ieee80211_uhr_capa_get_om_pu_to_us - get OM parameter update timeout in usec + * @cap: the UHR capability element, size must be validated + * + * Return: the OM parameter update timeout in usec, or -1 if it's not valid + */ +static inline int +ieee80211_uhr_capa_get_om_pu_to_us(const struct ieee80211_uhr_cap *cap) +{ + u8 timeout; + + timeout = u8_get_bits(cap->mac.mac_cap[3], + IEEE80211_UHR_MAC_CAP3_UHR_OM_PU_TO_HIGH); + timeout <<= 2; + timeout |= u8_get_bits(cap->mac.mac_cap[2], + IEEE80211_UHR_MAC_CAP2_UHR_OM_PU_TO_LOW); + + if (timeout > IEEE80211_UHR_OM_PU_TO_128TU) + return -1; + + if (!timeout) + return 0; + + return 128 << (timeout - 1); +} + +/* only valid from AP, must check ieee80211_uhr_capa_size_ok(..., true) */ +static inline const struct ieee80211_uhr_cap_dbe * +ieee80211_uhr_dbe_cap(const struct ieee80211_uhr_cap *cap) +{ + if (!(cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP)) + return NULL; + + return (const void *)cap->variable; +} + #define IEEE80211_SMD_INFO_CAPA_DL_DATA_FWD 0x01 #define IEEE80211_SMD_INFO_CAPA_MAX_NUM_PREP 0x0E #define IEEE80211_SMD_INFO_CAPA_TYPE 0x10 diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 339faa7a0a0e..29bdfd2a39bd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -99,6 +99,7 @@ enum ieee80211_status_data { IEEE80211_STATUS_TYPE_INVALID = 0, IEEE80211_STATUS_TYPE_SMPS = 1, IEEE80211_STATUS_TYPE_NEG_TTLM = 2, + IEEE80211_STATUS_TYPE_UHR_OMP = 3, IEEE80211_STATUS_SUBDATA_MASK = 0x1ff0, }; @@ -412,6 +413,7 @@ enum ieee80211_conn_bw_limit { struct ieee80211_conn_settings { enum ieee80211_conn_mode mode; enum ieee80211_conn_bw_limit bw_limit; + bool dbe_enabled; }; extern const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited; @@ -639,6 +641,15 @@ struct ieee80211_if_managed { bool enabled; u8 dialog_token; } epcs; + + struct { + struct wiphy_hrtimer_work status_work; + u32 timeout_us; + u16 links; + u16 pending, pending_init; + u8 dialog_token; + bool acked; + } uhr_omp; }; struct ieee80211_if_ibss { @@ -2790,6 +2801,7 @@ ieee80211_chanreq_downgrade(struct ieee80211_chan_req *chanreq, return; if (conn->mode < IEEE80211_CONN_MODE_EHT) chanreq->ap.chan = NULL; + conn->dbe_enabled = false; } bool ieee80211_chanreq_identical(const struct ieee80211_chan_req *a, @@ -2980,7 +2992,8 @@ void ieee80211_rearrange_tpe_psd(struct ieee80211_parsed_tpe_psd *psd, struct ieee802_11_elems * ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, struct ieee80211_conn_settings *conn, - struct cfg80211_bss *cbss, int link_id, + struct cfg80211_bss *cbss, + struct link_sta_info *link_sta, int link_id, struct ieee80211_chan_req *chanreq, struct cfg80211_chan_def *ap_chandef, unsigned long *userspace_selectors); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 3553e39a7db4..9e92337bb6f9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -171,14 +171,24 @@ struct ieee80211_determine_ap_chan_data { const struct ieee80211_conn_settings *conn; u32 vht_cap_info; bool ignore_ht_channel_mismatch; + const struct cfg80211_chan_def *cur_chandef; + bool cur_dbe_used; /* target chandef is filled in */ struct cfg80211_chan_def *chandef; }; +struct ieee80211_determine_ap_chan_output { + /* filled to indicate UHR DBE was used */ + bool dbe_used; + /* and need to know non-DBE width */ + enum nl80211_chan_width non_dbe_width; +}; + static enum ieee80211_conn_mode ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, - struct ieee80211_determine_ap_chan_data *data) + const struct ieee80211_determine_ap_chan_data *data, + struct ieee80211_determine_ap_chan_output *out) { bool ignore_ht_channel_mismatch = data->ignore_ht_channel_mismatch; const struct ieee802_11_elems *elems = data->elems; @@ -196,6 +206,8 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, bool no_vht = false; u32 ht_cfreq; + memset(out, 0, sizeof(*out)); + if (ieee80211_hw_check(&sdata->local->hw, STRICT)) ignore_ht_channel_mismatch = false; @@ -396,6 +408,8 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def npca_chandef = *chandef; const struct ieee80211_sta_uhr_cap *uhr_cap; const struct ieee80211_uhr_npca_info *npca; + const struct ieee80211_uhr_dbe_info *dbe; + struct cfg80211_chan_def dbe_chandef; /* frames other than beacons carry UHR capability too */ if (!elems->uhr_cap) @@ -427,6 +441,96 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, if (uhr_cap->mac.mac_cap[0] & IEEE80211_UHR_MAC_CAP0_NPCA_SUPP) *chandef = npca_chandef; + + dbe = ieee80211_uhr_oper_dbe_info(uhr_oper); + if (dbe) { + const struct ieee80211_uhr_cap_dbe *dbe_cap; + u8 dbe_bw_oper; + u8 dbe_bw_cap; + + dbe_cap = ieee80211_uhr_dbe_cap(elems->uhr_cap); + + if (!dbe_cap) { + sdata_info(sdata, + "AP without UHR DBE capability uses it, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + dbe_bw_oper = u8_get_bits(dbe->params, + IEEE80211_UHR_DBE_OPER_BANDWIDTH); + + if (le16_get_bits(uhr_oper->params, + IEEE80211_UHR_OPER_PARAMS_DBE_BW) != dbe_bw_oper) { + sdata_info(sdata, + "AP UHR DBE settings mismatch, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + if (ieee80211_uhr_dbe_bw_mhz(dbe_bw_oper) < 0) { + sdata_info(sdata, + "AP UHR DBE bandwidth invalid, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + dbe_bw_cap = u8_get_bits(dbe_cap->cap, + IEEE80211_UHR_MAC_CAP_DBE_MAX_BW); + + switch (dbe_bw_cap) { + case IEEE80211_UHR_DBE_MAX_BW_40: + case IEEE80211_UHR_DBE_MAX_BW_80: + case IEEE80211_UHR_DBE_MAX_BW_160: + case IEEE80211_UHR_DBE_MAX_BW_320: + break; + default: + sdata_info(sdata, + "AP UHR DBE capability invalid, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + /* 1-4 are same in DBE capabilities, map 320-2 to 320 */ + if (dbe_bw_oper == IEEE80211_UHR_DBE_OPER_BW_320_2) + dbe_bw_oper = IEEE80211_UHR_DBE_MAX_BW_320; + if (dbe_bw_oper > dbe_bw_cap) { + sdata_info(sdata, + "AP UHR DBE wider than capability, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + } + + dbe_chandef = *chandef; + + if (cfg80211_chandef_add_dbe(&dbe_chandef, dbe)) { + sdata_info(sdata, + "AP UHR DBE settings invalid, disabling UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + if (dbe && + /* maybe driver would like to never use DBE */ + uhr_cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP && + ieee80211_chandef_usable(sdata, &dbe_chandef, + IEEE80211_CHAN_DISABLED)) { + out->non_dbe_width = chandef->width; + *chandef = dbe_chandef; + out->dbe_used = true; + } + } else if (data->cur_chandef && data->cur_dbe_used && + cfg80211_chandef_compatible(chandef, data->cur_chandef)) { + u8 dbe_bw = le16_get_bits(uhr_oper->params, + IEEE80211_UHR_OPER_PARAMS_DBE_BW); + int dbe_bw_mhz; + + dbe_bw_mhz = ieee80211_uhr_dbe_bw_mhz(dbe_bw); + if (dbe_bw_mhz < 0) { + sdata_info(sdata, + "AP UHR DBE bandwidth invalid, drop UHR\n"); + return IEEE80211_CONN_MODE_EHT; + } + + if (cfg80211_chandef_get_width(data->cur_chandef) == dbe_bw_mhz) { + *chandef = *data->cur_chandef; + out->dbe_used = true; + } } return IEEE80211_CONN_MODE_UHR; @@ -1069,7 +1173,8 @@ static void ieee80211_set_chanreq_ap(struct ieee80211_sub_if_data *sdata, VISIBLE_IF_MAC80211_KUNIT struct ieee802_11_elems * ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, struct ieee80211_conn_settings *conn, - struct cfg80211_bss *cbss, int link_id, + struct cfg80211_bss *cbss, + struct link_sta_info *link_sta, int link_id, struct ieee80211_chan_req *chanreq, struct cfg80211_chan_def *ap_chandef, unsigned long *userspace_selectors) @@ -1099,6 +1204,7 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, enum ieee80211_conn_mode ap_mode; unsigned long unknown_rates_selectors[BITS_TO_LONGS(128)] = {}; unsigned long sta_selectors[BITS_TO_LONGS(128)] = {}; + struct ieee80211_determine_ap_chan_output ap_chan_out; struct ieee80211_determine_ap_chan_data ap_chan_data = { .channel = channel, .vht_cap_info = bss->vht_cap_info, @@ -1115,7 +1221,9 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, return ERR_PTR(-ENOMEM); ap_chan_data.elems = elems; - ap_mode = ieee80211_determine_ap_chan(sdata, &ap_chan_data); + ap_mode = ieee80211_determine_ap_chan(sdata, &ap_chan_data, + &ap_chan_out); + conn->dbe_enabled = ap_chan_out.dbe_used; /* this should be impossible since parsing depends on our mode */ if (WARN_ON(ap_mode > conn->mode)) { @@ -1331,6 +1439,10 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, goto free; } + if (conn->dbe_enabled && link_sta) + link_sta->uhr_usable_tx_width = + ieee80211_chan_width_to_rx_bw(ap_chan_out.non_dbe_width); + return elems; free: kfree(elems); @@ -1338,12 +1450,152 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, } EXPORT_SYMBOL_IF_MAC80211_KUNIT(ieee80211_determine_chan_mode); +static void ieee80211_send_uhr_omp_req_dbe(struct ieee80211_sub_if_data *sdata, + u16 link_mask, bool initial) +{ + struct ieee80211_mle_basic_common_info *common; + struct ieee80211_mle_per_sta_profile *per_sta; + struct ieee80211_uhr_mode_change_tuple *tuple; + struct ieee80211_local *local = sdata->local; + struct ieee80211_multi_link_elem *mle; + struct ieee80211_link_data *link; + struct ieee80211_tx_info *info; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + u8 *ml_elem_len; + size_t size; + + if (initial) { + bool enabled = false; + + for_each_link_data(sdata, link) { + if (!(link_mask & BIT(link->link_id))) + continue; + if (link->u.mgd.conn.dbe_enabled) { + enabled = true; + break; + } + } + + if (!enabled) + return; + } + + if (sdata->u.mgd.uhr_omp.links) { + if (initial) + sdata->u.mgd.uhr_omp.pending_init |= link_mask; + else + sdata->u.mgd.uhr_omp.pending |= link_mask; + return; + } + + size = local->hw.extra_tx_headroom + + IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req) + + 3 + sizeof(*mle) + sizeof(*common) + + IEEE80211_MLD_MAX_NUM_LINKS * + (2 + sizeof(*per_sta) + + 3 + sizeof(*tuple) /* single tuple for each link */); + + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + mgmt = skb_put_zero(skb, IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req)); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); + + mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_UHR; + mgmt->u.action.action_code = + IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST; + + sdata->u.mgd.dialog_token_alloc++; + /* + * NOTE: + * Driver and FW might both send these frames, and iwlwifi + * decided that the driver uses odd numbers, FW uses even + * numbers. For now, hardcode that here, until it matters + * to some other driver. + * + * Note also that there's currently no real synchronisation + * in this case, but it's not valid that both send such a + * frame at the same time, i.e. while waiting for a response + * there can't be another frame sent. This needs addressing + * in the future. + */ + if (sdata->u.mgd.dialog_token_alloc % 2 == 0) + sdata->u.mgd.dialog_token_alloc++; + + sdata->u.mgd.uhr_omp.dialog_token = sdata->u.mgd.dialog_token_alloc; + mgmt->u.action.uhr_link_reconf_req.dialog_token = + sdata->u.mgd.uhr_omp.dialog_token; + mgmt->u.action.uhr_link_reconf_req.type = + IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST; + + skb_put_u8(skb, WLAN_EID_EXTENSION); + ml_elem_len = skb_put(skb, 1); + skb_put_u8(skb, WLAN_EID_EXT_EHT_MULTI_LINK); + mle = skb_put_zero(skb, sizeof(*mle)); + mle->control = cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_RECONF | + IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR); + + common = skb_put(skb, sizeof(*common)); + common->len = sizeof(*common); + memcpy(common->mld_mac_addr, sdata->vif.addr, ETH_ALEN); + + for_each_link_data(sdata, link) { + u8 *subelem_len; + + if (!(link_mask & BIT(link->link_id))) + continue; + + sdata->u.mgd.uhr_omp.links |= BIT(link->link_id); + + skb_put_u8(skb, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE); + subelem_len = skb_put(skb, 1); + per_sta = skb_put_zero(skb, sizeof(*per_sta)); + per_sta->control = + le16_encode_bits(link->link_id, + IEEE80211_MLE_STA_CONTROL_LINK_ID) | + le16_encode_bits(IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_UHR_OMP_UPD, + IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE); + per_sta->sta_info_len = 1; /* includes itself */ + + skb_put_u8(skb, WLAN_EID_EXTENSION); + skb_put_u8(skb, 1 + sizeof(*tuple)); + skb_put_u8(skb, WLAN_EID_EXT_UHR_MODE_CHG); + tuple = skb_put_zero(skb, sizeof(*tuple)); + tuple->control = + le16_encode_bits(IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID); + + if (link->u.mgd.conn.dbe_enabled) + tuple->control |= + cpu_to_le16(IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE); + + ieee80211_fragment_element(skb, subelem_len, + IEEE80211_MLE_SUBELEM_FRAGMENT); + } + + ieee80211_fragment_element(skb, ml_elem_len, WLAN_EID_FRAGMENT); + + info = IEEE80211_SKB_CB(skb); + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + info->status_data = IEEE80211_STATUS_TYPE_UHR_OMP; + ieee80211_tx_skb(sdata, skb); +} + static int ieee80211_config_bw(struct ieee80211_link_data *link, struct ieee802_11_elems *elems, bool update, u64 *changed, u16 stype) { struct ieee80211_channel *channel = link->conf->chanreq.oper.chan; struct cfg80211_chan_def ap_chandef; + struct ieee80211_determine_ap_chan_output ap_chan_out; struct ieee80211_determine_ap_chan_data ap_chan_data = { .channel = channel, .vht_cap_info = 0, @@ -1351,6 +1603,8 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, .chandef = &ap_chandef, .elems = elems, .conn = &link->u.mgd.conn, + .cur_chandef = &link->conf->chanreq.oper, + .cur_dbe_used = link->u.mgd.conn.dbe_enabled, }; struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_chanctx_conf *chanctx_conf; @@ -1387,7 +1641,9 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link, ap_chan_data.vht_cap_info = le32_to_cpu(elems->vht_cap_elem->vht_cap_info); - ap_mode = ieee80211_determine_ap_chan(sdata, &ap_chan_data); + ap_mode = ieee80211_determine_ap_chan(sdata, &ap_chan_data, + &ap_chan_out); + link->u.mgd.conn.dbe_enabled = ap_chan_out.dbe_used; if (ap_mode != link->u.mgd.conn.mode) { link_info(link, @@ -2652,6 +2908,7 @@ static void ieee80211_csa_switch_work(struct wiphy *wiphy, cfg80211_ch_switch_notify(sdata->dev, &link->csa.chanreq.oper, link->link_id); link->conf->csa_active = false; + link->u.mgd.conn.dbe_enabled = false; ap_sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); if (WARN_ON(!ap_sta)) @@ -2662,6 +2919,12 @@ static void ieee80211_csa_switch_work(struct wiphy *wiphy, if (WARN_ON(!link_sta)) return; + /* + * If the link was somehow deactivated in the middle of enabling + * DBE while waiting for a response, this could be stuck, reset. + */ + link_sta->uhr_usable_tx_width = IEEE80211_STA_RX_BW_MAX; + link_sta->pub->bandwidth = ieee80211_sta_current_bw(link_sta, &link->csa.chanreq.oper, @@ -2754,6 +3017,8 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) { struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct link_sta_info *link_sta; + struct sta_info *ap_sta; int ret; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -2763,6 +3028,7 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) ieee80211_vif_unblock_queues_csa(sdata); link->conf->csa_active = false; + link->u.mgd.conn.dbe_enabled = false; link->u.mgd.csa.blocked_tx = false; link->u.mgd.csa.waiting_bcn = false; @@ -2775,6 +3041,20 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) return; } + ap_sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); + if (WARN_ON(!ap_sta)) + return; + + link_sta = sdata_dereference(ap_sta->link[link->link_id], sdata); + if (WARN_ON(!link_sta)) + return; + + /* + * If DBE was being activated and CSA happened, this could be + * on a wrong value. Reset it. + */ + link_sta->uhr_usable_tx_width = IEEE80211_STA_RX_BW_MAX; + cfg80211_ch_switch_notify(sdata->dev, &link->conf->chanreq.oper, link->link_id); } @@ -4479,6 +4759,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(ifmgd->userspace_selectors, 0, sizeof(ifmgd->userspace_selectors)); + + ifmgd->uhr_omp.pending = 0; + ifmgd->uhr_omp.pending_init = 0; } static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) @@ -4799,6 +5082,101 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) ifmgd->reconnect = false; } +static void ieee80211_uhr_omp_req_status(struct ieee80211_sub_if_data *sdata) +{ + bool acked = sdata->u.mgd.uhr_omp.acked; + u16 links = sdata->u.mgd.uhr_omp.links; + struct ieee80211_link_data *link; + struct sta_info *ap; + + /* timer and queued RX could overlap */ + if (!links) + return; + + sdata->u.mgd.uhr_omp.links = 0; + sdata->u.mgd.uhr_omp.acked = false; + + if (!acked) { + sdata_dbg(sdata, "UHR OMP frame not ACKed - disconnect\n"); + __ieee80211_disconnect(sdata); + return; + } + + ap = sta_info_get(sdata, sdata->vif.cfg.ap_addr); + if (!ap) + return; + + for_each_link_data(sdata, link) { + struct link_sta_info *link_sta; + + if (!(links & BIT(link->link_id))) + continue; + + /* only handle transition to enabled for now */ + if (!link->u.mgd.conn.dbe_enabled) + continue; + + link_sta = sdata_dereference(ap->link[link->link_id], sdata); + if (WARN_ON(!link_sta)) + continue; + + link_sta->uhr_usable_tx_width = IEEE80211_STA_RX_BW_MAX; + ieee80211_link_sta_update_rc_bw(link, link_sta); + } + + /* next round - send pending frames if needed */ + + if (sdata->u.mgd.uhr_omp.pending_init) { + links = sdata->u.mgd.uhr_omp.pending_init; + + sdata->u.mgd.uhr_omp.pending_init = 0; + ieee80211_send_uhr_omp_req_dbe(sdata, links, true); + return; + } + + if (sdata->u.mgd.uhr_omp.pending) { + links = sdata->u.mgd.uhr_omp.pending; + + sdata->u.mgd.uhr_omp.pending = 0; + ieee80211_send_uhr_omp_req_dbe(sdata, links, false); + return; + } +} + +static void ieee80211_uhr_omp_req_status_wk(struct wiphy *wiphy, + struct wiphy_work *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.uhr_omp.status_work.work); + + ieee80211_uhr_omp_req_status(sdata); +} + +static void ieee80211_process_uhr_omp_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt) +{ + if (mgmt->u.action.uhr_link_reconf_notif.dialog_token != + sdata->u.mgd.uhr_omp.dialog_token) + return; + + wiphy_hrtimer_work_cancel(sdata->local->hw.wiphy, + &sdata->u.mgd.uhr_omp.status_work); + ieee80211_uhr_omp_req_status(sdata); +} + +static void +ieee80211_process_uhr_link_reconf_notif(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + switch (mgmt->u.action.uhr_link_reconf_notif.type) { + case IEEE80211_UHR_LINK_RECONFIG_NOTIFY_OMP_RESPONSE: + ieee80211_process_uhr_omp_resp(sdata, mgmt); + break; + } +} + static void ieee80211_beacon_connection_loss_work(struct wiphy *wiphy, struct wiphy_work *work) { @@ -5777,12 +6155,27 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, if (elems->uhr_operation && elems->uhr_cap && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_UHR) { + int omp_to_us; + ieee80211_uhr_cap_ie_to_sta_uhr_cap(sdata, sband, elems->uhr_cap, elems->uhr_cap_len, link_sta); bss_conf->uhr_support = link_sta->pub->uhr_cap.has_uhr; + + /* + * This assumes that the timeout is the same across all links, + * maybe we should actually validate that. + */ + omp_to_us = ieee80211_uhr_capa_get_om_pu_to_us(elems->uhr_cap); + if (omp_to_us < 0) { + ret = false; + link_info(link, "Invalid UHR OMP timeout\n"); + goto out; + } + + sdata->u.mgd.uhr_omp.timeout_us = omp_to_us; } else { bss_conf->uhr_support = false; } @@ -6312,6 +6705,7 @@ ieee80211_determine_our_sta_mode_assoc(struct ieee80211_sub_if_data *sdata, static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link, + struct link_sta_info *link_sta, int link_id, struct cfg80211_bss *cbss, bool mlo, struct ieee80211_conn_settings *conn, @@ -6327,7 +6721,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, lockdep_assert_wiphy(local->hw.wiphy); rcu_read_lock(); - elems = ieee80211_determine_chan_mode(sdata, conn, cbss, link_id, + elems = ieee80211_determine_chan_mode(sdata, conn, cbss, + link_sta, link_id, &chanreq, &ap_chandef, userspace_selectors); @@ -6647,7 +7042,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (link_id != assoc_data->assoc_link_id) { link->u.mgd.conn = assoc_data->link[link_id].conn; - err = ieee80211_prep_channel(sdata, link, link_id, cbss, + err = ieee80211_prep_channel(sdata, link, link_sta, + link_id, cbss, true, &link->u.mgd.conn, sdata->u.mgd.userspace_selectors); if (err) { @@ -6728,6 +7124,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_sta_reset_conn_monitor(sdata); + ieee80211_send_uhr_omp_req_dbe(sdata, ~0, true); + return true; out_err: eth_zero_addr(sdata->vif.cfg.ap_addr); @@ -9067,6 +9465,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ieee80211_neg_ttlm_timeout_work); wiphy_work_init(&ifmgd->teardown_ttlm_work, ieee80211_teardown_ttlm_work); + wiphy_hrtimer_work_init(&ifmgd->uhr_omp.status_work, + ieee80211_uhr_omp_req_status_wk); ifmgd->flags = 0; ifmgd->powersave = sdata->wdev.ps; @@ -9281,6 +9681,9 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, } if (new_sta || override) { + struct link_sta_info *link_sta; + struct sta_info *ap; + /* * Only set this if we're also going to calculate the AP * settings etc., otherwise this was set before in a @@ -9288,7 +9691,18 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, * if the settings were changed. */ link->u.mgd.conn = *conn; - err = ieee80211_prep_channel(sdata, link, link->link_id, cbss, + + ap = new_sta ?: have_sta; + link_sta = sdata_dereference(ap->link[link->link_id], sdata); + if (!link_sta) { + err = -EINVAL; + if (new_sta) + sta_info_free(local, new_sta); + goto out_err; + } + + err = ieee80211_prep_channel(sdata, link, link_sta, + link->link_id, cbss, mlo, &link->u.mgd.conn, userspace_selectors); if (err) { @@ -10183,8 +10597,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, continue; if (i == assoc_data->assoc_link_id) continue; - /* only calculate the mode, hence link == NULL */ - err = ieee80211_prep_channel(sdata, NULL, i, + /* only calculate the mode, hence link/link_sta == NULL */ + err = ieee80211_prep_channel(sdata, NULL, NULL, i, assoc_data->link[i].bss, true, &assoc_data->link[i].conn, sdata->u.mgd.userspace_selectors); @@ -10367,6 +10781,8 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) &ifmgd->csa_connection_drop_work); wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->tdls_peer_del_work); + wiphy_hrtimer_work_cancel(sdata->local->hw.wiphy, + &ifmgd->uhr_omp.status_work); if (ifmgd->assoc_data) ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT); @@ -10613,7 +11029,7 @@ ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, link->conf->dtim_period = link->u.mgd.dtim_period ?: 1; link->u.mgd.conn = add_links_data->link[link_id].conn; - if (ieee80211_prep_channel(sdata, link, link_id, cbss, + if (ieee80211_prep_channel(sdata, link, link_sta, link_id, cbss, true, &link->u.mgd.conn, sdata->u.mgd.userspace_selectors)) { link_info(link, "mlo: reconf: prep_channel failed\n"); @@ -10658,6 +11074,8 @@ ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_ps(local); ieee80211_recalc_ps_vif(sdata); + ieee80211_send_uhr_omp_req_dbe(sdata, link_mask, true); + done_data.buf = (const u8 *)mgmt; done_data.len = orig_len; done_data.added_links = link_mask; @@ -11028,7 +11446,7 @@ int ieee80211_mgd_assoc_ml_reconf(struct ieee80211_sub_if_data *sdata, continue; /* only used to verify the mode, nothing is allocated */ - err = ieee80211_prep_channel(sdata, NULL, link_id, + err = ieee80211_prep_channel(sdata, NULL, NULL, link_id, data->link[link_id].bss, true, &data->link[link_id].conn, @@ -11490,6 +11908,15 @@ void ieee80211_sta_rx_queued_frame(struct ieee80211_sub_if_data *sdata, break; } break; + case WLAN_CATEGORY_PROTECTED_UHR: + switch (mgmt->u.action.action_code) { + case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY: + ieee80211_process_uhr_link_reconf_notif(sdata, + mgmt, + skb->len); + break; + } + break; } break; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 91b4f6cbfce8..858c3f659119 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3947,6 +3947,20 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) break; } break; + case WLAN_CATEGORY_PROTECTED_UHR: + if (len < IEEE80211_MIN_ACTION_SIZE(action_code)) + break; + + switch (mgmt->u.action.action_code) { + case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY: + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + + if (len < IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_notif)) + goto invalid; + goto queue; + } + break; } return RX_CONTINUE; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 205e8d5c0c72..2787be556034 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -572,6 +572,20 @@ static int sta_info_alloc_link(struct ieee80211_local *local, link_info->rx_omi_bw_tx = IEEE80211_STA_RX_BW_MAX; link_info->rx_omi_bw_staging = IEEE80211_STA_RX_BW_MAX; + /* + * This will always be taken into account, so set to MAX. + * When mac80211 is the client on a UHR AP, it'll be used + * for the TX side, to limit the bandwidth to TX to the AP + * with, to limit to the BSS width during DBE enablement. + * + * This is needed since the chanreq, which normally has + * maximum bandwidth to use with the AP, will already be + * set to the DBE width during enablement to prepare for + * RX (and not be racy), but the TX can only use higher + * bandwidth after enablement finishes. + */ + link_info->uhr_usable_tx_width = IEEE80211_STA_RX_BW_MAX; + link_info->op_mode_bw = IEEE80211_STA_RX_BW_MAX; /* @@ -3727,6 +3741,8 @@ ieee80211_sta_current_bw_tx_to_sta(struct link_sta_info *link_sta, bw = min(bw, link_sta->op_mode_bw); /* also limit to RX OMI bandwidth we TX to the STA */ bw = min(bw, link_sta->rx_omi_bw_tx); + /* and UHR DBE transition limits */ + bw = min(bw, link_sta->uhr_usable_tx_width); /* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of * IEEE80211-2016 specification makes higher bandwidth operation diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 3abac031d01c..0f56a6e53629 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -511,6 +511,9 @@ struct ieee80211_fragment_cache { * @rx_omi_bw_tx: RX OMI bandwidth restriction to apply for TX * @rx_omi_bw_staging: RX OMI bandwidth restriction to apply later * during finalize + * @uhr_usable_tx_width: bandwidth restriction for UHR for TX, only when + * the link_sta is an AP, to restrict TX to BSS width during DBE + * enablement * @debugfs_dir: debug filesystem directory dentry * @pub: public (driver visible) link STA data */ @@ -562,6 +565,7 @@ struct link_sta_info { enum ieee80211_sta_rx_bandwidth rx_omi_bw_rx, rx_omi_bw_tx, rx_omi_bw_staging; + enum ieee80211_sta_rx_bandwidth uhr_usable_tx_width; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *debugfs_dir; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 8716eda8317d..dd1dbba06838 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -731,6 +731,28 @@ ieee80211_handle_teardown_ttlm_status(struct ieee80211_sub_if_data *sdata, &sdata->u.mgd.teardown_ttlm_work); } +static void +ieee80211_handle_uhr_omp_status(struct ieee80211_sub_if_data *sdata, bool acked) +{ + if (!sdata || !ieee80211_sdata_running(sdata)) + return; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return; + + sdata->u.mgd.uhr_omp.acked = acked; + + if (!acked) { + wiphy_hrtimer_work_queue(sdata->local->hw.wiphy, + &sdata->u.mgd.uhr_omp.status_work, 0); + return; + } + + wiphy_hrtimer_work_queue(sdata->local->hw.wiphy, + &sdata->u.mgd.uhr_omp.status_work, + us_to_ktime(sdata->u.mgd.uhr_omp.timeout_us)); +} + static void ieee80211_report_used_skb(struct ieee80211_local *local, struct sk_buff *skb, bool dropped, ktime_t ack_hwtstamp) @@ -811,6 +833,9 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, case IEEE80211_STATUS_TYPE_NEG_TTLM: ieee80211_handle_teardown_ttlm_status(sdata, acked); break; + case IEEE80211_STATUS_TYPE_UHR_OMP: + ieee80211_handle_uhr_omp_status(sdata, acked); + break; } rcu_read_unlock(); } diff --git a/net/mac80211/tests/chan-mode.c b/net/mac80211/tests/chan-mode.c index fa370831d617..ab7d38ef6a3a 100644 --- a/net/mac80211/tests/chan-mode.c +++ b/net/mac80211/tests/chan-mode.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel mode functions * - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include #include @@ -243,7 +243,7 @@ static void test_determine_chan_mode(struct kunit *test) rcu_read_lock(); elems = ieee80211_determine_chan_mode(t_sdata->sdata, &conn, &cbss, - 0, &chanreq, &ap_chandef, + NULL, 0, &chanreq, &ap_chandef, userspace_selectors); rcu_read_unlock(); From 14b1a85518a17c01a8d52a8d3332596b68b4f201 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:08 +0200 Subject: [PATCH 151/285] wifi: mac80211: AP: handle DBE for clients In AP mode, track the BSS non-DBE bandwidth and apply that to all non-DBE clients, then track OMP updates from the clients and enable/disable DBE accordingly. For now don't send a response, clients need to have a timer anyway (it's up to the driver to set the right timeout in UHR capabilities.) Link: https://patch.msgid.link/20260529102644.be84f2b055cc.I4d2c067dfe54c47621d5a872ca07a0e754d6c20f@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211-eht.h | 16 +++- include/linux/ieee80211-uhr.h | 16 ++++ net/mac80211/ap.c | 146 ++++++++++++++++++++++++++++++++++ net/mac80211/cfg.c | 35 ++++++++ net/mac80211/ieee80211_i.h | 2 + net/mac80211/rx.c | 9 +++ net/mac80211/sta_info.c | 10 ++- net/mac80211/sta_info.h | 4 + 8 files changed, 232 insertions(+), 6 deletions(-) diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index 73e97fe30724..18f9c662cf4c 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -393,14 +393,24 @@ ieee80211_eht_oper_size_ok(const u8 *data, u8 len) return len >= needed; } +/* must validate ieee80211_eht_oper_size_ok() first */ +static inline const struct ieee80211_eht_operation_info * +ieee80211_eht_oper_info(const struct ieee80211_eht_operation *eht_oper) +{ + if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) + return NULL; + + return (const void *)eht_oper->optional; +} + /* must validate ieee80211_eht_oper_size_ok() first */ static inline u16 ieee80211_eht_oper_dis_subchan_bitmap(const struct ieee80211_eht_operation *eht_oper) { - const struct ieee80211_eht_operation_info *info = - (const void *)eht_oper->optional; + const struct ieee80211_eht_operation_info *info; - if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) + info = ieee80211_eht_oper_info(eht_oper); + if (!info) return 0; if (!(eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h index 1dd53c5f2954..597c9e559261 100644 --- a/include/linux/ieee80211-uhr.h +++ b/include/linux/ieee80211-uhr.h @@ -628,4 +628,20 @@ struct ieee80211_uhr_mode_change_tuple { u8 variable[]; } __packed; +static inline int +ieee80211_uhr_mode_change_tuple_size(const struct ieee80211_uhr_mode_change_tuple *tuple) +{ + return sizeof(*tuple) + + le16_get_bits(tuple->control, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_LENGTH); +} + +#define for_each_uhr_mode_change_tuple(data, len, tuple) \ + for (tuple = (const void *)(data); \ + (len) - ((const u8 *)tuple - (data)) >= sizeof(*tuple) && \ + (len) - ((const u8 *)tuple - (data)) >= \ + ieee80211_uhr_mode_change_tuple_size(tuple); \ + tuple = (const void *)((const u8 *)tuple + \ + ieee80211_uhr_mode_change_tuple_size(tuple))) + #endif /* LINUX_IEEE80211_UHR_H */ diff --git a/net/mac80211/ap.c b/net/mac80211/ap.c index 6c7d2d51a372..e7ac99c5a22e 100644 --- a/net/mac80211/ap.c +++ b/net/mac80211/ap.c @@ -8,6 +8,7 @@ #include "driver-ops.h" #include "ieee80211_i.h" +#include "rate.h" static void ieee80211_send_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata, @@ -186,6 +187,113 @@ ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata, ieee80211_send_eml_op_mode_notif(sdata, mgmt, opt_len); } +static void +ieee80211_rx_uhr_link_reconfig_req(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + const struct element *sub; + struct sta_info *sta; + + /* + * rx.c only accepts IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST + * which is valid, so no need to check the frame type/format/etc. + */ + + sta = sta_info_get_bss(sdata, mgmt->sa); + if (!sta) + return; + + struct ieee802_11_elems *elems __free(kfree) = + ieee802_11_parse_elems(mgmt->u.action.uhr_link_reconf_req.variable, + skb->len - IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req), + IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION, + NULL); + /* STA will assume we processed it, not good */ + if (!elems) + return; + + if (!elems->ml_reconf) + return; + + for_each_mle_subelement(sub, (u8 *)elems->ml_reconf, + elems->ml_reconf_len) { + const struct ieee80211_mle_per_sta_profile *prof = + (const void *)sub->data; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + struct ieee80211_link_data *link; + struct link_sta_info *link_sta; + const struct element *chg; + u16 control; + u8 link_id; + + if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) + continue; + + if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data, + sub->datalen)) + return; + + control = le16_to_cpu(prof->control); + link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID; + + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return; + + link = sdata_dereference(sdata->link[link_id], sdata); + if (!link) + continue; + + chanctx_conf = sdata_dereference(link->conf->chanctx_conf, + sdata); + if (!chanctx_conf) + continue; + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, + conf); + + link_sta = sdata_dereference(sta->link[link_id], sdata); + if (!link_sta) + continue; + + /* do we need to handle any other bits? */ + if (control & ~(IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID | + IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE)) + continue; + + if (u16_get_bits(control, IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE) != + IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_TYPE_UHR_OMP_UPD) + continue; + + for_each_element_extid(chg, WLAN_EID_EXT_UHR_MODE_CHG, + prof->variable + prof->sta_info_len - 1, + sub->datalen - sizeof(*prof) - + prof->sta_info_len + 1) { + const struct ieee80211_uhr_mode_change_tuple *tuple; + + for_each_uhr_mode_change_tuple(chg->data + 1, + chg->datalen - 1, + tuple) { + u8 id = le16_get_bits(tuple->control, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID); + bool enabled = le16_get_bits(tuple->control, + IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE); + + /* only handle DBE (for now?) */ + if (id != IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE) + continue; + + link_sta->uhr_dbe_enabled = enabled; + /* also recalculates and updates per-STA bw */ + ieee80211_recalc_chanctx_min_def(sdata->local, + chanctx); + } + } + } + + /* TODO: send a response */ +} + void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { @@ -203,5 +311,43 @@ void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata, break; } break; + case WLAN_CATEGORY_PROTECTED_UHR: + switch (mgmt->u.action.action_code) { + case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST: + ieee80211_rx_uhr_link_reconfig_req(sdata, skb); + break; + } + break; } } + +void ieee80211_uhr_disable_dbe_all_stas(struct ieee80211_link_data *link) +{ + struct ieee80211_sub_if_data *sdata = link->sdata; + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + int link_id = link->link_id; + struct sta_info *sta; + + chanctx_conf = sdata_dereference(link->conf->chanctx_conf, sdata); + if (!chanctx_conf) + return; + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); + + list_for_each_entry(sta, &local->sta_list, list) { + struct link_sta_info *link_sta; + + if (sta->sdata->bss != sdata->bss) + continue; + + link_sta = sdata_dereference(sta->link[link_id], sdata); + if (!link_sta) + continue; + + link_sta->uhr_dbe_enabled = false; + } + + /* also recalculates and updates per-STA bw */ + ieee80211_recalc_chanctx_min_def(local, chanctx); +} diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1554e31f0029..3b58af59f7e4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1391,6 +1391,36 @@ ieee80211_calc_ap_he_and_lower(struct cfg80211_beacon_data *params) return IEEE80211_STA_RX_BW_20; } +static enum ieee80211_sta_rx_bandwidth +ieee80211_calc_ap_eht_bw(struct cfg80211_beacon_data *params, + enum ieee80211_sta_rx_bandwidth he_and_lower) +{ + const struct ieee80211_eht_operation_info *info; + + if (!params->eht_oper) + return he_and_lower; + + info = ieee80211_eht_oper_info(params->eht_oper); + if (!info) + return he_and_lower; + + switch (u8_get_bits(info->control, IEEE80211_EHT_OPER_CHAN_WIDTH)) { + case IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ: + return IEEE80211_STA_RX_BW_20; + case IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ: + return IEEE80211_STA_RX_BW_40; + case IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ: + return IEEE80211_STA_RX_BW_80; + case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ: + return IEEE80211_STA_RX_BW_160; + case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ: + return IEEE80211_STA_RX_BW_320; + } + + /* invalid setting, assume 20 MHz */ + return IEEE80211_STA_RX_BW_20; +} + static void ieee80211_update_ap_bandwidth(struct ieee80211_link_data *link, struct cfg80211_beacon_data *params) { @@ -1415,6 +1445,8 @@ static void ieee80211_update_ap_bandwidth(struct ieee80211_link_data *link, return; link->bss_bw.he_and_lower = ieee80211_calc_ap_he_and_lower(params); + link->bss_bw.eht = ieee80211_calc_ap_eht_bw(params, + link->bss_bw.he_and_lower); chanctx_conf = sdata_dereference(link->conf->chanctx_conf, link->sdata); chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); @@ -4469,6 +4501,9 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) ieee80211_link_info_change_notify(sdata, link_data, changed); + if (sdata->vif.type == NL80211_IFTYPE_AP) + ieee80211_uhr_disable_dbe_all_stas(link_data); + ieee80211_vif_unblock_queues_csa(sdata); err = drv_post_channel_switch(link_data); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 29bdfd2a39bd..34a9ea8b6f85 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1146,6 +1146,7 @@ struct ieee80211_link_data { struct { enum ieee80211_sta_rx_bandwidth he_and_lower; + enum ieee80211_sta_rx_bandwidth eht; /* and UHR non-DBE */ } bss_bw; #ifdef CONFIG_MAC80211_DEBUGFS @@ -2003,6 +2004,7 @@ bool ieee80211_is_our_addr(struct ieee80211_sub_if_data *sdata, /* AP code */ void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +void ieee80211_uhr_disable_dbe_all_stas(struct ieee80211_link_data *link); /* STA code */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 858c3f659119..db421edfd54c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3952,6 +3952,15 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) break; switch (mgmt->u.action.action_code) { + case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST: + if (sdata->vif.type != NL80211_IFTYPE_AP) + break; + if (len < IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req)) + goto invalid; + if (mgmt->u.action.uhr_link_reconf_req.type != + IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST) + break; + goto queue; case IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY: if (sdata->vif.type != NL80211_IFTYPE_STATION) break; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 2787be556034..02b587ff8504 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -3701,10 +3701,14 @@ ieee80211_sta_usable_bw(struct link_sta_info *link_sta, if (WARN_ON(!link)) return IEEE80211_STA_RX_BW_20; - if (link_sta->pub->eht_cap.has_eht) - return bw; + if (!link_sta->pub->eht_cap.has_eht) + return min(bw, link->bss_bw.he_and_lower); - return min(bw, link->bss_bw.he_and_lower); + if (!link_sta->pub->uhr_cap.has_uhr || + !link_sta->uhr_dbe_enabled) + return min(bw, link->bss_bw.eht); + + return bw; } static enum ieee80211_sta_rx_bandwidth diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 0f56a6e53629..26138934b72e 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -514,6 +514,8 @@ struct ieee80211_fragment_cache { * @uhr_usable_tx_width: bandwidth restriction for UHR for TX, only when * the link_sta is an AP, to restrict TX to BSS width during DBE * enablement + * @uhr_dbe_enabled: for STAs as clients to an AP interface indicates + * DBE is enabled by the STA * @debugfs_dir: debug filesystem directory dentry * @pub: public (driver visible) link STA data */ @@ -567,6 +569,8 @@ struct link_sta_info { rx_omi_bw_staging; enum ieee80211_sta_rx_bandwidth uhr_usable_tx_width; + bool uhr_dbe_enabled; + #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *debugfs_dir; #endif From ade3b16a01568ab1feceddfb0841aabfaaf7953e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 May 2026 10:25:09 +0200 Subject: [PATCH 152/285] wifi: mac80211_hwsim: claim DBE capability Claim DBE capability in UHR MAC capabilities, hostapd will have to sort out the actual DBE capabilities based on the EHT capabilities. Link: https://patch.msgid.link/20260529102644.4db84674e8c2.I8731be8ea589c94ece5623e7e716cbbc03f50466@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/virtual/mac80211_hwsim_main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index de0bb4ce7456..0dd8a6c85953 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -4708,6 +4708,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = { .has_uhr = true, .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, }, .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX | IEEE80211_UHR_PHY_CAP_ELR_TX), @@ -4885,6 +4886,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { .has_uhr = true, .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, }, .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_TX), }, @@ -5017,6 +5019,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = { .has_uhr = true, .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, }, .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX), }, @@ -5217,6 +5220,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { .has_uhr = true, .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, }, .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_TX), }, @@ -5370,6 +5374,7 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = { .has_uhr = true, .mac.mac_cap = { [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + [1] = IEEE80211_UHR_MAC_CAP1_DBE_SUPP, }, .phy.cap = cpu_to_le32(IEEE80211_UHR_PHY_CAP_ELR_RX), }, From 1821c068aa5ea852970b5c9ca05b21681f5d8e83 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 12 May 2026 08:22:57 +0300 Subject: [PATCH 153/285] wifi: iwlwifi: add support for AX231 AX231 is a device that is based on AX211 that doesn't support 6E and its bandwidth is limited to 80 MHz. Just reuse the radio config from AX203 which has the exact same characteristics. It has a specific subdevice ID to allow the driver to differentiate between AX211 and AX231. Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20260512082114.0685ed313987.Ibcfa24e196ac778405d2843f0984b66ca167704e@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c | 3 ++- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 4 +++- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c index 99a5110924cd..8b63cdb3d66c 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include "iwl-config.h" @@ -60,6 +60,7 @@ const char iwl_ax411_killer_1690i_name[] = const char iwl_ax210_name[] = "Intel(R) Wi-Fi 6E AX210 160MHz"; const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz"; +const char iwl_ax231_name[] = "Intel(R) Wi-Fi 6 AX231"; const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6E AX411 160MHz"; MODULE_FIRMWARE(IWL_BZ_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 30d5ec31b9c3..933a96131d1a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2005-2014, 2018-2021 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #ifndef __IWL_CONFIG_H__ #define __IWL_CONFIG_H__ @@ -669,6 +669,7 @@ extern const char iwl_ax411_killer_1690s_name[]; extern const char iwl_ax411_killer_1690i_name[]; extern const char iwl_ax210_name[]; extern const char iwl_ax211_name[]; +extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; extern const char iwl_killer_be1750s_name[]; extern const char iwl_killer_be1750i_name[]; @@ -740,6 +741,7 @@ extern const struct iwl_rf_cfg iwl_rf_hr; extern const struct iwl_rf_cfg iwl_rf_hr_80mhz; extern const struct iwl_rf_cfg iwl_rf_gf; +#define iwl_rf_ot iwl_rf_hr_80mhz #endif /* CONFIG_IWLMVM */ #if IS_ENABLED(CONFIG_IWLMLD) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 608100bc6b11..5eb09ee24944 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -1016,6 +1016,7 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { /* GF RF */ IWL_DEV_INFO(iwl_rf_gf, iwl_ax211_name, RF_TYPE(GF)), + IWL_DEV_INFO(iwl_rf_ot, iwl_ax231_name, RF_TYPE(GF), SUBDEV(0x0294)), IWL_DEV_INFO(iwl_rf_gf, iwl_ax411_name, RF_TYPE(GF), CDB), IWL_DEV_INFO(iwl_rf_gf, iwl_ax210_name, DEVICE(0x2725)), From e1ed149b321e8eab9957c109efa79fd850b5ff98 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 May 2026 23:04:58 +0300 Subject: [PATCH 154/285] wifi: iwlwifi: fw: cut down NIC wakeups during dump Currently, the dump code attempts to dump any number of memories and register banks, as defined by the firmware. Especially when the device is failing, this can lead to excessive time spent attempting to acquire NIC access over and over again. Improve the code to only attempt to acquire NIC access once or twice, but using the new memory dump functions that may drop the spinlock etc. Mark all dump regions that require NIC access, and skip them if we couldn't obtain that. In order to avoid CPU latency due to the increased time holding the spinlock (and possibly disabling softirqs), drop locks and call cond_resched() after each section (if holding NIC access) but don't release HW NIC access. Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260527230313.bec886142cc8.I41f2eaf2403b38147504d5dab0a7414de2699adc@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 90 +++++++++---------- .../net/wireless/intel/iwlwifi/iwl-trans.c | 5 ++ .../net/wireless/intel/iwlwifi/iwl-trans.h | 11 +++ .../intel/iwlwifi/pcie/gen1_2/internal.h | 1 + .../intel/iwlwifi/pcie/gen1_2/trans.c | 2 +- 5 files changed, 63 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 069c3bad6f29..337e312c2f43 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -103,7 +103,7 @@ static int iwl_dump_ini_prph_mac_iter_common(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = size; for (i = 0; i < le32_to_cpu(size); i += 4) - *val++ = cpu_to_le32(iwl_read_prph(fwrt->trans, addr + i)); + *val++ = cpu_to_le32(iwl_trans_read_prph(fwrt->trans, addr + i)); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -158,9 +158,6 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt, indirect_wr_addr += le32_to_cpu(offset); indirect_rd_addr += le32_to_cpu(offset); - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - dphy_addr = (offset) ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW; dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr); @@ -194,7 +191,6 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt, *val++ = cpu_to_le32(prph_val); } - iwl_trans_release_nic_access(fwrt->trans); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -283,8 +279,8 @@ static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->dev_addr.size; - iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, - le32_to_cpu(reg->dev_addr.size)); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, addr, range->data, + le32_to_cpu(reg->dev_addr.size)); if (reg->sub_type == IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_HW_SMEM && fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf) @@ -368,8 +364,8 @@ static int iwl_dump_ini_mon_smem_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->internal_buffer.size; - iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, - le32_to_cpu(reg->internal_buffer.size)); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, addr, range->data, + le32_to_cpu(reg->internal_buffer.size)); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -443,9 +439,6 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, if (!iwl_ini_txf_iter(fwrt, reg_data, idx)) return -EIO; - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - range->fifo_hdr.fifo_num = cpu_to_le32(iter->fifo); range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num); range->range_data_size = cpu_to_le32(iter->fifo_size + registers_size); @@ -489,8 +482,6 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, reg_dump, iter->fifo_size); out: - iwl_trans_release_nic_access(fwrt->trans); - return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -511,9 +502,6 @@ iwl_dump_ini_prph_snps_dphyip_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->dev_addr.size; - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - indirect_rd_wr_addr += le32_to_cpu(offset); dphy_addr = offset ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW; @@ -537,7 +525,6 @@ iwl_dump_ini_prph_snps_dphyip_iter(struct iwl_fw_runtime *fwrt, DPHYIP_INDIRECT_RD_SHIFT); } - iwl_trans_release_nic_access(fwrt->trans); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -626,9 +613,6 @@ static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, if (!rxf_data.size) return -EIO; - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - range->fifo_hdr.fifo_num = cpu_to_le32(rxf_data.fifo_num); range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num); range->range_data_size = cpu_to_le32(rxf_data.size + registers_size); @@ -668,8 +652,6 @@ static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, *data++ = cpu_to_le32(iwl_trans_read_prph(fwrt->trans, addr)); out: - iwl_trans_release_nic_access(fwrt->trans); - return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -686,8 +668,8 @@ iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = err_table->size; - iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, - le32_to_cpu(err_table->size)); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, addr, range->data, + le32_to_cpu(err_table->size)); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -707,8 +689,8 @@ iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = special_mem->size; - iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, - le32_to_cpu(special_mem->size)); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, addr, range->data, + le32_to_cpu(special_mem->size)); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -724,9 +706,6 @@ iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, u32 prph_data; int i; - if (!iwl_trans_grab_nic_access(fwrt->trans)) - return -EBUSY; - range->range_data_size = reg->dev_addr.size; for (i = 0; i < (le32_to_cpu(reg->dev_addr.size) / 4); i++) { prph_data = @@ -734,13 +713,11 @@ iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, (i % 2) ? DBGI_SRAM_TARGET_ACCESS_RDATA_MSB : DBGI_SRAM_TARGET_ACCESS_RDATA_LSB); - if (iwl_trans_is_hw_error_value(prph_data)) { - iwl_trans_release_nic_access(fwrt->trans); + if (iwl_trans_is_hw_error_value(prph_data)) return -EBUSY; - } *val++ = cpu_to_le32(prph_data); } - iwl_trans_release_nic_access(fwrt->trans); + return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -787,8 +764,8 @@ static int iwl_dump_ini_imr_iter(struct iwl_fw_runtime *fwrt, fwrt->trans->dbg.imr_data.imr_curr_addr = imr_curr_addr + size_to_dump; fwrt->trans->dbg.imr_data.imr2sram_remainbyte -= size_to_dump; - iwl_trans_read_mem_bytes(fwrt->trans, sram_addr, range->data, - size_to_dump); + iwl_trans_read_mem_bytes_no_grab(fwrt->trans, sram_addr, range->data, + size_to_dump); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -842,11 +819,6 @@ iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id, struct iwl_fw_ini_monitor_dump *data, const struct iwl_fw_mon_regs *addrs) { - if (!iwl_trans_grab_nic_access(fwrt->trans)) { - IWL_ERR(fwrt, "Failed to get monitor header\n"); - return NULL; - } - data->write_ptr = iwl_get_mon_reg(fwrt, alloc_id, &addrs->write_ptr); if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { @@ -859,8 +831,6 @@ iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id, data->cur_frag = iwl_get_mon_reg(fwrt, alloc_id, &addrs->cur_frag); - iwl_trans_release_nic_access(fwrt->trans); - data->header.version = cpu_to_le32(IWL_INI_DUMP_VER); return data->data; @@ -1266,6 +1236,7 @@ iwl_dump_ini_imr_get_size(struct iwl_fw_runtime *fwrt, * the first range or NULL if failed to fill headers. * @fill_range: copies a given memory range into the dump. * Returns the size of the range or negative error value otherwise. + * @requires_nic_access: indicates to dump only if NIC access was acquired */ struct iwl_dump_ini_mem_ops { u32 (*get_num_of_ranges)(struct iwl_fw_runtime *fwrt, @@ -1278,6 +1249,7 @@ struct iwl_dump_ini_mem_ops { int (*fill_range)(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data, void *range, int idx); + bool requires_nic_access; }; struct iwl_fw_ini_dump_entry { @@ -1374,7 +1346,8 @@ static void iwl_dump_ini_mem_prep(struct iwl_fw_runtime *fwrt, } static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, - struct iwl_fw_ini_dump_entry *entry) + struct iwl_fw_ini_dump_entry *entry, + bool have_nic_access) { struct iwl_dump_ini_region_data *reg_data = &entry->reg_data; struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; @@ -1388,6 +1361,9 @@ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, u32 free_size; u64 header_size; + if (!have_nic_access && ops->requires_nic_access) + goto out_err; + tlv = (void *)entry->data; header = (void *)tlv->data; @@ -1541,36 +1517,42 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .get_size = iwl_dump_ini_mon_smem_get_size, .fill_mem_hdr = iwl_dump_ini_mon_smem_fill_header, .fill_range = iwl_dump_ini_mon_smem_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_DRAM_BUFFER] = { .get_num_of_ranges = iwl_dump_ini_mon_dram_ranges, .get_size = iwl_dump_ini_mon_dram_get_size, .fill_mem_hdr = iwl_dump_ini_mon_dram_fill_header, .fill_range = iwl_dump_ini_mon_dram_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_TXF] = { .get_num_of_ranges = iwl_dump_ini_txf_ranges, .get_size = iwl_dump_ini_txf_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_txf_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_RXF] = { .get_num_of_ranges = iwl_dump_ini_single_range, .get_size = iwl_dump_ini_rxf_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_rxf_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_LMAC_ERROR_TABLE] = { .get_num_of_ranges = iwl_dump_ini_single_range, .get_size = iwl_dump_ini_err_table_get_size, .fill_mem_hdr = iwl_dump_ini_err_table_fill_header, .fill_range = iwl_dump_ini_err_table_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_UMAC_ERROR_TABLE] = { .get_num_of_ranges = iwl_dump_ini_single_range, .get_size = iwl_dump_ini_err_table_get_size, .fill_mem_hdr = iwl_dump_ini_err_table_fill_header, .fill_range = iwl_dump_ini_err_table_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_RSP_OR_NOTIF] = { .get_num_of_ranges = iwl_dump_ini_single_range, @@ -1583,30 +1565,35 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .get_size = iwl_dump_ini_mem_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_dev_mem_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_MAC] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, .get_size = iwl_dump_ini_mem_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_prph_mac_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_PHY] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, .get_size = iwl_dump_ini_mem_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_prph_phy_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_MAC_RANGE] = { .get_num_of_ranges = iwl_dump_ini_mem_block_ranges, .get_size = iwl_dump_ini_mem_block_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_prph_mac_block_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE] = { .get_num_of_ranges = iwl_dump_ini_mem_block_ranges, .get_size = iwl_dump_ini_mem_block_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_prph_phy_block_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_AUX] = {}, [IWL_FW_INI_REGION_PAGING] = { @@ -1626,6 +1613,7 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .get_size = iwl_dump_ini_imr_get_size, .fill_mem_hdr = iwl_dump_ini_imr_fill_header, .fill_range = iwl_dump_ini_imr_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PCI_IOSF_CONFIG] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, @@ -1638,18 +1626,21 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { .get_size = iwl_dump_ini_special_mem_get_size, .fill_mem_hdr = iwl_dump_ini_special_mem_fill_header, .fill_range = iwl_dump_ini_special_mem_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_DBGI_SRAM] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, .get_size = iwl_dump_ini_mon_dbgi_get_size, .fill_mem_hdr = iwl_dump_ini_mon_dbgi_fill_header, .fill_range = iwl_dump_ini_dbgi_sram_iter, + .requires_nic_access = true, }, [IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP] = { .get_num_of_ranges = iwl_dump_ini_mem_ranges, .get_size = iwl_dump_ini_mem_get_size, .fill_mem_hdr = iwl_dump_ini_mem_fill_header, .fill_range = iwl_dump_ini_prph_snps_dphyip_iter, + .requires_nic_access = true, }, }; @@ -1733,8 +1724,11 @@ iwl_dump_ini_dump_entries(struct iwl_fw_runtime *fwrt, enum iwl_dump_ini_region_selector which) { struct iwl_fw_ini_dump_entry *entry, *tmp; + bool have_nic_access; u32 size = 0; + have_nic_access = iwl_trans_grab_nic_access(fwrt->trans); + list_for_each_entry_safe(entry, tmp, list, list) { u32 dp = entry->region_dump_policy; @@ -1751,9 +1745,15 @@ iwl_dump_ini_dump_entries(struct iwl_fw_runtime *fwrt, break; } - size += iwl_dump_ini_mem(fwrt, entry); + size += iwl_dump_ini_mem(fwrt, entry, have_nic_access); + + if (have_nic_access) + iwl_trans_resched_with_nic_access(fwrt->trans); } + if (have_nic_access) + iwl_trans_release_nic_access(fwrt->trans); + return size; } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 0009488ca51b..73aae1125042 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -558,6 +558,11 @@ bool iwl_trans_grab_nic_access(struct iwl_trans *trans) } IWL_EXPORT_SYMBOL(iwl_trans_grab_nic_access); +void iwl_trans_resched_with_nic_access(struct iwl_trans *trans) +{ + iwl_trans_pcie_resched_with_nic_access(trans); +} + void __releases(nic_access) iwl_trans_release_nic_access(struct iwl_trans *trans) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 3ae840e546e8..914864005704 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -981,6 +981,17 @@ void iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, bool iwl_trans_grab_nic_access(struct iwl_trans *trans); +/** + * iwl_trans_resched_with_nic_access - reschedule while holding NIC access + * @trans: the transport pointer + * + * This can be called while holding NIC access, during periods where + * the lock itself isn't interesting, the NIC should remain active, + * but a lot of processing is happening and the CPU may need to be + * released. In practice, this is only during FW dump. + */ +void iwl_trans_resched_with_nic_access(struct iwl_trans *trans); + void __releases(nic_access) iwl_trans_release_nic_access(struct iwl_trans *trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index abc0c831d1ca..d84c7c1efee1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1203,6 +1203,7 @@ void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val); bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans); +void iwl_trans_pcie_resched_with_nic_access(struct iwl_trans *trans); void __releases(nic_access_nobh) iwl_trans_pcie_release_nic_access(struct iwl_trans *trans); void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 1c4ee76d8387..40fd5f3fdeb7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -2424,7 +2424,7 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) return false; } -static void iwl_trans_pcie_resched_with_nic_access(struct iwl_trans *trans) +void iwl_trans_pcie_resched_with_nic_access(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); From 60ede4709783ee84ec5514886e690c846c74c43b Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 27 May 2026 23:04:59 +0300 Subject: [PATCH 155/285] wifi: iwlwifi: remove stale comment iwl_pcie_set_hw_ready still returns the return value of iwl_poll_bits, but the latter one no longer returns the time elapsed until success, now it returns either success or failure. Remove the comment entirely. Link: https://patch.msgid.link/20260527230313.ae42da7924ec.I1a92266621dc0033afa80f022d4c45e91674fedb@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 40fd5f3fdeb7..976a47ef6d0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -521,7 +521,6 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans) #define HW_READY_TIMEOUT (50) -/* Note: returns poll_bit return value, which is >= 0 if success */ static int iwl_pcie_set_hw_ready(struct iwl_trans *trans) { int ret; From 8c1104b006414a404d32000191fb31917c965c98 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 27 May 2026 23:05:00 +0300 Subject: [PATCH 156/285] wifi: iwlwifi: remove mvm prefix from marker command This command is sent in other opmodes as well. Remove the mvm prefix. Link: https://patch.msgid.link/20260527230313.290e4d9db14a.Ia4edc64dacc8e298ab7817ab5c37843e92698b8d@changeid Signed-off-by: Miri Korenblit --- .../net/wireless/intel/iwlwifi/fw/api/commands.h | 4 ++-- .../net/wireless/intel/iwlwifi/fw/api/debug.h | 16 ++++++++-------- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index abd259350589..f76f76e54c4e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -466,8 +466,8 @@ enum iwl_legacy_cmds { MCC_CHUB_UPDATE_CMD = 0xc9, /** - * @MARKER_CMD: trace marker command, uses &struct iwl_mvm_marker - * with &struct iwl_mvm_marker_rsp + * @MARKER_CMD: trace marker command, uses &struct iwl_marker + * with &struct iwl_marker_rsp */ MARKER_CMD = 0xcb, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index 61a850de26fc..382d1c2581ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2025 Intel Corporation + * Copyright (C) 2005-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -262,20 +262,20 @@ struct iwl_mfu_assert_dump_notif { } __packed; /* MFU_DUMP_ASSERT_API_S_VER_1 */ /** - * enum iwl_mvm_marker_id - marker ids + * enum iwl_marker_id - marker ids * * The ids for different type of markers to insert into the usniffer logs * * @MARKER_ID_TX_FRAME_LATENCY: TX latency marker * @MARKER_ID_SYNC_CLOCK: sync FW time and systime */ -enum iwl_mvm_marker_id { +enum iwl_marker_id { MARKER_ID_TX_FRAME_LATENCY = 1, MARKER_ID_SYNC_CLOCK = 2, }; /* MARKER_ID_API_E_VER_2 */ /** - * struct iwl_mvm_marker - mark info into the usniffer logs + * struct iwl_marker - mark info into the usniffer logs * * (MARKER_CMD = 0xcb) * @@ -284,12 +284,12 @@ enum iwl_mvm_marker_id { * In the command response the ucode will return the GP2 time. * * @dw_len: The amount of dwords following this byte including this byte. - * @marker_id: A unique marker id (iwl_mvm_marker_id). + * @marker_id: A unique marker id (iwl_marker_id). * @reserved: reserved. * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC * @metadata: additional meta data that will be written to the unsiffer log */ -struct iwl_mvm_marker { +struct iwl_marker { u8 dw_len; u8 marker_id; __le16 reserved; @@ -298,11 +298,11 @@ struct iwl_mvm_marker { } __packed; /* MARKER_API_S_VER_1 */ /** - * struct iwl_mvm_marker_rsp - Response to marker cmd + * struct iwl_marker_rsp - Response to marker cmd * * @gp2: The gp2 clock value in the FW */ -struct iwl_mvm_marker_rsp { +struct iwl_marker_rsp { __le32 gp2; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 337e312c2f43..6621ef9358bc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -2383,8 +2383,8 @@ static int iwl_fw_dbg_restart_recording(struct iwl_trans *trans, int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt) { - struct iwl_mvm_marker marker = { - .dw_len = sizeof(struct iwl_mvm_marker) / 4, + struct iwl_marker marker = { + .dw_len = sizeof(struct iwl_marker) / 4, .marker_id = MARKER_ID_SYNC_CLOCK, }; struct iwl_host_cmd hcmd = { @@ -2392,7 +2392,7 @@ int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt) .id = WIDE_ID(LONG_GROUP, MARKER_CMD), .dataflags = {}, }; - struct iwl_mvm_marker_rsp *resp; + struct iwl_marker_rsp *resp; int cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, WIDE_ID(LONG_GROUP, MARKER_CMD), IWL_FW_CMD_VER_UNKNOWN); From be443355dafcd4ab40f59b399868b04cc1e9da78 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 27 May 2026 23:05:01 +0300 Subject: [PATCH 157/285] wifi: iwlwifi: mld: fix smatch warning We dereference the mld_sta pointer before checking for NULL. But we do check the sta pointer, and sta != NULL means mld_sta != NULL, so there is no real issue. Fix it anyway to silence the warning. Link: https://patch.msgid.link/20260527200512.506707-2-miriam.rachel.korenblit@intel.com Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/tx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c index 1e8716cbb4ce..2185dade95f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -563,7 +563,8 @@ iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb, * - when no sta is given. * - frames that are sent to an NMI sta, which is only used for management. */ - if (unlikely(!sta || mld_sta->vif->type == NL80211_IFTYPE_NAN || + if (unlikely(!sta || + (mld_sta && mld_sta->vif->type == NL80211_IFTYPE_NAN) || info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) { flags |= IWL_TX_FLAGS_CMD_RATE; rate_n_flags = iwl_mld_get_tx_rate_n_flags(mld, info, sta, From f60f2159219fda21e5fde5a6a9aa94edb14d76a0 Mon Sep 17 00:00:00 2001 From: Moriya Itzchaki Date: Wed, 27 May 2026 23:05:02 +0300 Subject: [PATCH 158/285] wifi: iwlwifi: fix STEP_URM register address for SC devices The CNVI_PMU_STEP_FLOW register address differs between device families. For SC and newer devices, the register is at 0xA2D688, while for BZ devices it's at 0xA2D588. Signed-off-by: Moriya Itzchaki Link: https://patch.msgid.link/20260527230313.f0c115c4f74e.I3c66b2e39a97f754e853ac7e7dba8e433523619e@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 3 ++- .../wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c | 12 ++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 6ca1f51b69a1..50acfe0d4f79 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -385,7 +385,8 @@ enum { #define CNVI_SCU_REG_FOR_ECO_1_WIAMT_KNOWN BIT(4) #define CNVI_SCU_REG_FOR_ECO_1_WIAMT_PRESENT BIT(5) -#define CNVI_PMU_STEP_FLOW 0xA2D588 +#define CNVI_PMU_STEP_FLOW_BZ 0xA2D588 +#define CNVI_PMU_STEP_FLOW_SC 0xA2D688 #define CNVI_PMU_STEP_FLOW_FORCE_URM BIT(2) #define PREG_AUX_BUS_WPROT_0 0xA04CC0 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c index 0e324aeb9055..f41cbe62e7aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c @@ -376,6 +376,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 step_reg; iwl_pcie_reset_ict(trans); @@ -404,10 +405,13 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans) iwl_pcie_get_rf_name(trans); mutex_unlock(&trans_pcie->mutex); - if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) - trans->step_urm = !!(iwl_read_prph(trans, - CNVI_PMU_STEP_FLOW) & - CNVI_PMU_STEP_FLOW_FORCE_URM); + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + return; + + step_reg = trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC ? + CNVI_PMU_STEP_FLOW_SC : CNVI_PMU_STEP_FLOW_BZ; + trans->step_urm = !!(iwl_read_prph(trans, step_reg) & + CNVI_PMU_STEP_FLOW_FORCE_URM); } static bool iwl_pcie_set_ltr(struct iwl_trans *trans) From 18928cd2b08e4efa0e09597710c2e7b768bab864 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 May 2026 23:05:03 +0300 Subject: [PATCH 159/285] wifi: iwlwifi: mvm: rename iwl_mvm_mac80211_idx_to_hwrate() Given that we now use v3 rates with FW index throughout, _to_hwrate() is confusing, since the hardware still uses the PLCP value, the driver just doesn't see that now (as it talks to firmware, not hardware.) Rename this to iwl_mvm_rate_idx_to_fw_idx() to more clearly indicate what it's doing. Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260527230313.a60c8aea5b6c.I6af48d5d9748e184eed9d3437d312291cab61d7f@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 10 +++++----- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 8ffa72aca3cf..3e5084d10a60 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -935,7 +935,7 @@ u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx) : IWL_MAC_BEACON_CCK_V1; if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8) - flags |= iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx); + flags |= iwl_mvm_rate_idx_to_fw_idx(fw, rate_idx); else flags |= iwl_fw_rate_idx_to_plcp(rate_idx); @@ -998,7 +998,7 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, else rate_n_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM; - rate_n_flags |= iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate); + rate_n_flags |= iwl_mvm_rate_idx_to_fw_idx(mvm->fw, rate); tx_params->rate_n_flags = iwl_mvm_v3_rate_to_fw(rate_n_flags, mvm->fw_rates_ver); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index be89b84204fb..393acf5c5f55 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1653,7 +1653,7 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, enum nl80211_band band, struct ieee80211_tx_rate *r); -u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx); +u8 iwl_mvm_rate_idx_to_fw_idx(const struct iwl_fw *fw, int rate_idx); u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac); bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index bca13417e82c..dc69c71faa76 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -268,7 +268,6 @@ static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm, int rate_idx) { u32 rate_flags = 0; - u8 rate_plcp; bool is_cck; /* if the rate isn't a well known legacy rate, take the lowest one */ @@ -277,8 +276,9 @@ static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm, info, info->control.vif); - /* Get PLCP rate for tx_cmd->rate_n_flags */ - rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate_idx); + /* Get FW rate for tx_cmd->rate_n_flags */ + rate_flags |= iwl_mvm_rate_idx_to_fw_idx(mvm->fw, rate_idx); + is_cck = (rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE); @@ -288,7 +288,7 @@ static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm, else rate_flags |= RATE_MCS_MOD_TYPE_CCK; - return (u32)rate_plcp | rate_flags; + return rate_flags; } static u32 iwl_mvm_get_inject_tx_rate(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index f052537e9567..8aba9768afcf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -157,7 +157,7 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, return -1; } -u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx) +u8 iwl_mvm_rate_idx_to_fw_idx(const struct iwl_fw *fw, int rate_idx) { return rate_idx >= IWL_FIRST_OFDM_RATE ? rate_idx - IWL_FIRST_OFDM_RATE : From 3ea44b983afde21500b10672d2107b942c567f9b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 May 2026 23:05:04 +0300 Subject: [PATCH 160/285] wifi: iwlwifi: move iwl_fw_rate_idx_to_plcp() to mvm It's only needed by mvm, so there's no need to have it in iwlwifi and export it, just move it to mvm itself. Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260527230313.87769f13c7d7.I3875d768694b9484317a3253f479a2a2100244f4@changeid Signed-off-by: Miri Korenblit --- .../net/wireless/intel/iwlwifi/fw/api/rs.h | 3 +- drivers/net/wireless/intel/iwlwifi/fw/rs.c | 27 ----------------- .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + .../net/wireless/intel/iwlwifi/mvm/utils.c | 30 +++++++++++++++++-- 5 files changed, 30 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index ae6be3ed32f8..f90e743caa7e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2022, 2024-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022, 2024-2026 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_rs_h__ @@ -889,7 +889,6 @@ struct iwl_lq_cmd { __le32 ss_params; }; /* LINK_QUALITY_CMD_API_S_VER_1 */ -u8 iwl_fw_rate_idx_to_plcp(int idx); const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx); const char *iwl_rs_pretty_ant(u8 ant); const char *iwl_rs_pretty_bw(int bw); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c index 2aa300b26158..ccdd6856572b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c @@ -8,27 +8,6 @@ #include "iwl-drv.h" #include "iwl-config.h" -#define IWL_DECLARE_RATE_INFO(r) \ - [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP - -/* - * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP - * */ -static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1), - IWL_DECLARE_RATE_INFO(2), - IWL_DECLARE_RATE_INFO(5), - IWL_DECLARE_RATE_INFO(11), - IWL_DECLARE_RATE_INFO(6), - IWL_DECLARE_RATE_INFO(9), - IWL_DECLARE_RATE_INFO(12), - IWL_DECLARE_RATE_INFO(18), - IWL_DECLARE_RATE_INFO(24), - IWL_DECLARE_RATE_INFO(36), - IWL_DECLARE_RATE_INFO(48), - IWL_DECLARE_RATE_INFO(54), -}; - /* mbps, mcs */ static const struct iwl_rate_mcs_info rate_mcs[IWL_RATE_COUNT] = { { "1", "BPSK DSSS"}, @@ -61,12 +40,6 @@ static const char * const pretty_bw[] = { "320Mhz", }; -u8 iwl_fw_rate_idx_to_plcp(int idx) -{ - return fw_rate_idx_to_plcp[idx]; -} -IWL_EXPORT_SYMBOL(iwl_fw_rate_idx_to_plcp); - const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx) { return &rate_mcs[idx]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 3e5084d10a60..d6a8624b1ae5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -937,7 +937,7 @@ u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx) if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8) flags |= iwl_mvm_rate_idx_to_fw_idx(fw, rate_idx); else - flags |= iwl_fw_rate_idx_to_plcp(rate_idx); + flags |= iwl_mvm_rate_idx_to_plcp(rate_idx); return flags; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 393acf5c5f55..e09b63516230 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1646,6 +1646,7 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm); int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm); /* Utils */ +u8 iwl_mvm_rate_idx_to_plcp(int idx); int iwl_mvm_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band); int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 8aba9768afcf..2e12f93ad32b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -14,6 +14,30 @@ #include "fw/api/rs.h" #include "fw/img.h" +#define IWL_DECLARE_RATE_INFO(r) \ + [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP + +/* Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP */ +static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { + IWL_DECLARE_RATE_INFO(1), + IWL_DECLARE_RATE_INFO(2), + IWL_DECLARE_RATE_INFO(5), + IWL_DECLARE_RATE_INFO(11), + IWL_DECLARE_RATE_INFO(6), + IWL_DECLARE_RATE_INFO(9), + IWL_DECLARE_RATE_INFO(12), + IWL_DECLARE_RATE_INFO(18), + IWL_DECLARE_RATE_INFO(24), + IWL_DECLARE_RATE_INFO(36), + IWL_DECLARE_RATE_INFO(48), + IWL_DECLARE_RATE_INFO(54), +}; + +u8 iwl_mvm_rate_idx_to_plcp(int idx) +{ + return fw_rate_idx_to_plcp[idx]; +} + /* * Will return 0 even if the cmd failed when RFKILL is asserted unless * CMD_WANT_SKB is set in cmd->flags. @@ -151,7 +175,7 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, if (band != NL80211_BAND_2GHZ) band_offset = IWL_FIRST_OFDM_RATE; for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) - if (iwl_fw_rate_idx_to_plcp(idx) == rate) + if (iwl_mvm_rate_idx_to_plcp(idx) == rate) return idx - band_offset; return -1; @@ -1243,7 +1267,7 @@ static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags) int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE; for (idx = offset; idx < last; idx++) - if (iwl_fw_rate_idx_to_plcp(idx) == rate) + if (iwl_mvm_rate_idx_to_plcp(idx) == rate) return idx - offset; return IWL_RATE_INVALID; } @@ -1353,7 +1377,7 @@ __le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver) rate_idx = u32_get_bits(rate, RATE_LEGACY_RATE_MSK); if (!(result & RATE_MCS_CCK_MSK_V1)) rate_idx += IWL_FIRST_OFDM_RATE; - result |= u32_encode_bits(iwl_fw_rate_idx_to_plcp(rate_idx), + result |= u32_encode_bits(iwl_mvm_rate_idx_to_plcp(rate_idx), RATE_LEGACY_RATE_MSK_V1); break; case RATE_MCS_MOD_TYPE_HT: From 44e9ece99e5b9e4480ecdb03bf790422b9d0ef6b Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Wed, 27 May 2026 23:05:05 +0300 Subject: [PATCH 161/285] wifi: iwlwifi: mld: always allow mimo in NAN The mimo field of the sta command is badly named. It really carries the initial SMPS value as it is in the association request of the client station (when we are the AP). In NAN we don't have this information, just mark SMPS as disabled. Link: https://patch.msgid.link/20260527230313.abd136be474e.I9eb663d953b482236345ffbcb611f28facea83c1@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/sta.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index 77eeeed66116..e18d86f021dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -538,6 +538,12 @@ int iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, break; } + /* In NAN, there is no association request so no initial SMPS info */ + if (mld_sta->vif->type == NL80211_IFTYPE_NAN_DATA) { + cmd.mimo = cpu_to_le32(1); + cmd.mimo_protection = cpu_to_le32(0); + } + iwl_mld_fill_ampdu_size_and_dens(link_sta, is_6ghz, &cmd.tx_ampdu_max_size, &cmd.tx_ampdu_spacing); From 13676ae3cf90e2b98651920b982fbd62e9490bcf Mon Sep 17 00:00:00 2001 From: Israel Kozitz Date: Wed, 27 May 2026 23:05:06 +0300 Subject: [PATCH 162/285] wifi: iwlwifi: mld: support FW TLV for NAN max channel switch time Add a new FW TLV (IWL_UCODE_TLV_FW_NAN_MAX_CHAN_SWITCH_TIME) that allows the firmware to specify the NAN maximum channel switch time in microseconds. When the TLV is present, use its value for the NAN device capability. Otherwise, fall back to the default of 4 milliseconds. Signed-off-by: Israel Kozitz Link: https://patch.msgid.link/20260527230313.e8ae1a3adacd.I15b933407ca3974a65047b63b4f9b00bed3520fb@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/file.h | 1 + drivers/net/wireless/intel/iwlwifi/fw/img.h | 1 + drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 6 ++++++ drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 9 +++++++-- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 197c88c25f72..a26ed82a8106 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -112,6 +112,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_FW_NUM_LINKS = IWL_UCODE_TLV_CONST_BASE + 1, IWL_UCODE_TLV_FW_NUM_BEACONS = IWL_UCODE_TLV_CONST_BASE + 2, IWL_UCODE_TLV_FW_NUM_MCAST_KEY_ENTRIES = IWL_UCODE_TLV_CONST_BASE + 3, + IWL_UCODE_TLV_FW_NAN_MAX_CHAN_SWITCH_TIME = IWL_UCODE_TLV_CONST_BASE + 4, IWL_UCODE_TLV_TYPE_DEBUG_INFO = IWL_UCODE_TLV_DEBUG_BASE + 0, IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_TLV_DEBUG_BASE + 1, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index 75b1344f6cbe..95b45a253641 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -54,6 +54,7 @@ struct iwl_ucode_capabilities { u32 num_links; u32 num_beacons; u32 num_mcast_key_entries; + u16 nan_max_chan_switch_time; DECLARE_BITMAP(_api, NUM_IWL_UCODE_TLV_API); DECLARE_BITMAP(_capa, NUM_IWL_UCODE_TLV_CAPA); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 488524529538..842586d4fc5c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1337,6 +1337,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, capa->num_mcast_key_entries = le32_to_cpup((const __le32 *)tlv_data); break; + case IWL_UCODE_TLV_FW_NAN_MAX_CHAN_SWITCH_TIME: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + capa->nan_max_chan_switch_time = + le32_to_cpup((const __le32 *)tlv_data); + break; case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: { const struct iwl_umac_debug_addrs *dbg_ptrs = (const void *)tlv_data; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 92858b8f7395..56e0c19e0f81 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -291,8 +291,13 @@ static void iwl_mld_hw_set_nan(struct iwl_mld *mld) NAN_DEV_CAPA_NUM_RX_ANT_POS) & NAN_DEV_CAPA_NUM_RX_ANT_MASK); - /* Maximal channel switch time is 4 msec */ - hw->wiphy->nan_capa.max_channel_switch_time = 4 * USEC_PER_MSEC; + /* Maximal channel switch time - use FW TLV value if available */ + if (mld->fw->ucode_capa.nan_max_chan_switch_time) + hw->wiphy->nan_capa.max_channel_switch_time = + mld->fw->ucode_capa.nan_max_chan_switch_time; + else + hw->wiphy->nan_capa.max_channel_switch_time = + 4 * USEC_PER_MSEC; hw->wiphy->nan_capa.phy.ht = mld->nvm_data->nan_phy_capa.ht; hw->wiphy->nan_capa.phy.vht = mld->nvm_data->nan_phy_capa.vht; From a9505106c9effe53de91c6c5a28c7d24b32acdfb Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Wed, 27 May 2026 23:05:07 +0300 Subject: [PATCH 163/285] wifi: iwlwifi: mld: set fast-balance scan for active EMLSR While associated to MLD AP with active EMLSR, set all scan operations as fast-balance scans. The only exception is when a fragmented scan is planned (high traffic or low latency), in which case the fragmented scan is preserved. Signed-off-by: Pagadala Yesu Anjaneyulu Link: https://patch.msgid.link/20260527230313.32d278842b0e.Ia3d73e4085eefc4d3921e93de4107b2d6a6f922e@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/scan.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c index b3836423e53e..d80a1cfc2ed5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -237,6 +237,12 @@ iwl_mld_scan_type iwl_mld_get_scan_type(struct iwl_mld *mld, vif->type != NL80211_IFTYPE_P2P_DEVICE) return IWL_SCAN_TYPE_FRAGMENTED; + /* While associated to MLD AP with active EMLSR, set all scan + * operations as fast-balance scans. + */ + if (iwl_mld_emlsr_active(vif)) + return IWL_SCAN_TYPE_FAST_BALANCE; + /* In case of DCM with P2P GO set all scan requests as * fast-balance scan */ From 7ebdca63eca82394f546a2274c460956b356cea3 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Wed, 27 May 2026 23:05:08 +0300 Subject: [PATCH 164/285] wifi: iwlwifi: cfg: Revert "wifi: iwlwifi: cfg: move the MODULE_FIRMWARE to the per-rf file" IWL_BZ_UCODE_CORE_MAX is undefined in cfg/rf-fm.c, this causes __stringify(core) to turn it into the literal token text, so MODULE_FIRMWARE entries are generated as "iwlwifi...-cIWL_BZ_UCODE_CORE_MAX.ucode", instead of the actual number. This reverts the commit below. Signed-off-by: Shahar Tzarfati Link: https://patch.msgid.link/20260527230313.a10bc3359dca.I446a1340c635f07aff3efaba5317635e010c156f@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 12 ++++++++++++ drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index ecb4f81a99f5..9cdc4f142c7f 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -15,6 +15,12 @@ /* Lowest firmware core release supported */ #define IWL_BZ_UCODE_CORE_MIN 101 +#define IWL_BZ_A_FM_B_FW_PRE "iwlwifi-bz-a0-fm-b0" +#define IWL_BZ_A_FM_C_FW_PRE "iwlwifi-bz-a0-fm-c0" +#define IWL_BZ_A_FM4_B_FW_PRE "iwlwifi-bz-a0-fm4-b0" +#define IWL_GL_B_FM_B_FW_PRE "iwlwifi-gl-b0-fm-b0" +#define IWL_GL_C_FM_C_FW_PRE "iwlwifi-gl-c0-fm-c0" + static const struct iwl_family_base_params iwl_bz_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, @@ -85,3 +91,9 @@ const struct iwl_mac_cfg iwl_gl_mac_cfg = { .xtal_latency = 12000, .low_latency_xtal = true, }; + +IWL_CORE_FW(IWL_BZ_A_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); +IWL_CORE_FW(IWL_BZ_A_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX); +IWL_CORE_FW(IWL_BZ_A_FM4_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); +IWL_CORE_FW(IWL_GL_B_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); +IWL_CORE_FW(IWL_GL_C_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c index 294cf25ae2a6..35b1618f3474 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c @@ -5,12 +5,6 @@ */ #include "iwl-config.h" -#define IWL_BZ_A_FM_B_FW_PRE "iwlwifi-bz-a0-fm-b0" -#define IWL_BZ_A_FM_C_FW_PRE "iwlwifi-bz-a0-fm-c0" -#define IWL_BZ_A_FM4_B_FW_PRE "iwlwifi-bz-a0-fm4-b0" -#define IWL_GL_B_FM_B_FW_PRE "iwlwifi-gl-b0-fm-b0" -#define IWL_GL_C_FM_C_FW_PRE "iwlwifi-gl-c0-fm-c0" - #define IWL_DEVICE_FM \ .ht_params = { \ .stbc = true, \ @@ -54,9 +48,3 @@ const char iwl_be201_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz"; const char iwl_be200_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz"; const char iwl_be202_name[] = "Intel(R) Wi-Fi 7 BE202 160MHz"; const char iwl_be401_name[] = "Intel(R) Wi-Fi 7 BE401 320MHz"; - -IWL_CORE_FW(IWL_BZ_A_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); -IWL_CORE_FW(IWL_BZ_A_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX); -IWL_CORE_FW(IWL_BZ_A_FM4_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); -IWL_CORE_FW(IWL_GL_B_FM_B_FW_PRE, IWL_BZ_UCODE_CORE_MAX); -IWL_CORE_FW(IWL_GL_C_FM_C_FW_PRE, IWL_BZ_UCODE_CORE_MAX); From 301b0dfa9db3f1e204de95e803bbd88fbd878c7c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 May 2026 23:05:09 +0300 Subject: [PATCH 165/285] wifi: iwlwifi: mld: don't WARN on WoWLAN suspend w/o netdetect Clearly, from a user perspective, it must be valid to configure WoWLAN and then suspend while not connected to a network. Since mac80211 doesn't distinguish these cases and simply calls the driver to suspend whenever WoWLAN is configured, the driver has to cleanly handle the case where it's called for WoWLAN, it's not connected but there's also no netdetect configured. Remove the WARN_ON() and keep returning 1 to disconnect and then suspend. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260527230313.19720967372b.Iff30814510a26f9f609f98eeea3111c50c1afb31@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/d3.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c index fc0a5871df2f..458a668ba916 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -2066,8 +2066,11 @@ int iwl_mld_wowlan_suspend(struct iwl_mld *mld, struct cfg80211_wowlan *wowlan) if (!bss_vif->cfg.assoc) { int ret; - /* If we're not associated, this must be netdetect */ - if (WARN_ON(!wowlan->nd_config)) + /* + * If not associated we can only do netdetect, if + * that's not enabled then just suspend normally. + */ + if (!wowlan->nd_config) return 1; ret = iwl_mld_netdetect_config(mld, bss_vif, wowlan); From fa6758a86e37b4572f33763d8cb991b1d98ea857 Mon Sep 17 00:00:00 2001 From: Avinash Bhatt Date: Wed, 27 May 2026 23:05:10 +0300 Subject: [PATCH 166/285] wifi: iwlwifi: mld: add KUnit tests for duplicated beacon RSSI adjustment Add KUnit tests to verify RSSI adjustment for 6 GHz duplicated beacons across different operational bandwidths and validate detection of the duplicated beacon bit. Signed-off-by: Avinash Bhatt Link: https://patch.msgid.link/20260527230313.a3500c44f5e8.Icba6ee1158e9f563a91b482b8cdd3f51ddace468@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 3 +- drivers/net/wireless/intel/iwlwifi/mld/link.h | 5 + .../wireless/intel/iwlwifi/mld/tests/link.c | 102 +++++++++++++++++- .../wireless/intel/iwlwifi/mld/tests/utils.c | 60 +++++++++++ .../wireless/intel/iwlwifi/mld/tests/utils.h | 7 +- 5 files changed, 173 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 234821f6a441..98b9c4eef583 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -1024,7 +1024,7 @@ iwl_mld_get_avail_chan_load(struct iwl_mld *mld, return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf); } -static s8 +VISIBLE_IF_IWLWIFI_KUNIT s8 iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf) { @@ -1077,6 +1077,7 @@ iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld, return 0; } } +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_dup_beacon_rssi_adjust); static s8 iwl_mld_get_primary_psd(const struct ieee80211_parsed_tpe_psd *psd, const struct cfg80211_chan_def *chandef, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index f1997e280058..d0aa577de81d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -142,6 +142,11 @@ int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif, unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf); +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +s8 iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf); +#endif + unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c index 69a0d67858bf..21bcc341cd7d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * KUnit tests for channel helper functions + * KUnit tests for link helper functions * - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -96,8 +96,106 @@ static void test_missed_beacon(struct kunit *test) /* TODO: add test cases for esr and check */ } +struct dup_beacon_test_case { + const char *desc; + enum nl80211_chan_width bandwidth; + bool has_he_oper; + bool dup_beacon_bit; + s8 expected_adj; +}; + +static const struct dup_beacon_test_case dup_beacon_cases[] = { + { + .desc = "20 MHz - no duplication", + .bandwidth = NL80211_CHAN_WIDTH_20, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 0, + }, + { + .desc = "40 MHz with duplication - 3 dB", + .bandwidth = NL80211_CHAN_WIDTH_40, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 3, + }, + { + .desc = "80 MHz with duplication - 6 dB", + .bandwidth = NL80211_CHAN_WIDTH_80, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 6, + }, + { + .desc = "160 MHz with duplication - 9 dB", + .bandwidth = NL80211_CHAN_WIDTH_160, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 9, + }, + { + .desc = "320 MHz with duplication - 12 dB", + .bandwidth = NL80211_CHAN_WIDTH_320, + .has_he_oper = true, + .dup_beacon_bit = true, + .expected_adj = 12, + }, + { + .desc = "80 MHz without dup bit - no adjustment", + .bandwidth = NL80211_CHAN_WIDTH_80, + .has_he_oper = true, + .dup_beacon_bit = false, + .expected_adj = 0, + }, + { + .desc = "80 MHz without HE oper - no adjustment", + .bandwidth = NL80211_CHAN_WIDTH_80, + .has_he_oper = false, + .dup_beacon_bit = true, + .expected_adj = 0, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_dup_beacon_rssi_adjust, dup_beacon_cases, desc); + +static void test_dup_beacon_rssi_adjust(struct kunit *test) +{ + const struct dup_beacon_test_case *params = test->param_value; + struct iwl_mld *mld = test->priv; + struct ieee80211_bss_conf *link_conf; + struct cfg80211_bss *bss; + struct element *he_elem = NULL; + s8 result; + + KUNIT_ALLOC_AND_ASSERT(test, link_conf); + KUNIT_ALLOC_AND_ASSERT(test, bss); + link_conf->bss = bss; + + link_conf->chanreq.oper.chan = &chan_6ghz; + link_conf->chanreq.oper.width = params->bandwidth; + + if (params->has_he_oper) { + struct ieee80211_he_6ghz_oper he_6ghz = {}; + + if (params->dup_beacon_bit) + he_6ghz.control = + IEEE80211_HE_6GHZ_OPER_CTRL_DUP_BEACON; + he_elem = iwlmld_kunit_create_he_6ghz_oper(he_6ghz); + } + + rcu_assign_pointer(bss->beacon_ies, + iwlmld_kunit_create_bss_ies(he_elem)); + + guard(wiphy)(mld->wiphy); + result = iwl_mld_get_dup_beacon_rssi_adjust(mld, link_conf); + + KUNIT_EXPECT_EQ(test, result, params->expected_adj); +} + static struct kunit_case link_cases[] = { KUNIT_CASE_PARAM(test_missed_beacon, test_missed_beacon_gen_params), + KUNIT_CASE_PARAM(test_dup_beacon_rssi_adjust, + test_dup_beacon_rssi_adjust_gen_params), {}, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c index cb1968b07452..fdeab7082e78 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -439,6 +439,66 @@ struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, return vif; } +struct element * +iwlmld_kunit_create_he_6ghz_oper(struct ieee80211_he_6ghz_oper he_6ghz) +{ + struct kunit *test = kunit_get_current_test(); + u8 *data; + size_t data_len; + size_t offset = 0; + __le32 he_oper_params; + __le16 he_mcs_nss_set = 0; + + /* Build HE Operation IE with 6 GHz info using raw buffer. + * Cannot use struct embedding because ieee80211_he_operation + * has a flexible array member (optional[]). + * + * Layout: + * 1 byte: ext_id (WLAN_EID_EXT_HE_OPERATION) + * he_oper_params: he_oper_params (from ieee80211_he_operation) + * he_mcs_nss_set: he_mcs_nss_set (from ieee80211_he_operation) + * he_6ghz: ieee80211_he_6ghz_oper (goes into optional[]) + */ + data_len = 1 + sizeof(he_oper_params) + sizeof(he_mcs_nss_set) + + sizeof(struct ieee80211_he_6ghz_oper); + + data = kunit_kzalloc(test, data_len, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, data); + + data[offset++] = WLAN_EID_EXT_HE_OPERATION; + + he_oper_params = cpu_to_le32(IEEE80211_HE_OPERATION_6GHZ_OP_INFO); + memcpy(&data[offset], &he_oper_params, sizeof(he_oper_params)); + offset += sizeof(he_oper_params); + + memcpy(&data[offset], &he_mcs_nss_set, sizeof(he_mcs_nss_set)); + offset += sizeof(he_mcs_nss_set); + + memcpy(&data[offset], &he_6ghz, sizeof(he_6ghz)); + + return iwlmld_kunit_gen_element(WLAN_EID_EXTENSION, data, data_len); +} + +struct cfg80211_bss_ies *iwlmld_kunit_create_bss_ies(struct element *elem) +{ + struct kunit *test = kunit_get_current_test(); + struct cfg80211_bss_ies *ies; + size_t ies_len = 0; + + if (elem) + ies_len = sizeof(*elem) + elem->datalen; + + ies = kunit_kzalloc(test, sizeof(*ies) + ies_len, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ies); + + ies->len = ies_len; + + if (elem) + memcpy(ies->data, elem, ies_len); + + return ies; +} + struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len) { struct kunit *test = kunit_get_current_test(); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h index edf8eef4e81a..cfed5acaac3a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_kunit_utils_h__ @@ -121,6 +121,11 @@ iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len); +struct element * +iwlmld_kunit_create_he_6ghz_oper(struct ieee80211_he_6ghz_oper he_6ghz); + +struct cfg80211_bss_ies *iwlmld_kunit_create_bss_ies(struct element *elem); + /** * iwlmld_kunit_get_phy_of_link - Get the phy of a link * From b74e377cad9271950c57472867c469e4b5b2ff0c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 May 2026 23:05:11 +0300 Subject: [PATCH 167/285] wifi: iwlwifi: mvm: fix P2P-Device binding handling Our binding handling for P2P-Device can run into the following scenario, as observed by our testing: - a station interface is connected on some channel - the P2P-Device does a remain-on-channel (ROC) on that channel - the ROC ends, and the P2P-Device is removed from the binding, but the phy_ctxt pointer is left around as a PHY cache so we don't need to recalibrate to the channel again and again in case it's not shared - a binding update by the station interface, even a removal, will re-add the P2P-Device to the binding - the P2P-Device is removed, which removes the PHY context, but it's still in the binding so the firmware crashes Since the P2P device is removed from the binding and only re- added by unrelated code, but we want to keep the phy_ctxt around as a cache for future ROC usage, fix it by adding a boolean that indicates whether or not the P2P-Device should be added to the binding, and handle that in the binding iterator. That way, the station interface cannot re-add the P2P-Device to the binding when that isn't active. Assisted-by: Github Copilot:claude-opus-4-6 Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260527230313.07f94335ae06.I384238b0859343c4a9a9dda20682be1aad89cc9d@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/binding.c | 5 ++++- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 12 +++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 3 ++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c index 58e9a940024d..0812522edea0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2012-2014, 2020 Intel Corporation * Copyright (C) 2016 Intel Deutschland GmbH - * Copyright (C) 2022, 2024 Intel Corporation + * Copyright (C) 2022, 2024, 2026 Intel Corporation */ #include #include "fw-api.h" @@ -76,6 +76,9 @@ static void iwl_mvm_iface_iterator(void *_data, u8 *mac, if (vif == data->ignore_vif) return; + if (vif->type == NL80211_IFTYPE_P2P_DEVICE && !mvmvif->p2p_in_binding) + return; + if (mvmvif->deflink.phy_ctxt != data->phyctxt) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index c256cbc6602e..74bd4038fd56 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1104,6 +1104,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, spin_unlock_bh(&mvm->time_event_lock); mvmvif->roc_activity = ROC_NUM_ACTIVITIES; + mvmvif->p2p_in_binding = false; mvmvif->bf_enabled = false; mvmvif->ba_enabled = false; @@ -4634,6 +4635,7 @@ static int iwl_mvm_add_aux_sta_for_hs20(struct iwl_mvm *mvm, u32 lmac_id) static int iwl_mvm_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; lockdep_assert_held(&mvm->mutex); @@ -4642,10 +4644,18 @@ static int iwl_mvm_roc_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (WARN(ret, "Failed binding P2P_DEVICE\n")) return ret; + mvmvif->p2p_in_binding = true; + /* The station and queue allocation must be done only after the binding * is done, as otherwise the FW might incorrectly configure its state. */ - return iwl_mvm_add_p2p_bcast_sta(mvm, vif); + ret = iwl_mvm_add_p2p_bcast_sta(mvm, vif); + if (ret) { + iwl_mvm_binding_remove_vif(mvm, vif); + mvmvif->p2p_in_binding = false; + } + + return ret; } static int iwl_mvm_roc(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index e09b63516230..6bf71092faa1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -383,6 +383,8 @@ struct iwl_mvm_vif_link_info { * @pm_enabled: indicates powersave is enabled * @roc_activity: currently running ROC activity for this vif (or * ROC_NUM_ACTIVITIES if no activity is running). + * @p2p_in_binding: indicates that this P2P-Device interface should be + * added to the binding, i.e. is running ROC right now * @session_prot_connection_loss: the connection was lost due to session * protection ending without receiving a beacon, so we need to now * protect the deauth separately @@ -492,6 +494,7 @@ struct iwl_mvm_vif { struct iwl_mvm_time_event_data time_event_data; struct iwl_mvm_time_event_data hs_time_event_data; enum iwl_roc_activity roc_activity; + bool p2p_in_binding; /* TCP Checksum Offload */ netdev_features_t features; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 2b52a4f3bff9..1692b6e75f57 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH */ @@ -88,6 +88,7 @@ static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm) } else { iwl_mvm_rm_p2p_bcast_sta(mvm, vif); iwl_mvm_binding_remove_vif(mvm, vif); + mvmvif->p2p_in_binding = false; } /* Do not remove the PHY context as removing and adding From 33346563d6d2b860c0a11bf31dca925b48dd3db2 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 27 May 2026 23:05:12 +0300 Subject: [PATCH 168/285] wifi: iwlwifi: mld: Require HT support for NAN NAN cannot be supported if HT is not supported, so check that HT is supported before declaring that NAN is supported. Signed-off-by: Ilan Peer Link: https://patch.msgid.link/20260527230313.6274b222e849.If215f00f0cdb5eefb2507f8d0fb5734a65ce945f@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/nan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c index d34a9a2cbeae..dbe7a54a38b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c @@ -25,7 +25,8 @@ bool iwl_mld_nan_supported(struct iwl_mld *mld) iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, NAN_PEER_CMD), 0) >= 1 && iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), 0) >= 3 && iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), 0) >= 4 && - iwl_fw_lookup_cmd_ver(fw, WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD), 0) >= 6) + iwl_fw_lookup_cmd_ver(fw, WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD), 0) >= 6 && + mld->nvm_data->nan_phy_capa.ht.ht_supported) return true; return false; } From f255f1f2a05d2e2147a24f649755b2a0dd8aa1a7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 31 May 2026 13:53:00 +0300 Subject: [PATCH 169/285] wifi: iwlwifi: pcie: fix write pointer move detection Ever since the TFD queue size is no longer limited to 256 entries, this code has been wrong, and might erroneously not detect a move if it was by a multiple of 256. Not a big deal, but fix it while I see it. Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260531135036.87ffbeab298e.I4fae41383b6756bccbed250985e0521b68a40d0c@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 976a47ef6d0e..28b276c65952 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -2590,7 +2590,7 @@ int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) struct iwl_txq *txq; unsigned long now = jiffies; bool overflow_tx; - u8 wr_ptr; + int wr_ptr; /* Make sure the NIC is still alive in the bus */ if (test_bit(STATUS_TRANS_DEAD, &trans->status)) @@ -2613,7 +2613,7 @@ int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) overflow_tx) && !time_after(jiffies, now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { - u8 write_ptr = READ_ONCE(txq->write_ptr); + int write_ptr = READ_ONCE(txq->write_ptr); /* * If write pointer moved during the wait, warn only From ff124ad01dd3fedfda710b6961080a199e1f94aa Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Sun, 31 May 2026 13:53:01 +0300 Subject: [PATCH 170/285] wifi: iwlwifi: fix a typo We use 512 A-MSDUs in an A-MPDU, not 612. Fix the typo. Link: https://patch.msgid.link/20260531135036.62a394741a04.I2fd9e1d5dc4d467426c9061df2796ff8ba0129d4@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 933a96131d1a..6a3539ad7331 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -372,7 +372,7 @@ struct iwl_mac_cfg { }; /* - * These sizes were picked according to 8 MSDUs inside 64/256/612 A-MSDUs + * These sizes were picked according to 8 MSDUs inside 64/256/512 A-MSDUs * in an A-MPDU, with additional overhead to account for processing time. * They will be doubled for MACs starting from So/Ty that don't support * putting multiple frames into a single buffer. From a7bdb7804570f3ac93e368b51fecd8051a23fc18 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Sun, 31 May 2026 13:53:02 +0300 Subject: [PATCH 171/285] wifi: iwlwifi: remove orphaned DC2DC config enum FW core102 removed both DC2DC_CONFIG_CMD_API_S and DC2DC_CONFIG_CMD_RSP_API_S. The only driver-side artifact is enum iwl_dc2dc_config_id in fw/api/config.h, which has no callers in any .c file across all driver paths (mld/mvm/xvt). Remove the dead definition. Signed-off-by: Shahar Tzarfati Link: https://patch.msgid.link/20260531135036.487ceed62714.I13cf8cc214c68899379112e8e52f0cd38dc7b6f8@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/fw/api/config.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h index 1fc65469990e..84fa5ff7a57f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/config.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/config.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2012-2014, 2018-2019, 2023-2024 Intel Corporation + * Copyright (C) 2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -99,14 +100,4 @@ struct iwl_phy_cfg_cmd_v3 { struct iwl_phy_specific_cfg phy_specific_cfg; } __packed; /* PHY_CONFIGURATION_CMD_API_S_VER_3 */ -/* - * enum iwl_dc2dc_config_id - flag ids - * - * Ids of dc2dc configuration flags - */ -enum iwl_dc2dc_config_id { - DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */ - DCDC_FREQ_TUNE_SET = 0x2, -}; /* MARKER_ID_API_E_VER_1 */ - #endif /* __iwl_fw_api_config_h__ */ From 719d11f57a812d215d744bba464d3a0fae36fe44 Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Sun, 31 May 2026 13:53:03 +0300 Subject: [PATCH 172/285] wifi: iwlwifi: stop supporting core101 BZ, DR and SC no longer need to accept core101 firmware. Raise the minimum supported firmware core from 101 to 102 so these families only match supported core102 and newer images. Signed-off-by: Shahar Tzarfati Link: https://patch.msgid.link/20260531135036.4ece89be11a9.If00f9c7e011ec75219d28a38ca2077a926afc70e@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 9cdc4f142c7f..1542cbc3bb22 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -13,7 +13,7 @@ #define IWL_BZ_UCODE_CORE_MAX 105 /* Lowest firmware core release supported */ -#define IWL_BZ_UCODE_CORE_MIN 101 +#define IWL_BZ_UCODE_CORE_MIN 102 #define IWL_BZ_A_FM_B_FW_PRE "iwlwifi-bz-a0-fm-b0" #define IWL_BZ_A_FM_C_FW_PRE "iwlwifi-bz-a0-fm-c0" diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index e8968b3051d3..6a5870619411 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -12,7 +12,7 @@ #define IWL_DR_UCODE_CORE_MAX 105 /* Lowest firmware core release supported */ -#define IWL_DR_UCODE_CORE_MIN 101 +#define IWL_DR_UCODE_CORE_MIN 102 #define IWL_DR_A_PE_A_FW_PRE "iwlwifi-dr-a0-pe-a0" diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index 6aaa49aeec99..f8913293ccef 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -13,7 +13,7 @@ #define IWL_SC_UCODE_CORE_MAX 105 /* Lowest firmware core release supported */ -#define IWL_SC_UCODE_CORE_MIN 101 +#define IWL_SC_UCODE_CORE_MIN 102 #define IWL_SC_A_FM_B_FW_PRE "iwlwifi-sc-a0-fm-b0" #define IWL_SC_A_FM_C_FW_PRE "iwlwifi-sc-a0-fm-c0" From 4d30cc10e2b9df618b694aba503254eaa71d2c04 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Sun, 31 May 2026 13:53:04 +0300 Subject: [PATCH 173/285] wifi: iwlwifi: trans: export the maximum supported hcmd size Export the maximum allowed host command payload size to the op-modes. Note that this information was available to the op-modes also before this change, this just adds a clear macro. Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20260531135036.2e6b15bcaf50.I027e150e5f25ef2431ab4e212175dc00ca5e8abd@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 914864005704..c581cc8f5bcb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -161,6 +161,10 @@ struct iwl_device_tx_cmd { #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) +/* Maximum payload size for a non-NOCOPY host command (excluding the header) */ +#define IWL_MAX_CMD_PAYLOAD_SIZE \ + (TFD_MAX_PAYLOAD_SIZE - sizeof(struct iwl_cmd_header_wide)) + /* * number of transfer buffers (fragments) per transmit frame descriptor; * this is just the driver's idea, the hardware supports 20 From d947b22fd075a336be514ef428377bb757e0848c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Sun, 31 May 2026 13:53:05 +0300 Subject: [PATCH 174/285] wifi: iwlwifi: mvm: remove __must_check annotation from command sending We don't acually need to always check the return value. For example, if we send a command to remove an object - we can assume success (if it fails it is probably because the fw is dead, and then it doesn't have the object anyway). Remove the annotations. Link: https://patch.msgid.link/20260531135036.434473c7b29a.I455e0c3f93c25635df708da7d3216c183dbdbbbb@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 6bf71092faa1..683cac56822c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1673,16 +1673,16 @@ void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, int clock_type, u32 *gp2, u32 iwl_mvm_get_systime(struct iwl_mvm *mvm); /* Tx / Host Commands */ -int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, - struct iwl_host_cmd *cmd); -int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, - u32 flags, u16 len, const void *data); -int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, - struct iwl_host_cmd *cmd, - u32 *status); -int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, - u16 len, const void *data, - u32 *status); +int iwl_mvm_send_cmd(struct iwl_mvm *mvm, + struct iwl_host_cmd *cmd); +int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, + u32 flags, u16 len, const void *data); +int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, + struct iwl_host_cmd *cmd, + u32 *status); +int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, + u16 len, const void *data, + u32 *status); int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta); int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb); From 18bf30892cd9716947c41c629c7dc7a6355edadc Mon Sep 17 00:00:00 2001 From: Shahar Tzarfati Date: Sun, 31 May 2026 13:53:06 +0300 Subject: [PATCH 175/285] wifi: iwlwifi: mld: drop TLC config cmd v4/v5 compat code FW core102 bumped TLC_MNG_CONFIG_CMD_API_S from version 5 to version 6. The v4 and v5 compatibility paths in iwl_mld_send_tlc_cmd() are no longer reachable on any supported firmware. Signed-off-by: Shahar Tzarfati Link: https://patch.msgid.link/20260531135036.c0e2dbfd0569.I44f8eb4d985bb9590b65b77e9a3dd157e4bd5e79@changeid Signed-off-by: Miri Korenblit --- .../wireless/intel/iwlwifi/fw/api/datapath.h | 2 +- .../net/wireless/intel/iwlwifi/fw/api/rs.h | 31 -------- drivers/net/wireless/intel/iwlwifi/mld/tlc.c | 74 ------------------- 3 files changed, 1 insertion(+), 106 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index e494e5b18d22..c8eb016e1a7e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -57,7 +57,7 @@ enum iwl_data_path_subcmd_ids { /** * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4 or - * &struct iwl_tlc_config_cmd_v5 or &struct iwl_tlc_config_cmd. + * &struct iwl_tlc_config_cmd. */ TLC_MNG_CONFIG_CMD = 0xF, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index f90e743caa7e..f1254546cc9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -206,37 +206,6 @@ struct iwl_tlc_config_cmd_v4 { __le16 max_tx_op; } __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_4 */ -/** - * struct iwl_tlc_config_cmd_v5 - TLC configuration - * @sta_id: station id - * @reserved1: reserved - * @max_ch_width: max supported channel width from &enum iwl_tlc_mng_cfg_cw - * @mode: &enum iwl_tlc_mng_cfg_mode - * @chains: bitmask of &enum iwl_tlc_mng_cfg_chains - * @sgi_ch_width_supp: bitmap of SGI support per channel width - * use BIT(&enum iwl_tlc_mng_cfg_cw) - * @flags: bitmask of &enum iwl_tlc_mng_cfg_flags - * @non_ht_rates: bitmap of supported legacy rates - * @ht_rates: bitmap of &enum iwl_tlc_mng_ht_rates, per - * pair (0 - 80mhz width and below, 1 - 160mhz, 2 - 320mhz). - * @max_mpdu_len: max MPDU length, in bytes - * @max_tx_op: max TXOP in uSecs for all AC (BK, BE, VO, VI), - * set zero for no limit. - */ -struct iwl_tlc_config_cmd_v5 { - u8 sta_id; - u8 reserved1[3]; - u8 max_ch_width; - u8 mode; - u8 chains; - u8 sgi_ch_width_supp; - __le16 flags; - __le16 non_ht_rates; - __le32 ht_rates[IWL_TLC_NSS_MAX][IWL_TLC_MCS_PER_BW_NUM_V4]; - __le16 max_mpdu_len; - __le16 max_tx_op; -} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_5 */ - /** * struct iwl_tlc_config_cmd - TLC configuration * @sta_mask: station mask (in NAN we can have multiple logical stations of diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c index edf9c735dd67..b6c2fa3664cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -467,65 +467,6 @@ iwl_mld_fill_supp_rates(struct iwl_mld *mld, } } -static int iwl_mld_convert_tlc_cmd_to_v5(struct iwl_tlc_config_cmd *cmd, - struct iwl_tlc_config_cmd_v5 *cmd_v5) -{ - if (WARN_ON_ONCE(hweight32(le32_to_cpu(cmd->sta_mask)) != 1)) - return -EINVAL; - - /* Convert sta_mask to sta_id */ - cmd_v5->sta_id = __ffs(le32_to_cpu(cmd->sta_mask)); - - /* Copy all the rest */ - cmd_v5->max_ch_width = cmd->max_ch_width; - cmd_v5->mode = cmd->mode; - cmd_v5->chains = cmd->chains; - cmd_v5->sgi_ch_width_supp = cmd->sgi_ch_width_supp; - cmd_v5->flags = cmd->flags; - cmd_v5->non_ht_rates = cmd->non_ht_rates; - - BUILD_BUG_ON(sizeof(cmd_v5->ht_rates) != sizeof(cmd->ht_rates)); - memcpy(cmd_v5->ht_rates, cmd->ht_rates, sizeof(cmd->ht_rates)); - - cmd_v5->max_mpdu_len = cmd->max_mpdu_len; - cmd_v5->max_tx_op = cmd->max_tx_op; - - return 0; -} - -static int iwl_mld_convert_tlc_cmd_to_v4(struct iwl_tlc_config_cmd *cmd, - struct iwl_tlc_config_cmd_v4 *cmd_v4) -{ - if (WARN_ON_ONCE(hweight32(le32_to_cpu(cmd->sta_mask)) != 1)) - return -EINVAL; - - /* Convert sta_mask to sta_id */ - cmd_v4->sta_id = __ffs(le32_to_cpu(cmd->sta_mask)); - - /* Copy everything until ht_rates */ - cmd_v4->max_ch_width = cmd->max_ch_width; - cmd_v4->mode = cmd->mode; - cmd_v4->chains = cmd->chains; - cmd_v4->sgi_ch_width_supp = cmd->sgi_ch_width_supp; - cmd_v4->flags = cmd->flags; - cmd_v4->non_ht_rates = cmd->non_ht_rates; - - /* Convert ht_rates from __le32 to __le16 */ - BUILD_BUG_ON(ARRAY_SIZE(cmd_v4->ht_rates) != ARRAY_SIZE(cmd->ht_rates)); - BUILD_BUG_ON(ARRAY_SIZE(cmd_v4->ht_rates[0]) != ARRAY_SIZE(cmd->ht_rates[0])); - - for (int nss = 0; nss < ARRAY_SIZE(cmd->ht_rates); nss++) - for (int bw = 0; bw < ARRAY_SIZE(cmd->ht_rates[nss]); bw++) - cmd_v4->ht_rates[nss][bw] = - cpu_to_le16(le32_to_cpu(cmd->ht_rates[nss][bw])); - - /* Copy the rest */ - cmd_v4->max_mpdu_len = cmd->max_mpdu_len; - cmd_v4->max_tx_op = cmd->max_tx_op; - - return 0; -} - static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif, struct iwl_mld_sta *mld_sta, @@ -544,8 +485,6 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, }; u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD); u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0); - struct iwl_tlc_config_cmd_v5 cmd_v5 = {}; - struct iwl_tlc_config_cmd_v4 cmd_v4 = {}; void *cmd_ptr; u8 cmd_size; int ret; @@ -558,19 +497,6 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, if (cmd_ver == 6) { cmd_ptr = &cmd; cmd_size = sizeof(cmd); - } else if (cmd_ver == 5) { - /* TODO: remove support once FW moves to version 6 */ - ret = iwl_mld_convert_tlc_cmd_to_v5(&cmd, &cmd_v5); - if (ret) - return; - cmd_ptr = &cmd_v5; - cmd_size = sizeof(cmd_v5); - } else if (cmd_ver == 4) { - ret = iwl_mld_convert_tlc_cmd_to_v4(&cmd, &cmd_v4); - if (ret) - return; - cmd_ptr = &cmd_v4; - cmd_size = sizeof(cmd_v4); } else { IWL_ERR(mld, "Unsupported TLC config cmd version %d\n", cmd_ver); From 7d390ff7f25410f746b09026e4be6c93e064c945 Mon Sep 17 00:00:00 2001 From: Avinash Bhatt Date: Sun, 31 May 2026 13:53:07 +0300 Subject: [PATCH 176/285] wifi: iwlwifi: mld: add KUnit tests for PSD/EIRP RSSI adjustment Add tests for PSD/EIRP RSSI adjustment which compensates measurements when APs use PSD-based power scaling with bandwidth. Tests cover all power types, bandwidths, and limiting scenarios. Signed-off-by: Avinash Bhatt Link: https://patch.msgid.link/20260531135036.a18b8d0acd62.I68dfcc17359ab8a5abdc84e1e21db4ad1671af41@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 4 +- drivers/net/wireless/intel/iwlwifi/mld/link.h | 1 + .../wireless/intel/iwlwifi/mld/tests/link.c | 372 ++++++++++++++++++ .../wireless/intel/iwlwifi/mld/tests/utils.h | 2 + 4 files changed, 378 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 98b9c4eef583..2b8b0196692e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -1098,7 +1098,8 @@ static s8 iwl_mld_get_primary_psd(const struct ieee80211_parsed_tpe_psd *psd, return psd->power[primary_idx] / 2; } -static s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) +VISIBLE_IF_IWLWIFI_KUNIT s8 +iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) { const struct ieee80211_parsed_tpe *tpe = &link_conf->tpe; s8 psd_20mhz, psd_oper, psd_local, psd_reg, psd_boost; @@ -1217,6 +1218,7 @@ static s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) return adjustment; } +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_psd_eirp_rssi_adjust); /* This function calculates the grade of a link. Returns 0 in error case */ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h index d0aa577de81d..7b56819d45fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -145,6 +145,7 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) s8 iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld, struct ieee80211_bss_conf *link_conf); +s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf); #endif unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c index 21bcc341cd7d..a4e5f2be499f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -158,6 +158,354 @@ static const struct dup_beacon_test_case dup_beacon_cases[] = { KUNIT_ARRAY_PARAM_DESC(test_dup_beacon_rssi_adjust, dup_beacon_cases, desc); +struct psd_eirp_test_case { + const char *desc; + const struct cfg80211_chan_def *chandef; + enum ieee80211_ap_reg_power power_type; + struct { + s8 psd_20; + s8 psd_oper; + s8 eirp_20; + s8 eirp_oper; + } local, reg; + s8 expected_adj; + struct { + bool no_psd_data; + bool no_eirp_data; + bool no_reg_psd_data; + bool has_partial_psd; + u8 psd_partial_count; + bool non_uniform_psd; + bool has_unusable_channels; + } flags; +}; + +static const struct psd_eirp_test_case psd_eirp_cases[] = { + { + .desc = "20 MHz VLP baseline - no boost expected", + .chandef = &chandef_6ghz_20mhz, + .power_type = IEEE80211_REG_VLP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 40, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 40, + }, + .expected_adj = 0, + }, + { + .desc = "40 MHz VLP - power limit prevents boost", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_VLP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .expected_adj = 0, + }, + { + .desc = "80 MHz LPI - power limit caps the boost", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + }, + { + .desc = "160 MHz LPI - power limit caps the boost", + .chandef = &chandef_6ghz_160mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .expected_adj = 3, + }, + { + .desc = "320 MHz SP - power limit caps the boost", + .chandef = &chandef_6ghz_320mhz_pri0, + .power_type = IEEE80211_REG_SP_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 63, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 63, + }, + .expected_adj = 3, + }, + { + .desc = "80 MHz - EIRP prevents boost", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 20, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 20, + }, + .expected_adj = 0, + }, + { + .desc = "40 MHz - regulatory TPE sets lower limits", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 30, .psd_oper = 30, + .eirp_20 = 50, .eirp_oper = 56, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 46, + }, + .expected_adj = 3, + }, + { + .desc = "80 MHz - PSD missing, use EIRP only", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = S8_MAX, .psd_oper = S8_MAX, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = S8_MAX, .psd_oper = S8_MAX, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 0, + .flags.no_psd_data = true, + }, + { + .desc = "80 MHz - single PSD source available", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + .flags.no_reg_psd_data = true, + }, + { + .desc = "80 MHz - partial PSD data present", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 24, .psd_oper = 24, + .eirp_20 = 40, .eirp_oper = 56, + }, + .reg = { + .psd_20 = 24, .psd_oper = 24, + .eirp_20 = 40, .eirp_oper = 56, + }, + .expected_adj = 0, + .flags.has_partial_psd = true, + .flags.psd_partial_count = 2, + }, + { + .desc = "160 MHz - different PSD per sub-channel", + .chandef = &chandef_6ghz_160mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 8, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .reg = { + .psd_20 = 8, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 58, + }, + .expected_adj = 11, + .flags.non_uniform_psd = true, + }, + { + .desc = "80 MHz - EIRP missing, use PSD only", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = S8_MAX, .eirp_oper = S8_MAX, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = S8_MAX, .eirp_oper = S8_MAX, + }, + .expected_adj = 3, + .flags.no_eirp_data = true, + }, + { + .desc = "80 MHz - skip unusable channels in average", + .chandef = &chandef_6ghz_80mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 52, + }, + .expected_adj = 3, + .flags.has_unusable_channels = true, + }, + { + .desc = "40 MHz - no negative adjustment", + .chandef = &chandef_6ghz_40mhz, + .power_type = IEEE80211_REG_LPI_AP, + .local = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 18, + }, + .reg = { + .psd_20 = 20, .psd_oper = 20, + .eirp_20 = 40, .eirp_oper = 18, + }, + .expected_adj = 0, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_psd_eirp_rssi_adjust, psd_eirp_cases, desc); + +static void setup_psd(struct ieee80211_bss_conf *link_conf, + const struct psd_eirp_test_case *params, + int num_subchannels) +{ + int i; + + if (params->flags.no_psd_data) { + link_conf->tpe.psd_local[0].valid = false; + link_conf->tpe.psd_reg_client[0].valid = false; + link_conf->tpe.psd_local[0].count = 0; + link_conf->tpe.psd_reg_client[0].count = 0; + } else if (params->flags.no_reg_psd_data) { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = num_subchannels; + link_conf->tpe.psd_reg_client[0].valid = false; + link_conf->tpe.psd_reg_client[0].count = 0; + } else if (params->flags.has_partial_psd) { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = + params->flags.psd_partial_count; + link_conf->tpe.psd_reg_client[0].valid = true; + link_conf->tpe.psd_reg_client[0].count = + params->flags.psd_partial_count; + } else { + link_conf->tpe.psd_local[0].valid = true; + link_conf->tpe.psd_local[0].count = num_subchannels; + link_conf->tpe.psd_reg_client[0].valid = true; + link_conf->tpe.psd_reg_client[0].count = num_subchannels; + } + + /* TPE element stores PSD limit as value * 2 */ + if (params->flags.non_uniform_psd) { + /* PSD varies per sub-channel: 10/12/10/8 dBm pattern */ + static const s8 psd_values[] = {20, 24, 20, 16, 20, 24, 20, 16, + 20, 24, 20, 16, 20, 24, 20}; + /* Set primary channel (index 0) explicitly */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Set remaining subchannels with pattern */ + for (i = 1; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = + psd_values[i - 1]; + link_conf->tpe.psd_reg_client[0].power[i] = + psd_values[i - 1]; + } + } else if (params->flags.no_psd_data) { + for (i = 0; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = S8_MAX; + link_conf->tpe.psd_reg_client[0].power[i] = S8_MAX; + } + } else if (params->flags.has_unusable_channels) { + /* Alternate usable/unusable channels for S8_MIN test */ + /* Set primary channel (index 0) explicitly */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Alternate usable/unusable for remaining subchannels */ + for (i = 1; i < num_subchannels; i++) { + if (i % 2 == 0) { + link_conf->tpe.psd_local[0].power[i] = + params->local.psd_oper * 2; + link_conf->tpe.psd_reg_client[0].power[i] = + params->reg.psd_oper * 2; + } else { + link_conf->tpe.psd_local[0].power[i] = S8_MIN; + link_conf->tpe.psd_reg_client[0].power[i] = + S8_MIN; + } + } + } else { + /* Set primary channel (index 0) separately */ + link_conf->tpe.psd_local[0].power[0] = + params->local.psd_20 * 2; + link_conf->tpe.psd_reg_client[0].power[0] = + params->reg.psd_20 * 2; + /* Set remaining subchannels */ + for (i = 1; i < num_subchannels; i++) { + link_conf->tpe.psd_local[0].power[i] = + params->local.psd_oper * 2; + link_conf->tpe.psd_reg_client[0].power[i] = + params->reg.psd_oper * 2; + } + } +} + +static void setup_eirp(struct ieee80211_bss_conf *link_conf, + const struct psd_eirp_test_case *params, + int num_subchannels) +{ + int i; + int count = ilog2(num_subchannels) + 1; + + link_conf->tpe.max_local[0].valid = !params->flags.no_eirp_data; + link_conf->tpe.max_reg_client[0].valid = !params->flags.no_eirp_data; + + if (params->flags.no_eirp_data) { + link_conf->tpe.max_local[0].count = 0; + link_conf->tpe.max_reg_client[0].count = 0; + return; + } + + link_conf->tpe.max_local[0].count = count; + link_conf->tpe.max_reg_client[0].count = count; + + /* TPE element stores EIRP limit as value * 2 */ + link_conf->tpe.max_local[0].power[0] = params->local.eirp_20 * 2; + link_conf->tpe.max_reg_client[0].power[0] = params->reg.eirp_20 * 2; + for (i = 1; i < count; i++) { + link_conf->tpe.max_local[0].power[i] = + params->local.eirp_oper * 2; + link_conf->tpe.max_reg_client[0].power[i] = + params->reg.eirp_oper * 2; + } +} + static void test_dup_beacon_rssi_adjust(struct kunit *test) { const struct dup_beacon_test_case *params = test->param_value; @@ -192,10 +540,34 @@ static void test_dup_beacon_rssi_adjust(struct kunit *test) KUNIT_EXPECT_EQ(test, result, params->expected_adj); } +static void test_psd_eirp_rssi_adjust(struct kunit *test) +{ + const struct psd_eirp_test_case *params = test->param_value; + struct ieee80211_bss_conf *link_conf; + int num_subchannels; + s8 result; + + KUNIT_ALLOC_AND_ASSERT(test, link_conf); + + link_conf->power_type = params->power_type; + link_conf->chanreq.oper = *params->chandef; + num_subchannels = + nl80211_chan_width_to_mhz(params->chandef->width) / 20; + + setup_psd(link_conf, params, num_subchannels); + setup_eirp(link_conf, params, num_subchannels); + + result = iwl_mld_get_psd_eirp_rssi_adjust(link_conf); + + KUNIT_EXPECT_EQ(test, result, params->expected_adj); +} + static struct kunit_case link_cases[] = { KUNIT_CASE_PARAM(test_missed_beacon, test_missed_beacon_gen_params), KUNIT_CASE_PARAM(test_dup_beacon_rssi_adjust, test_dup_beacon_rssi_adjust_gen_params), + KUNIT_CASE_PARAM(test_psd_eirp_rssi_adjust, + test_psd_eirp_rssi_adjust_gen_params), {}, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h index cfed5acaac3a..7cc8cb6eedd6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -77,6 +77,8 @@ CHANNEL(chan_6ghz_221, NL80211_BAND_6GHZ, 7055); NL80211_CHAN_WIDTH_160) \ CHANDEF(chandef_6ghz_320mhz, chan_6ghz, 6105, \ NL80211_CHAN_WIDTH_320) \ + CHANDEF(chandef_6ghz_320mhz_pri0, chan_6ghz, 6265, \ + NL80211_CHAN_WIDTH_320) \ CHANDEF(chandef_6ghz_221_160mhz, chan_6ghz_221, 6985, \ NL80211_CHAN_WIDTH_160) \ /* Feel free to add more */ From a153825987be45a6a22fdf3bb4ae59109818c62b Mon Sep 17 00:00:00 2001 From: Avinash Bhatt Date: Sun, 31 May 2026 13:53:08 +0300 Subject: [PATCH 177/285] wifi: iwlwifi: mld: add KUnit tests for link grading Add tests for the link grading algorithm covering per-bandwidth grading tables, channel load calculation, 6 GHz RSSI adjustments including duplicated beacon and PSD/EIRP compensation, and puncturing penalty. Signed-off-by: Avinash Bhatt Link: https://patch.msgid.link/20260531135036.a4251e5665a0.I811b35680115e7de0ffd75b6b7a1c91ad361c97c@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 8 + .../intel/iwlwifi/mld/tests/link-selection.c | 282 +++++++++++++++--- 2 files changed, 255 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 2b8b0196692e..96e06940b34c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -3,6 +3,8 @@ * Copyright (C) 2024-2026 Intel Corporation */ +#include + #include "constants.h" #include "link.h" #include "iface.h" @@ -1032,6 +1034,9 @@ iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld, const struct cfg80211_bss_ies *beacon_ies; const struct element *elem; + KUNIT_STATIC_STUB_REDIRECT(iwl_mld_get_dup_beacon_rssi_adjust, + mld, link_conf); + /* Duplicated beacon feature is only specific to 6 GHz */ if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ, "Unexpected band %d\n", @@ -1109,6 +1114,9 @@ iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) int bw_mhz, num_subchans; u8 bw_index; + KUNIT_STATIC_STUB_REDIRECT(iwl_mld_get_psd_eirp_rssi_adjust, + link_conf); + /* PSD/EIRP adjustment is only specific to 6 GHz */ if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ, "PSD/EIRP adjustment called for non-6 GHz band %d\n", diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c index 69d222a8194c..73c2b7b3c9de 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -13,24 +13,39 @@ #include "phy.h" #include "mlo.h" +struct link_grading_input { + u8 link_id; + const struct cfg80211_chan_def *chandef; + bool active; + s32 signal; + bool has_chan_util_elem; + u8 chan_util; + u8 chan_load_by_us; + s8 dup_beacon_adj; + s8 psd_eirp_adj; + u16 punctured; +}; + static const struct link_grading_test_case { const char *desc; - struct { - struct { - u8 link_id; - const struct cfg80211_chan_def *chandef; - bool active; - s32 signal; - bool has_chan_util_elem; - u8 chan_util; /* 0-255 , used only if has_chan_util_elem is true */ - u8 chan_load_by_us; /* 0-100, used only if active is true */; - } link; - } input; + struct link_grading_input link; unsigned int expected_grade; } link_grading_cases[] = { + /* Per-bandwidth grading table tests */ { - .desc = "channel util of 128 (50%)", - .input.link = { + .desc = "20 MHz grading table: -75 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_2ghz_20mhz, + .active = false, + .signal = -75, + }, + /* 137 * 0.7 (default 2.4GHz channel load 30%) */ + .expected_grade = 96, + }, + { + .desc = "20 MHz with channel util 128 (50%): -70 dBm", + .link = { .link_id = 0, .chandef = &chandef_2ghz_20mhz, .active = false, @@ -41,8 +56,8 @@ static const struct link_grading_test_case { .expected_grade = 86, }, { - .desc = "channel util of 180 (70%)", - .input.link = { + .desc = "20 MHz with channel util 180 (70%): -70 dBm", + .link = { .link_id = 0, .chandef = &chandef_2ghz_20mhz, .active = false, @@ -53,32 +68,222 @@ static const struct link_grading_test_case { .expected_grade = 51, }, { - .desc = "channel util of 180 (70%), channel load by us of 10%", - .input.link = { + .desc = "20 MHz active link with chan load by us 10%: -70 dBm", + .link = { .link_id = 0, .chandef = &chandef_2ghz_20mhz, + .active = true, .signal = -70, .has_chan_util_elem = true, .chan_util = 180, - .active = true, .chan_load_by_us = 10, }, .expected_grade = 67, }, - { - .desc = "no channel util element", - .input.link = { + { + .desc = "40 MHz grading table: -80 dBm", + .link = { .link_id = 0, - .chandef = &chandef_2ghz_20mhz, - .signal = -70, - .active = true, + .chandef = &chandef_5ghz_40mhz, + .active = false, + .signal = -80, }, - .expected_grade = 120, + /* 206 * 0.85 (default 5GHz channel load 15%) */ + .expected_grade = 175, + }, + { + .desc = "80 MHz grading table: -70 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_5ghz_80mhz, + .active = false, + .signal = -70, + }, + /* 548 * 0.85 (default 5GHz channel load 15%) */ + .expected_grade = 466, + }, + { + .desc = "160 MHz grading table: -65 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_5ghz_160mhz, + .active = false, + .signal = -65, + }, + /* 1240 * 0.85 (default 5GHz channel load 15%) */ + .expected_grade = 1055, + }, + { + .desc = "320 MHz grading table: -60 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_320mhz, + .active = false, + .signal = -60, + }, + /* 3680 at -56 dBm (-60 + 4 dBm 6 GHz) */ + .expected_grade = 3680, + }, + /* 6 GHz RSSI adjustment integration tests */ + { + .desc = "6 GHz 160 MHz with fixed +4 dBm adjustment", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_160mhz, + .active = false, + .signal = -69, + }, + /* -69 + 4 dBm = -65, grade 1240 */ + .expected_grade = 1240, + }, + { + .desc = "6 GHz 80 MHz with fixed +4 dBm adjustment", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_80mhz, + .active = false, + .signal = -74, + }, + /* -74 + 4 dBm = -70, grade 548 */ + .expected_grade = 548, + }, + { + .desc = "6 GHz 40 MHz with fixed +4 dBm adjustment", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_40mhz, + .active = false, + .signal = -84, + }, + /* -84 + 4 dBm = -80, grade 206 */ + .expected_grade = 206, + }, + { + .desc = "6 GHz 20 MHz with fixed +4 dBm adjustment", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_20mhz, + .active = false, + .signal = -79, + }, + .expected_grade = 137, + }, + /* Duplicated beacon RSSI adjustment tests */ + { + .desc = "6 GHz 40 MHz dup beacon: -81 dBm + 3 dBm = -78 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_40mhz, + .active = false, + .signal = -81, + .dup_beacon_adj = 3, + }, + .expected_grade = 206, + }, + { + .desc = "6 GHz 80 MHz dup beacon: -73 dBm + 6 dBm = -67 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_80mhz, + .active = false, + .signal = -73, + .dup_beacon_adj = 6, + }, + .expected_grade = 620, + }, + { + .desc = "6 GHz 160 MHz dup beacon: -74 dBm + 9 dBm = -65 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_160mhz, + .active = false, + .signal = -74, + .dup_beacon_adj = 9, + }, + .expected_grade = 1240, + }, + { + .desc = "6 GHz 320 MHz dup beacon: -72 dBm + 12 dBm = -60 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_320mhz, + .active = false, + .signal = -72, + .dup_beacon_adj = 12, + }, + .expected_grade = 3296, + }, + /* PSD/EIRP RSSI adjustment tests */ + { + .desc = "6 GHz 80 MHz PSD/EIRP: -77 dBm + 3 dBm = -74 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_80mhz, + .active = false, + .signal = -77, + .psd_eirp_adj = 3, + }, + /* -77 + 3 dBm = -74, grade 412; fallback +4: -73 -> 548 */ + .expected_grade = 412, + }, + { + .desc = "6 GHz 160 MHz PSD/EIRP: -70 dBm + 3 dBm = -67 dBm", + .link = { + .link_id = 0, + .chandef = &chandef_6ghz_160mhz, + .active = false, + .signal = -70, + .psd_eirp_adj = 3, + }, + /* -70 + 3 dBm = -67, grade 1096; fallback +4: -66 -> 1240 */ + .expected_grade = 1096, + }, + /* Puncturing penalty tests */ + { + .desc = "80 MHz with 20 MHz punctured: 3 active subchannels", + .link = { + .link_id = 0, + .chandef = &chandef_5ghz_80mhz, + .active = false, + .signal = -70, + .punctured = 0x2, + }, + /* 548 * 0.85 (5GHz load) * 3/4 (puncturing) */ + .expected_grade = 349, + }, + { + .desc = "160 MHz with 40 MHz punctured: 6 active subchannels", + .link = { + .link_id = 0, + .chandef = &chandef_5ghz_160mhz, + .active = false, + .signal = -65, + .punctured = 0xC, + }, + /* 1240 * 0.85 (5GHz load) * 6/8 (puncturing) */ + .expected_grade = 791, }, }; KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc); +static s8 fake_dup_beacon_rssi_adjust(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + const struct link_grading_test_case *params = + kunit_get_current_test()->param_value; + + return params->link.dup_beacon_adj; +} + +static s8 fake_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf) +{ + const struct link_grading_test_case *params = + kunit_get_current_test()->param_value; + + return params->link.psd_eirp_adj; +} + static void setup_link(struct ieee80211_bss_conf *link) { struct kunit *test = kunit_get_current_test(); @@ -88,14 +293,14 @@ static void setup_link(struct ieee80211_bss_conf *link) KUNIT_ALLOC_AND_ASSERT(test, link->bss); - link->bss->signal = DBM_TO_MBM(test_param->input.link.signal); + link->bss->signal = DBM_TO_MBM(test_param->link.signal); - link->chanreq.oper = *test_param->input.link.chandef; + link->chanreq.oper = *test_param->link.chandef; - if (test_param->input.link.has_chan_util_elem) { + if (test_param->link.has_chan_util_elem) { struct cfg80211_bss_ies *ies; struct ieee80211_bss_load_elem bss_load = { - .channel_util = test_param->input.link.chan_util, + .channel_util = test_param->link.chan_util, }; struct element *elem = iwlmld_kunit_gen_element(WLAN_EID_QBSS_LOAD, @@ -110,7 +315,10 @@ static void setup_link(struct ieee80211_bss_conf *link) rcu_assign_pointer(link->bss->ies, ies); } - if (test_param->input.link.active) { + if (test_param->link.punctured) + link->chanreq.oper.punctured = test_param->link.punctured; + + if (test_param->link.active) { struct ieee80211_chanctx_conf *chan_ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); struct iwl_mld_phy *phy; @@ -119,7 +327,7 @@ static void setup_link(struct ieee80211_bss_conf *link) phy = iwl_mld_phy_from_mac80211(chan_ctx); - phy->channel_load_by_us = test_param->input.link.chan_load_by_us; + phy->channel_load_by_us = test_param->link.chan_load_by_us; } } @@ -131,12 +339,11 @@ static void test_link_grading(struct kunit *test) struct ieee80211_vif *vif; struct ieee80211_bss_conf *link; unsigned int actual_grade; - /* Extract test case parameters */ - u8 link_id = test_param->input.link.link_id; - bool active = test_param->input.link.active; + u8 link_id = test_param->link.link_id; + bool active = test_param->link.active; u16 valid_links; struct iwl_mld_kunit_link assoc_link = { - .chandef = test_param->input.link.chandef, + .chandef = test_param->link.chandef, }; /* If the link is not active, use a different link as the assoc link */ @@ -150,6 +357,11 @@ static void test_link_grading(struct kunit *test) vif = iwlmld_kunit_setup_mlo_assoc(valid_links, &assoc_link); + kunit_activate_static_stub(test, iwl_mld_get_dup_beacon_rssi_adjust, + fake_dup_beacon_rssi_adjust); + kunit_activate_static_stub(test, iwl_mld_get_psd_eirp_rssi_adjust, + fake_psd_eirp_rssi_adjust); + wiphy_lock(mld->wiphy); link = wiphy_dereference(mld->wiphy, vif->link_conf[link_id]); KUNIT_ASSERT_NOT_NULL(test, link); From a6136ca2dd9773d6bcd45e8290403536c9c71054 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 31 May 2026 13:53:09 +0300 Subject: [PATCH 178/285] wifi: iwlwifi: bump maximum core version for BZ/SC/DR to 106 Start supporting Core 106 FW on these devices. Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260531135036.4ec96e57a17b.I1eea0a221656b2f03839964734d9a3624530b964@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index 1542cbc3bb22..606362463dc7 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_BZ_UCODE_CORE_MAX 105 +#define IWL_BZ_UCODE_CORE_MAX 106 /* Lowest firmware core release supported */ #define IWL_BZ_UCODE_CORE_MIN 102 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index 6a5870619411..946975294b4f 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -9,7 +9,7 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_DR_UCODE_CORE_MAX 105 +#define IWL_DR_UCODE_CORE_MAX 106 /* Lowest firmware core release supported */ #define IWL_DR_UCODE_CORE_MIN 102 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index f8913293ccef..e8240c1782ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -10,7 +10,7 @@ #include "fw/api/txq.h" /* Highest firmware core release supported */ -#define IWL_SC_UCODE_CORE_MAX 105 +#define IWL_SC_UCODE_CORE_MAX 106 /* Lowest firmware core release supported */ #define IWL_SC_UCODE_CORE_MIN 102 From 1f2b940056a45827f0f0486f3b72d88ec894d874 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 3 Jun 2026 14:36:24 +0200 Subject: [PATCH 179/285] wifi: mac80211: Free keys associated with NAN Device A NAN Device interface can have keys associated with it, e.g., IGTK and BIGTK used for Tx. When a NAN Device interface is stopped, we need to clear these keys. Signed-off-by: Ilan Peer Link: https://patch.msgid.link/20260603143624.c6c771885383.I929410cb8efec4fab2d42ead396bfefaf9f803f1@changeid Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 56400c22d8bc..086272c3ec08 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -648,6 +648,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do spin_unlock_bh(&sdata->u.nan.de.func_lock); } + + /* + * Free the remaining keys that might be associated with the + * NAN interface, e.g., IGTK and BIGTK used for Tx. + */ + ieee80211_free_keys(sdata, true); break; case NL80211_IFTYPE_NAN_DATA: RCU_INIT_POINTER(sdata->u.nan_data.nmi, NULL); From d832f6b83d48b27b98ffeda973d83c0d0409fccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=82=85=E7=BB=A7=E6=99=97?= Date: Wed, 3 Jun 2026 12:28:54 -0700 Subject: [PATCH 180/285] wifi: mac80211: fix monitor mode frame capture for real chanctx drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d594cc6f2c58 ("wifi: mac80211: restore non-chanctx injection behaviour") restored the monitor injection fallback for drivers using chanctx emulation but explicitly deferred drivers that transitioned to real chanctx ops. mt76 falls in that category and still drops every injected frame when monitor coexists with another interface. When the monitor has no chanctx of its own, fall back to the only chanctx in flight if there is exactly one. Refuse if multiple are present: picking arbitrarily would inject on an unrelated channel. Emulated and real chanctx drivers both flow through this fallback, since emulation always presents zero or one chanctx in local->chanctx_list. Reran the airgeddon evil-twin flow (hostapd AP + coexisting monitor VIF on the same phy + aireplay-ng deauth from the monitor) on mt7921e PCIe and mt7921u USB across 2.4 GHz and 5 GHz, and on a Kali VM with MT7921U passthrough as the closest match to the original reporter's setup. None reproduced the hang seen against the earlier attempt at this fix (<20251216111909.25076-2-johannes@sipsolutions.net>) or against v1 on lore in March. Cc: # causes some older drivers to crash Reported-by: Oscar Alfonso Diaz Closes: https://github.com/morrownr/USB-WiFi/issues/682 Tested-by: Devin Wittmayer Fixes: 0a44dfc07074 ("wifi: mac80211: simplify non-chanctx drivers") Signed-off-by: 傅继晗 Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/5c2760bd9bc34616bf7892848872522254dd1ce5.1780513445.git.lucid_duck@justthetip.ca Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 933c86ca21c3..cf336e92c072 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2407,10 +2407,20 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, rcu_dereference(tmp_sdata->vif.bss_conf.chanctx_conf); } + if (!chanctx_conf) { + struct ieee80211_chanctx *ctx; + bool first = true; + + list_for_each_entry_rcu(ctx, &local->chanctx_list, list) { + if (!first) + goto fail_rcu; + chanctx_conf = &ctx->conf; + first = false; + } + } + if (chanctx_conf) chandef = &chanctx_conf->def; - else if (local->emulate_chanctx) - chandef = &local->hw.conf.chandef; else goto fail_rcu; From 7495adaa0e45e180f4b6b7436675c6266edff1ff Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Thu, 4 Jun 2026 12:33:16 +0200 Subject: [PATCH 181/285] wifi: wlcore: enable the right set of ciphers The firmware version number check for IGTK introduced in commit c34dbc5900b0 ("wifi: wlcore: Add support for IGTK key") lets the amount of ciphers decrease on every boot of a too old firmware and that is practically happening. It also does not take into account other chips than the wl18xx. On some wl128x, the following can be observed when connecting via nm to a common ap: [ 484.113311] wlcore: WARNING could not set keys [ 484.117828] wlcore: ERROR Could not add or replace key [ 484.123016] wlan0: failed to set key (5, ff:ff:ff:ff:ff:ff) to hardware (-5) [ 484.123046] wlcore: Hardware recovery in progress. FW ver: Rev 7.3.10.0.142 [ 484.139923] wlcore: pc: 0x0, hint_sts: 0x00000048 count: 1 [ 484.145721] wlcore: down [ 484.148986] ieee80211 phy0: Hardware restart was requested [ 484.610473] wlcore: firmware booted (Rev 7.3.10.0.142) [ 484.633758] wlcore: Association completed. [ 484.690490] wlcore: ERROR command execute failure 14 [ 484.690490] ------------[ cut here ]------------ [ 484.700195] WARNING: drivers/net/wireless/ti/wlcore/main.c:872 at wl12xx_queue_recovery_work+0x64/0x74 [wlcore], CPU#0: kworker/0:0/892 This repeats endlessly. Always disable IGTK on wl12xx and fix the decrementing mess. Fixes: c34dbc5900b0 ("wifi: wlcore: Add support for IGTK key") Signed-off-by: Andreas Kemnade Link: https://patch.msgid.link/20260604103316.377251-1-andreas@kemnade.info Signed-off-by: Johannes Berg --- drivers/net/wireless/ti/wlcore/main.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1c340a4a0930..be583ae331c0 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -32,6 +32,15 @@ #define WL1271_BOOT_RETRIES 3 #define WL1271_WAKEUP_TIMEOUT 500 +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WL1271_CIPHER_SUITE_GEM, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + static char *fwlog_param; static int fwlog_mem_blocks = -1; static int bug_on_recovery = -1; @@ -2367,6 +2376,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) static int wl12xx_init_fw(struct wl1271 *wl) { + struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev); int retries = WL1271_BOOT_RETRIES; bool booted = false; struct wiphy *wiphy = wl->hw->wiphy; @@ -2421,8 +2431,9 @@ static int wl12xx_init_fw(struct wl1271 *wl) /* WLAN_CIPHER_SUITE_AES_CMAC must be last in cipher_suites; support only with firmware 8.9.1 and newer */ - if (wl->chip.fw_ver[FW_VER_MAJOR] < 1) - wl->hw->wiphy->n_cipher_suites--; + if (wl->chip.fw_ver[FW_VER_MAJOR] < 1 || + (!strncmp(pdev_data->family->name, "wl12", 4))) + wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - 1; /* * Now we know if 11a is supported (info from the NVS), so disable @@ -6198,14 +6209,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl) static int wl1271_init_ieee80211(struct wl1271 *wl) { int i; - static const u32 cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - WL1271_CIPHER_SUITE_GEM, - WLAN_CIPHER_SUITE_AES_CMAC, - }; /* The tx descriptor buffer */ wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr); From c52b14422573a43917f48c094771a80cbe58f568 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 4 Jun 2026 15:40:41 -0700 Subject: [PATCH 182/285] wifi: mac80211: Fix -Wc23-extensions in hwmp_route_info_get() When building with a version of clang that supports '-fms-anonymous-structs' (which will be used by the kernel instead of the wider '-fms-extensions'), there are a couple warnings after some recent mesg_hwmp.c changes: net/mac80211/mesh_hwmp.c:373:3: error: label followed by a declaration is a C23 extension [-Werror,-Wc23-extensions] 373 | struct ieee80211_mesh_hwmp_preq_top *preq_elem_top = | ^ net/mac80211/mesh_hwmp.c:390:3: error: label followed by a declaration is a C23 extension [-Werror,-Wc23-extensions] 390 | struct ieee80211_mesh_hwmp_prep_top *prep_elem_top = | ^ 2 errors generated. Enclose the switch case blocks in braces to clear up the warning. Fixes: a91c65cb99d1 ("wifi: mac80211: Use struct instead of macro for PREP frame") Fixes: 4ac20bd40b7d ("wifi: mac80211: Use struct instead of macro for PREQ frame") Signed-off-by: Nathan Chancellor Link: https://patch.msgid.link/20260604-mac80211-mesh_hwmp-fix-c23-extensions-v1-1-25a64d6ce541@kernel.org Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 84903737271d..7f962b003579 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -369,7 +369,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, fresh_info = true; switch (action) { - case MPATH_PREQ: + case MPATH_PREQ: { struct ieee80211_mesh_hwmp_preq_top *preq_elem_top = (void *)hwmp_ie; struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom = @@ -381,7 +381,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, orig_metric = le32_to_cpu(preq_elem_bottom->metric); hopcount = preq_elem_top->hopcount + 1; break; - case MPATH_PREP: + } + case MPATH_PREP: { /* Originator here refers to the MP that was the target in the * Path Request. We divert from the nomenclature in the draft * so that we can easily use a single function to gather path @@ -398,6 +399,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, orig_metric = le32_to_cpu(prep_elem_bottom->metric); hopcount = prep_elem_top->hopcount + 1; break; + } default: rcu_read_unlock(); return 0; From b978c424cbfb1f5ab024db12cf7b813fa4e23ca0 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 4 Jun 2026 17:56:27 -0700 Subject: [PATCH 183/285] wifi: mac80211: fold tid_ampdu_rx allocations into a flexible array Convert the separately-allocated reorder_buf pointer to a C99 flexible array member at the end of struct tid_ampdu_rx, with both the sk_buff_head and the jiffies timestamp in each array element. This collapses three allocations into one and removes the corresponding kfree() pairs from the error and free paths. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260605005627.317194-1-rosenp@gmail.com [fix kernel-doc] Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 22 +++------------------- net/mac80211/rx.c | 14 +++++++------- net/mac80211/sta_info.h | 13 ++++++++----- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 0140ac826b23..9629e00069a1 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -49,9 +49,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h) int i; for (i = 0; i < tid_rx->buf_size; i++) - __skb_queue_purge(&tid_rx->reorder_buf[i]); - kfree(tid_rx->reorder_buf); - kfree(tid_rx->reorder_time); + __skb_queue_purge(&tid_rx->reorder[i].buf); kfree(tid_rx); } @@ -412,7 +410,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, } /* prepare A-MPDU MLME for Rx aggregation */ - tid_agg_rx = kzalloc_obj(*tid_agg_rx); + tid_agg_rx = kzalloc_flex(*tid_agg_rx, reorder, buf_size); if (!tid_agg_rx) goto end; @@ -426,27 +424,13 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, timer_setup(&tid_agg_rx->reorder_timer, sta_rx_agg_reorder_timer_expired, 0); - /* prepare reordering buffer */ - tid_agg_rx->reorder_buf = - kzalloc_objs(struct sk_buff_head, buf_size); - tid_agg_rx->reorder_time = - kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL); - if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { - kfree(tid_agg_rx->reorder_buf); - kfree(tid_agg_rx->reorder_time); - kfree(tid_agg_rx); - goto end; - } - for (i = 0; i < buf_size; i++) - __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]); + __skb_queue_head_init(&tid_agg_rx->reorder[i].buf); ret = drv_ampdu_action(local, sta->sdata, ¶ms); ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", sta->sta.addr, tid, ret); if (ret) { - kfree(tid_agg_rx->reorder_buf); - kfree(tid_agg_rx->reorder_time); kfree(tid_agg_rx); goto end; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index db421edfd54c..fb9a3574afe9 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1188,7 +1188,7 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx, int index) { - struct sk_buff_head *frames = &tid_agg_rx->reorder_buf[index]; + struct sk_buff_head *frames = &tid_agg_rx->reorder[index].buf; struct sk_buff *tail = skb_peek_tail(frames); struct ieee80211_rx_status *status; @@ -1211,7 +1211,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, int index, struct sk_buff_head *frames) { - struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index]; + struct sk_buff_head *skb_list = &tid_agg_rx->reorder[index].buf; struct sk_buff *skb; struct ieee80211_rx_status *status; @@ -1290,14 +1290,14 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, continue; } if (skipped && - !time_after(jiffies, tid_agg_rx->reorder_time[j] + + !time_after(jiffies, tid_agg_rx->reorder[j].time + HT_RX_REORDER_BUF_TIMEOUT)) goto set_release_timer; /* don't leave incomplete A-MSDUs around */ for (i = (index + 1) % tid_agg_rx->buf_size; i != j; i = (i + 1) % tid_agg_rx->buf_size) - __skb_queue_purge(&tid_agg_rx->reorder_buf[i]); + __skb_queue_purge(&tid_agg_rx->reorder[i].buf); ht_dbg_ratelimited(sdata, "release an RX reorder frame due to timeout on earlier frames\n"); @@ -1331,7 +1331,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, if (!tid_agg_rx->removed) mod_timer(&tid_agg_rx->reorder_timer, - tid_agg_rx->reorder_time[j] + 1 + + tid_agg_rx->reorder[j].time + 1 + HT_RX_REORDER_BUF_TIMEOUT); } else { timer_delete(&tid_agg_rx->reorder_timer); @@ -1426,9 +1426,9 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata } /* put the frame in the reordering buffer */ - __skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb); + __skb_queue_tail(&tid_agg_rx->reorder[index].buf, skb); if (!(status->flag & RX_FLAG_AMSDU_MORE)) { - tid_agg_rx->reorder_time[index] = jiffies; + tid_agg_rx->reorder[index].time = jiffies; tid_agg_rx->stored_mpdu_num++; ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames); } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 26138934b72e..5da3142d8516 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -207,11 +207,8 @@ struct tid_ampdu_tx { /** * struct tid_ampdu_rx - TID aggregation information (Rx). * - * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an - * A-MSDU with individually reported subframes. * @reorder_buf_filtered: bitmap indicating where there are filtered frames in * the reorder buffer that should be ignored when releasing frames - * @reorder_time: jiffies when skb was added * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) * @reorder_timer: releases expired frames from the reorder buffer. * @sta: station we are attached to @@ -228,6 +225,10 @@ struct tid_ampdu_tx { * and ssn. * @removed: this session is removed (but might have been found due to RCU) * @started: this session has started (head ssn or higher was received) + * @reorder: reorder buffer entries + * @reorder.buf: &struct sk_buff_head for the frames, since there could be + * multiple at each entry from an A-MSDU reported as individual subframes + * @reorder.time: time when this entry was filled (jiffies) * * This structure's lifetime is managed by RCU, assignments to * the array holding it must hold the aggregation mutex. @@ -241,8 +242,6 @@ struct tid_ampdu_rx { struct rcu_head rcu_head; spinlock_t reorder_lock; u64 reorder_buf_filtered; - struct sk_buff_head *reorder_buf; - unsigned long *reorder_time; struct sta_info *sta; struct timer_list session_timer; struct timer_list reorder_timer; @@ -256,6 +255,10 @@ struct tid_ampdu_rx { u8 auto_seq:1, removed:1, started:1; + struct { + struct sk_buff_head buf; + unsigned long time; + } reorder[]; }; /** From dfb67ae569bf0726187725b1ef8d89377778861e Mon Sep 17 00:00:00 2001 From: Thiyagarajan Pandiyan Date: Fri, 5 Jun 2026 11:13:07 +0530 Subject: [PATCH 184/285] wifi: nl80211: Increase ie_len size to prevent truncated IEs in new peer notifications Currently, ie_len in cfg80211_notify_new_peer_candidate is defined as 1-byte field, capping the maximum IE list size at 255 bytes. When a large beacon is received, the IE list is truncated, passing incomplete data to wpa_supplicant. This causes supplicant to fail parsing the IEs. Increasing the size of ie_len to allow the full length of the IE list to be forwarded properly. Signed-off-by: Thiyagarajan Pandiyan Link: https://patch.msgid.link/20260605054307.427874-1-thiyagarajan@aerlync.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- net/wireless/nl80211.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f91a71c7f4db..8188ad200de5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -8685,7 +8685,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, * cfg80211 then sends a notification to userspace. */ void cfg80211_notify_new_peer_candidate(struct net_device *dev, - const u8 *macaddr, const u8 *ie, u8 ie_len, + const u8 *macaddr, const u8 *ie, size_t ie_len, int sig_dbm, gfp_t gfp); /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 19244708f5c3..85057bd4d565 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -21355,7 +21355,7 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, } void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr, - const u8 *ie, u8 ie_len, + const u8 *ie, size_t ie_len, int sig_dbm, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; From 303f11fda2fa4c6f7aa86b8fa54aaee5e1ef181b Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Thu, 4 Jun 2026 21:54:01 +0530 Subject: [PATCH 185/285] wifi: mac80211: Add sta pointer sanity check in ieee80211_8023_xmit() Currently ieee80211_8023_xmit() accesses the sta pointer without any sanity check, assuming that only unicast packets for an authorized station are processed. But the sta pointer could become NULL when a framework to support 802.3 offload for the multicast packets is added in the follow-up patches. Add the valid sta pointer sanity check to avoid the invalid pointer access. This aligns with some of the subordinate functions called by ieee80211_8023_xmit() that already NULL-check 'sta' such as ieee80211_select_queue() and ieee80211_aggr_check(). Signed-off-by: Tamizh Chelvam Raja Link: https://patch.msgid.link/20260604162403.1563729-2-tamizh.raja@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index cf336e92c072..15ec77255c3f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4660,7 +4660,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, { struct ieee80211_tx_info *info; struct ieee80211_local *local = sdata->local; - struct tid_ampdu_tx *tid_tx; + struct tid_ampdu_tx *tid_tx = NULL; struct sk_buff *seg, *next; unsigned int skbs = 0, len = 0; u16 queue; @@ -4680,7 +4680,9 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, ieee80211_aggr_check(sdata, sta, skb); tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); + + if (sta) + tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); if (tid_tx) { if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { /* fall back to non-offload slow path */ @@ -4728,8 +4730,11 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, } dev_sw_netstats_tx_add(dev, skbs, len); - sta->deflink.tx_stats.packets[queue] += skbs; - sta->deflink.tx_stats.bytes[queue] += len; + + if (sta) { + sta->deflink.tx_stats.packets[queue] += skbs; + sta->deflink.tx_stats.bytes[queue] += len; + } ieee80211_tpt_led_trig_tx(local, len); From 2307b36ce34fd2166509ea2aeef0de5768ed03b7 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Thu, 4 Jun 2026 21:54:02 +0530 Subject: [PATCH 186/285] wifi: mac80211: Add multicast to unicast support for 802.3 path mac80211 already supports multicast-to-unicast conversion for native 802.11 TX paths, but this handling is missing for the 802.3 transmit path. Due to that the packet never converted to unicast and directly pass it to 802.11 Tx path by checking the destination address as multicast. Extend ieee80211_subif_start_xmit_8023() to honor the multicast_to_unicast setting by cloning and converting multicast Ethernet frames into per-station unicast transmissions, following the same behavior of the native 802.11 TX path and allow it to take 802.3 path. Signed-off-by: Tamizh Chelvam Raja Link: https://patch.msgid.link/20260604162403.1563729-3-tamizh.raja@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 15ec77255c3f..a353758f53ff 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4746,19 +4746,14 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, kfree_skb(skb); } -netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, - struct net_device *dev) +static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ethhdr *ehdr = (struct ethhdr *)skb->data; struct ieee80211_key *key; struct sta_info *sta; - if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - rcu_read_lock(); if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { @@ -4787,6 +4782,30 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, ieee80211_subif_start_xmit(skb, dev); out: rcu_read_unlock(); +} + +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ethhdr *ehdr = (struct ethhdr *)skb->data; + + if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (unlikely(is_multicast_ether_addr(ehdr->h_dest) && + ieee80211_multicast_to_unicast(skb, dev))) { + struct sk_buff_head queue; + + __skb_queue_head_init(&queue); + ieee80211_convert_to_unicast(skb, dev, &queue); + while ((skb = __skb_dequeue(&queue))) + __ieee80211_subif_start_xmit_8023(skb, dev); + } else { + __ieee80211_subif_start_xmit_8023(skb, dev); + } return NETDEV_TX_OK; } From 4cc0cc0b297c17c2b106d6892bd13d9c32fe66ce Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Thu, 4 Jun 2026 21:54:03 +0530 Subject: [PATCH 187/285] wifi: mac80211: Add 802.3 multicast encapsulation offload support mac80211 converts 802.3 multicast packets to 802.11 format before driver TX, even when Ethernet encapsulation offload is enabled. This prevents drivers that support multicast Ethernet encapsulation offload from receiving frames in native 802.3 format. Introduce the IEEE80211_OFFLOAD_ENCAP_MCAST flag to bypass the 802.11 encapsulation step and pass the multicast packet to the driver in 802.3 format. Drivers that support multicast Ethernet encapsulation offload can advertise this flag. Disable multicast encapsulation offload in MLO case for drivers not advertising MLO_MCAST_MULTI_LINK_TX support for AP mode and for 3-address AP_VLAN multicast packets. Signed-off-by: Tamizh Chelvam Raja Link: https://patch.msgid.link/20260604162403.1563729-4-tamizh.raja@oss.qualcomm.com [fix unlikely(), indentation] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++++ net/mac80211/tx.c | 51 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7dd558f4025b..4f95da023746 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2014,12 +2014,16 @@ enum ieee80211_vif_flags { * @IEEE80211_OFFLOAD_DECAP_ENABLED: rx encapsulation offload is enabled * The driver supports passing received 802.11 frames as 802.3 frames to * mac80211. + * @IEEE80211_OFFLOAD_ENCAP_MCAST: tx multicast encapsulation offload is enabled + * The driver supports sending multicast frames passed as 802.3 frames + * by mac80211. */ enum ieee80211_offload_flags { IEEE80211_OFFLOAD_ENCAP_ENABLED = BIT(0), IEEE80211_OFFLOAD_ENCAP_4ADDR = BIT(1), IEEE80211_OFFLOAD_DECAP_ENABLED = BIT(2), + IEEE80211_OFFLOAD_ENCAP_MCAST = BIT(3), }; #define IEEE80211_NAN_AVAIL_BLOB_MAX_LEN 54 diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a353758f53ff..e96a5733ec51 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4746,11 +4746,32 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, kfree_skb(skb); } +static bool ieee80211_check_mcast_offload(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ethhdr *ehdr = (struct ethhdr *)skb->data; + + if ((ieee80211_vif_is_mld(&sdata->vif) && + (sdata->vif.type == NL80211_IFTYPE_AP && + !ieee80211_hw_check(&sdata->local->hw, MLO_MCAST_MULTI_LINK_TX))) || + (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + !sdata->wdev.use_4addr)) + return false; + + if (!is_multicast_ether_addr(skb->data) || + !(sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_MCAST) || + sdata->control_port_protocol == ehdr->h_proto) + return false; + + return true; +} + static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ethhdr *ehdr = (struct ethhdr *)skb->data; + struct ieee80211_link_data *link; struct ieee80211_key *key; struct sta_info *sta; @@ -4761,14 +4782,30 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, goto out; } - if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || - !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || - sdata->control_port_protocol == ehdr->h_proto)) + /* + * If STA is invalid, use the multicast offload path when applicable. + * In AP mode, drop the frame if there are no associated stations; + * otherwise use the default link and multicast key for transmission. + */ + if (unlikely(IS_ERR_OR_NULL(sta) && + ieee80211_check_mcast_offload(sdata, skb))) { + sta = NULL; + if (ieee80211_vif_get_num_mcast_if(sdata) <= 0) { + /* No associated STAs - no need to send multicast frames. */ + kfree_skb(skb); + goto out; + } + link = &sdata->deflink; + key = rcu_dereference(link->default_multicast_key); + } else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || + !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || + sdata->control_port_protocol == ehdr->h_proto)) { goto skip_offload; - - key = rcu_dereference(sta->ptk[sta->ptk_idx]); - if (!key) - key = rcu_dereference(sdata->default_unicast_key); + } else { + key = rcu_dereference(sta->ptk[sta->ptk_idx]); + if (!key) + key = rcu_dereference(sdata->default_unicast_key); + } if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) From 88a240d86d3d64521f9194abe185ac71cc74d0bd Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:16 +0000 Subject: [PATCH 188/285] wifi: wcn36xx: fix heap overflow from oversized firmware HAL response The firmware response dispatcher copies all synchronous HAL responses into the 4096-byte hal_buf without validating the response length. A response exceeding WCN36XX_HAL_BUF_SIZE causes a heap buffer overflow with firmware-controlled content. Add a bounds check on the response length. Fixes: 8e84c2582169 ("wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-2-tristmd@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/wcn36xx/smd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 813553edcb78..f65328329f4f 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -3293,6 +3293,10 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev, case WCN36XX_HAL_EXIT_IMPS_RSP: case WCN36XX_HAL_UPDATE_CHANNEL_LIST_RSP: case WCN36XX_HAL_ADD_BCN_FILTER_RSP: + if (len > WCN36XX_HAL_BUF_SIZE) { + wcn36xx_warn("HAL response too large: %d\n", len); + break; + } memcpy(wcn->hal_buf, buf, len); wcn->hal_rsp_len = len; complete(&wcn->hal_rsp_compl); From df2187acfca6c6cca372c5d35f42394d9c270b09 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:17 +0000 Subject: [PATCH 189/285] wifi: wcn36xx: fix OOB read from firmware count in PRINT_REG_INFO indication The firmware-controlled rsp->count field is used as the loop bound for indexing into the flexible rsp->regs[] array without validation against the message length. A count exceeding the actual data causes out-of- bounds reads from the heap-allocated message buffer. Add a check that count fits within the received message. Fixes: 43efa3c0f241 ("wcn36xx: Implement print_reg indication") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-3-tristmd@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/wcn36xx/smd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index f65328329f4f..2a0c946d8109 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2805,6 +2805,12 @@ static int wcn36xx_smd_print_reg_info_ind(struct wcn36xx *wcn, return -EIO; } + if (rsp->count > (len - sizeof(*rsp)) / sizeof(rsp->regs[0])) { + wcn36xx_warn("Truncated print reg info indication: count %u, len %zu\n", + rsp->count, len); + return -EIO; + } + wcn36xx_dbg(WCN36XX_DBG_HAL, "reginfo indication, scenario: 0x%x reason: 0x%x\n", rsp->scenario, rsp->reason); From b5e6f21923ca89d90256e7346301056f6502691e Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Tue, 21 Apr 2026 13:50:18 +0000 Subject: [PATCH 190/285] wifi: wcn36xx: fix OOB read from short trigger BA firmware response The firmware response length is only checked against sizeof(*rsp) (20 bytes), but when candidate_cnt >= 1, a 22-byte candidate struct is read at buf + 20 without verifying the response contains it. This causes an out-of-bounds read of stale heap data, corrupting the BA session state. Add validation that the response includes the candidate data. Fixes: 16be1ac55944 ("wcn36xx: Parse trigger_ba response properly") Signed-off-by: Tristan Madani Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260421135018.352774-4-tristmd@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/wcn36xx/smd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 2a0c946d8109..c0b477345832 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2599,6 +2599,9 @@ static int wcn36xx_smd_trigger_ba_rsp(void *buf, int len, struct add_ba_info *ba if (rsp->candidate_cnt < 1) return rsp->status ? rsp->status : -EINVAL; + if (len < sizeof(*rsp) + sizeof(*candidate)) + return -EINVAL; + candidate = (struct wcn36xx_hal_trigger_ba_rsp_candidate *)(buf + sizeof(*rsp)); for (i = 0; i < STACFG_MAX_TC; i++) { From a969232fa359950f7cf2ea415938562263369909 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sun, 3 May 2026 21:58:32 +0500 Subject: [PATCH 191/285] wifi: wcn36xx: fix spelling mistakes in dxe header comment Fix three spelling mistakes in the DMA Transfer Engine (DXE) description comment at the top of dxe.c. No functional change. Signed-off-by: Stepan Ionichev Link: https://patch.msgid.link/20260503165832.1675-1-sozdayvek@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/wcn36xx/dxe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 5a0ef2295a3a..44020ec265fb 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -16,8 +16,8 @@ /* DXE - DMA transfer engine * we have 2 channels(High prio and Low prio) for TX and 2 channels for RX. - * through low channels data packets are transfered - * through high channels managment packets are transfered + * through low channels data packets are transferred + * through high channels management packets are transferred */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt From 0bd50e363581a9f833c051f7543ffd1fd3455509 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 18 May 2026 19:03:17 -0700 Subject: [PATCH 192/285] wifi: wcn36xx: allocate chan_surveys with main struct Avoid allocating separately with a flexible array member. Simplifies allocation slightly. Signed-off-by: Rosen Penev Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260519020317.635011-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/wcn36xx/main.c | 13 ++----------- drivers/net/wireless/ath/wcn36xx/wcn36xx.h | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index c3f0860873de..ad8a4bd910d2 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1568,7 +1568,8 @@ static int wcn36xx_probe(struct platform_device *pdev) wcnss = dev_get_drvdata(pdev->dev.parent); - hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops); + n_channels = wcn_band_2ghz.n_channels + wcn_band_5ghz.n_channels; + hw = ieee80211_alloc_hw(struct_size(wcn, chan_survey, n_channels), &wcn36xx_ops); if (!hw) { wcn36xx_err("failed to alloc hw\n"); ret = -ENOMEM; @@ -1590,16 +1591,6 @@ static int wcn36xx_probe(struct platform_device *pdev) goto out_wq; } - n_channels = wcn_band_2ghz.n_channels + wcn_band_5ghz.n_channels; - wcn->chan_survey = devm_kcalloc(wcn->dev, - n_channels, - sizeof(struct wcn36xx_chan_survey), - GFP_KERNEL); - if (!wcn->chan_survey) { - ret = -ENOMEM; - goto out_wq; - } - ret = dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32)); if (ret < 0) { wcn36xx_err("failed to set DMA mask: %d\n", ret); diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index 7ee79593cd23..8c43f67bd780 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -298,7 +298,7 @@ struct wcn36xx { struct ieee80211_channel *channel; spinlock_t survey_lock; /* protects chan_survey */ - struct wcn36xx_chan_survey *chan_survey; + struct wcn36xx_chan_survey chan_survey[]; }; static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, From 6a03ff9d5765ab701c12c9fc781fa6de171e1a2e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 6 May 2026 16:48:48 -0700 Subject: [PATCH 193/285] wifi: ath9k_htc: use module_usb_driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This follows the pattern with other USB Wifi drivers. There is nothing special being done in the _init and _exit functions here. Simplifies and saves some lines of code. Signed-off-by: Rosen Penev Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260506234848.189840-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath9k/hif_usb.c | 10 +--------- drivers/net/wireless/ath/ath9k/hif_usb.h | 2 -- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 18 ------------------ 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 821909b81ea9..7ad85a712757 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1530,12 +1530,4 @@ static struct usb_driver ath9k_hif_usb_driver = { .disable_hub_initiated_lpm = 1, }; -int ath9k_hif_usb_init(void) -{ - return usb_register(&ath9k_hif_usb_driver); -} - -void ath9k_hif_usb_exit(void) -{ - usb_deregister(&ath9k_hif_usb_driver); -} +module_usb_driver(ath9k_hif_usb_driver); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index b3e66b0485a5..c836bbe23b7a 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -136,8 +136,6 @@ struct hif_device_usb { u8 flags; /* HIF_USB_* */ }; -int ath9k_hif_usb_init(void); -void ath9k_hif_usb_exit(void); void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev); #endif /* HTC_USB_H */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 5270d0c0af17..6de78ae85726 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -1021,21 +1021,3 @@ int ath9k_htc_resume(struct htc_target *htc_handle) return ret; } #endif - -static int __init ath9k_htc_init(void) -{ - if (ath9k_hif_usb_init() < 0) { - pr_err("No USB devices found, driver not installed\n"); - return -ENODEV; - } - - return 0; -} -module_init(ath9k_htc_init); - -static void __exit ath9k_htc_exit(void) -{ - ath9k_hif_usb_exit(); - pr_info("Driver unloaded\n"); -} -module_exit(ath9k_htc_exit); From 44589c155e6f4531100ddd3b6478190edd075035 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 16 May 2026 21:27:16 -0700 Subject: [PATCH 194/285] wifi: ath9k: Clear DMA descriptors without memset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear ath9k DMA descriptors with explicit status word stores instead of memset(). The descriptor rings are coherent DMA memory, which may be mapped uncached on 32-bit powerpc. The optimized memset() path can use dcbz there and trigger an alignment warning. Use WRITE_ONCE() for the descriptor status words so the compiler keeps the clears as ordinary stores instead of folding them back into bulk memset(). This covers AR9003 TX status descriptors as well as the RX status area cleared when setting up RX descriptors. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260517042716.2218386-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath9k/ar9002_mac.c | 15 +++++++++++++- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 23 +++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index b70cd4af1ae0..e355f94a9390 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -403,6 +403,19 @@ static int ar9002_hw_get_duration(struct ath_hw *ah, const void *ds, int index) } } +static void ath9k_hw_clear_rxdesc_status(struct ar5416_desc *ads) +{ + WRITE_ONCE(ads->u.rx.status0, 0); + WRITE_ONCE(ads->u.rx.status1, 0); + WRITE_ONCE(ads->u.rx.status2, 0); + WRITE_ONCE(ads->u.rx.status3, 0); + WRITE_ONCE(ads->u.rx.status4, 0); + WRITE_ONCE(ads->u.rx.status5, 0); + WRITE_ONCE(ads->u.rx.status6, 0); + WRITE_ONCE(ads->u.rx.status7, 0); + WRITE_ONCE(ads->u.rx.status8, 0); +} + void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags) { @@ -412,7 +425,7 @@ void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, if (flags & ATH9K_RXDESC_INTREQ) ads->ds_ctl1 |= AR_RxIntrReq; - memset(&ads->u.rx, 0, sizeof(ads->u.rx)); + ath9k_hw_clear_rxdesc_status(ads); } EXPORT_SYMBOL(ath9k_hw_setuprxdesc); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index a8bc003077dc..97027d286317 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -352,6 +352,19 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked, return true; } +static void ath9k_hw_clear_txstatus(struct ar9003_txs *ads) +{ + WRITE_ONCE(ads->ds_info, 0); + WRITE_ONCE(ads->status1, 0); + WRITE_ONCE(ads->status2, 0); + WRITE_ONCE(ads->status3, 0); + WRITE_ONCE(ads->status4, 0); + WRITE_ONCE(ads->status5, 0); + WRITE_ONCE(ads->status6, 0); + WRITE_ONCE(ads->status7, 0); + WRITE_ONCE(ads->status8, 0); +} + static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_status *ts) { @@ -370,7 +383,7 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, (MS(ads->ds_info, AR_TxRxDesc) != 1)) { ath_dbg(ath9k_hw_common(ah), XMIT, "Tx Descriptor error %x\n", ads->ds_info); - memset(ads, 0, sizeof(*ads)); + ath9k_hw_clear_txstatus(ads); return -EIO; } @@ -427,7 +440,7 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11); ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12); - memset(ads, 0, sizeof(*ads)); + ath9k_hw_clear_txstatus(ads); return 0; } @@ -591,10 +604,12 @@ EXPORT_SYMBOL(ath9k_hw_process_rxdesc_edma); void ath9k_hw_reset_txstatus_ring(struct ath_hw *ah) { + int i; + ah->ts_tail = 0; - memset((void *) ah->ts_ring, 0, - ah->ts_size * sizeof(struct ar9003_txs)); + for (i = 0; i < ah->ts_size; i++) + ath9k_hw_clear_txstatus(&ah->ts_ring[i]); ath_dbg(ath9k_hw_common(ah), XMIT, "TS Start 0x%x End 0x%x Virt %p, Size %d\n", From 13cdd324cc155200ea328257c746004d898ff3de Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 17 May 2026 15:21:36 -0700 Subject: [PATCH 195/285] wifi: ath9k: remove TX99 power array zero init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This array is fully initialized in the loop itself. No need to zero initialize and then overwrite. Remove static from the array. This was a holdover from when the array was a static global variable. It no longer confers any benefit. Also add a min() call to avoid the manual if/ternary operation. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260517222136.1660347-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 6274d1624261..5d0433f2ae37 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1811,10 +1811,10 @@ static void ar9003_hw_tx99_stop(struct ath_hw *ah) static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower) { - static u8 p_pwr_array[ar9300RateSize] = { 0 }; + u8 p_pwr_array[ar9300RateSize]; unsigned int i; - txpower = txpower <= MAX_RATE_POWER ? txpower : MAX_RATE_POWER; + txpower = min(txpower, MAX_RATE_POWER); for (i = 0; i < ar9300RateSize; i++) p_pwr_array[i] = txpower; From f0e5e8703fd61dacaa0c18016146e64cf32ddcb7 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 21 May 2026 16:18:06 -0700 Subject: [PATCH 196/285] wifi: ath9k: remove disabling of bands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old platform data code that used this is gone and this serves no purpose. The modern way to disable bands is ieee80211-freq-limit, which is already implemented. Signed-off-by: Rosen Penev Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260521231806.261220-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath9k/hw.c | 16 ++++------------ drivers/net/wireless/ath/ath9k/hw.h | 2 -- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 05c95e67a853..e08ab73fcacb 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2498,19 +2498,11 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE); - if (eeval & AR5416_OPFLAGS_11A) { - if (ah->disable_5ghz) - ath_warn(common, "disabling 5GHz band\n"); - else - pCap->hw_caps |= ATH9K_HW_CAP_5GHZ; - } + if (eeval & AR5416_OPFLAGS_11A) + pCap->hw_caps |= ATH9K_HW_CAP_5GHZ; - if (eeval & AR5416_OPFLAGS_11G) { - if (ah->disable_2ghz) - ath_warn(common, "disabling 2GHz band\n"); - else - pCap->hw_caps |= ATH9K_HW_CAP_2GHZ; - } + if (eeval & AR5416_OPFLAGS_11G) + pCap->hw_caps |= ATH9K_HW_CAP_2GHZ; if ((pCap->hw_caps & (ATH9K_HW_CAP_2GHZ | ATH9K_HW_CAP_5GHZ)) == 0) { ath_err(common, "both bands are disabled\n"); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index d9d2f64c5570..b942b8303d8f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -974,8 +974,6 @@ struct ath_hw { bool is_clk_25mhz; int (*get_mac_revision)(void); int (*external_reset)(void); - bool disable_2ghz; - bool disable_5ghz; const struct firmware *eeprom_blob; u16 *nvmem_blob; /* devres managed */ From 38b2fb7d2df16f5801f7d88a4739942b95a5f6aa Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 21 May 2026 16:20:20 -0700 Subject: [PATCH 197/285] wifi: ath9k_htc: allocate tx_buf and buf together MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a flexible array member to combine allocations. No need to have them separate as they are always together. Signed-off-by: Rosen Penev Acked-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260521232020.261405-1-rosenp@gmail.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath9k/hif_usb.c | 14 ++------------ drivers/net/wireless/ath/ath9k/hif_usb.h | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 7ad85a712757..515267f48d80 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -454,7 +454,6 @@ static void hif_usb_stop(void *hif_handle) usb_kill_urb(tx_buf->urb); list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); - kfree(tx_buf->buf); kfree(tx_buf); spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); } @@ -811,7 +810,6 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) &hif_dev->tx.tx_buf, list) { list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); - kfree(tx_buf->buf); kfree(tx_buf); } spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); @@ -828,7 +826,6 @@ static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) usb_kill_urb(tx_buf->urb); list_del(&tx_buf->list); usb_free_urb(tx_buf->urb); - kfree(tx_buf->buf); kfree(tx_buf); spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); } @@ -849,14 +846,10 @@ static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) init_usb_anchor(&hif_dev->mgmt_submitted); for (i = 0; i < MAX_TX_URB_NUM; i++) { - tx_buf = kzalloc_obj(*tx_buf); + tx_buf = kzalloc_flex(*tx_buf, buf, MAX_TX_BUF_SIZE); if (!tx_buf) goto err; - tx_buf->buf = kzalloc(MAX_TX_BUF_SIZE, GFP_KERNEL); - if (!tx_buf->buf) - goto err; - tx_buf->urb = usb_alloc_urb(0, GFP_KERNEL); if (!tx_buf->urb) goto err; @@ -871,10 +864,7 @@ static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) return 0; err: - if (tx_buf) { - kfree(tx_buf->buf); - kfree(tx_buf); - } + kfree(tx_buf); ath9k_hif_usb_dealloc_tx_urbs(hif_dev); return -ENOMEM; } diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index c836bbe23b7a..dc0b0fa5c325 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -77,13 +77,13 @@ extern int htc_use_dev_fw; #define HIF_USB_MAX_TXPIPES 4 struct tx_buf { - u8 *buf; u16 len; u16 offset; struct urb *urb; struct sk_buff_head skb_queue; struct hif_device_usb *hif_dev; struct list_head list; + u8 buf[]; }; struct rx_buf { From b224d18b1e5d1cddfc67f63f41d80023b2ec8889 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Sat, 6 Jun 2026 07:43:45 +0000 Subject: [PATCH 198/285] wifi: mac80211: bound S1G TIM PVB walk to the TIM element ieee80211_s1g_check_tim() parses the S1G Partial Virtual Bitmap (PVB) of a received TIM element. The TIM is handed in as the element payload: ieee802_11_parse_elems_full() stores elems->tim = elem->data and elems->tim_len = elem->datalen (net/mac80211/parse.c), so the valid bytes are [tim, tim + tim_len). When walking the encoded blocks the function passes the walker an end sentinel of (const u8 *)tim + tim_len + 2, i.e. two bytes past the end of the element. ieee80211_s1g_find_target_block() loops while (ptr + 1 <= end) and dereferences ptr (and the per-mode ieee80211_s1g_len_*() helpers read *ptr), so it can read up to two bytes beyond the TIM element -- an out-of-bounds read of adjacent skb/heap data when the TIM is the last element in the frame. The +2 appears to account for the element id/len header, but tim already points past that header at the element payload, so the addend is wrong. Pass the correct element end, (const u8 *)tim + tim_len. Fixes: e0c47c6229c2 ("wifi: mac80211: support parsing S1G TIM PVB") Signed-off-by: Bryam Vargas Link: https://patch.msgid.link/20260606074341.49135-1-hexlabsecurity@proton.me Signed-off-by: Johannes Berg --- include/linux/ieee80211-s1g.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/ieee80211-s1g.h b/include/linux/ieee80211-s1g.h index 22dde4cbc1b0..3f9626ad3d97 100644 --- a/include/linux/ieee80211-s1g.h +++ b/include/linux/ieee80211-s1g.h @@ -556,7 +556,7 @@ static inline bool ieee80211_s1g_check_tim(const struct ieee80211_tim_ie *tim, */ err = ieee80211_s1g_find_target_block(&enc_blk, &target_aid, tim->virtual_map, - (const u8 *)tim + tim_len + 2); + (const u8 *)tim + tim_len); if (err) return false; From 9629f31f505d74e76ac0d7a9492fd06c0316fc5d Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 26 Apr 2026 23:33:36 +0900 Subject: [PATCH 199/285] wifi: mt76: mt7925: clean up DMA on probe failure mt7925_pci_probe() initializes DMA before registering the device. If mt7925_register_device() fails, probe returns through err_free_irq without tearing down DMA state. That leaves the TX NAPI instance enabled and skips the DMA queue cleanup that the normal remove path performs through mt7925e_unregister_device(). Add a dedicated unwind label for failures after mt7925_dma_init() succeeds. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260426143728.41534-1-pakmyeonghun@bagmyeonghun-ui-MacBookPro.local Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index c4161754c01d..48837723024d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -415,10 +415,12 @@ static int mt7925_pci_probe(struct pci_dev *pdev, ret = mt7925_register_device(dev); if (ret) - goto err_free_irq; + goto err_free_dma; return 0; +err_free_dma: + mt792x_dma_cleanup(dev); err_free_irq: devm_free_irq(&pdev->dev, pdev->irq, dev); err_free_dev: From 31ee1582717e220cc5a3fa8f3940d5693c5c9169 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 26 Apr 2026 22:17:46 -0700 Subject: [PATCH 200/285] wifi: mt76: fix of_get_mac_address error handling Check return value instead of is_valid_ether_addr. The latter is handled by the former. Signed-off-by: Rosen Penev Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260427051746.954704-1-rosenp@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c index afdb73661866..b99d7452800f 100644 --- a/drivers/net/wireless/mediatek/mt76/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -181,7 +181,7 @@ mt76_eeprom_override(struct mt76_phy *phy) if (err == -EPROBE_DEFER) return err; - if (!is_valid_ether_addr(phy->macaddr)) { + if (err) { eth_random_addr(phy->macaddr); dev_info(dev->dev, "Invalid MAC address, using random address %pM\n", From 346dac35b1384af9338b34b6835e82e634ea4d2c Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Tue, 12 May 2026 14:52:45 +0800 Subject: [PATCH 201/285] wifi: mt76: mt7921: fix resource leak in probe error path When pcim_iomap_region() or devm_kmemdup() fail, the code returns directly without cleaning up previously allocated resources: - mt76_device allocated by mt76_alloc_device() - pci irq vectors allocated by pci_alloc_irq_vectors() Fix this by jumping to the existing error cleanup path instead of returning directly. Fixes: ee5bb35d2b83 ("wifi: mt76: mt7921: Replace deprecated PCI function") Fixes: 222606f43b58 ("wifi: mt76: mt7921: handle MT7902 irq_map quirk with mutable copy") Signed-off-by: Hongling Zeng Link: https://patch.msgid.link/20260512065245.46496-1-zenghongling@kylinos.cn Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 7a790ddf43bb..49a37185f056 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -343,11 +343,14 @@ static int mt7921_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mdev); - regs = pcim_iomap_region(pdev, 0, pci_name(pdev)); - if (IS_ERR(regs)) - return PTR_ERR(regs); - dev = container_of(mdev, struct mt792x_dev, mt76); + + regs = pcim_iomap_region(pdev, 0, pci_name(pdev)); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err_free_dev; + } + dev->fw_features = features; dev->hif_ops = &mt7921_pcie_ops; dev->irq_map = &irq_map; @@ -359,8 +362,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev, /* MT7902 needs a mutable copy because wm2_complete_mask differs */ map = devm_kmemdup(&pdev->dev, &irq_map, sizeof(irq_map), GFP_KERNEL); - if (!map) - return -ENOMEM; + if (!map) { + ret = -ENOMEM; + goto err_free_dev; + } map->rx.wm2_complete_mask = 0; dev->irq_map = map; From 351dd7d2c80d23e56dcce6faa4e62bea5b0877c7 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Mon, 4 May 2026 07:51:06 -0700 Subject: [PATCH 202/285] wifi: mt76: mt7921/mt7925: fix NULL dereference in CSA beacon This patch is based on a BUG as reported by Bongani Hlope at https://lore.kernel.org/all/20260502125824.425d7159@bongani-mini.home.org.za/ When a channel-switch announcement (CSA) beacon is received, cfg80211 queues a wiphy work item that eventually calls mt7921_channel_switch_rx_beacon(). If the station disconnects (or the channel context is otherwise torn down) between the time the work is queued and the time it runs, the driver's dev->new_ctx pointer can already have been cleared to NULL. mt7921_channel_switch_rx_beacon() then dereferences new_ctx unconditionally, triggering a NULL pointer dereference at address 0x0: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:mt7921_channel_switch_rx_beacon+0x1f/0x100 [mt7921_common] The same missing guard exists in mt7925_channel_switch_rx_beacon(), which shares the same code pattern introduced by the same commit. Add an early-return NULL check for dev->new_ctx in both mt7921_channel_switch_rx_beacon() and mt7925_channel_switch_rx_beacon(). When new_ctx is NULL there is no pending channel switch to process, so returning immediately is the correct and safe action. Fixes: 8aa2f59260eb ("wifi: mt76: mt7921: introduce CSA support") Reported-by: Bongani Hlope Oops-Analysis: http://oops.fenrus.org/reports/lkml/20260502125824.425d7159@bongani-mini.home.org.za/report.html Link: https://lore.kernel.org/all/20260502125824.425d7159@bongani-mini.home.org.za/ Signed-off-by: Arjan van de Ven Cc: linux-wireless@vger.kernel.org Cc: linux-mediatek@lists.infradead.org Cc: Felix Fietkau Cc: Lorenzo Bianconi Cc: Ryder Lee Link: https://patch.msgid.link/20260504145107.1329197-1-arjan@linux.intel.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 3d74fabe7408..a326f4c95c7c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1508,6 +1508,9 @@ static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw, struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; u16 beacon_interval = vif->bss_conf.beacon_int; + if (!dev->new_ctx) + return; + if (cfg80211_chandef_identical(&chsw->chandef, &dev->new_ctx->def) && chsw->count) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 73d3722739d0..ec6eb2897aec 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -2400,6 +2400,9 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw, if (ieee80211_vif_is_mld(vif)) return; + if (!dev->new_ctx) + return; + beacon_interval = vif->bss_conf.beacon_int; if (cfg80211_chandef_identical(&chsw->chandef, From 5b7154f934c4c1b86e0fbfd95ad570a25bd08662 Mon Sep 17 00:00:00 2001 From: ElXreno Date: Wed, 6 May 2026 04:39:15 +0300 Subject: [PATCH 203/285] wifi: mt76: route TDLS-peer frames as 3-addr non-DS in HW encap With HW TX encap offload enabled, the mt76 firmware builds the 802.11 header for the 802.3 frame using the per-WCID context. For a STATION vif the HDR_TRANS TLV currently sets ToDS=1, which makes the firmware default to the BSSID as A1 and emit STA->AP-formatted frames regardless of which peer the WCID points to. For TDLS-paired peers this is wrong. Data frames go on air addressed to the AP, the AP MAC-ACKs and silently drops them per IEEE 802.11z (an AP must not forward to a TDLS-paired peer). Management and control frames bypass the HW encap path and still reach the peer; only user data fails. Add MT_WCID_FLAG_TDLS_PEER, set it in mt7915, mt7921, mt7925 and mt7996 sta-add paths when sta->tdls is true, and override the HDR_TRANS TLV in mt76_connac_mcu_wtbl_hdr_trans_tlv() (Connac2 - mt7915 / mt7921 / mt7922), mt7925_mcu_sta_hdr_trans_tlv() (mt7925) and mt7996_mcu_sta_hdr_trans_tlv() (mt7996) to set ToDS=0, FromDS=0 when the flag is set. The 3-addr non-DS form matches what 802.11z uses for direct links; the firmware then constructs the frame with A1=peer rather than A1=BSSID. HW encap offload remains enabled for AP and any non-TDLS traffic. Verified on mt7925e + Samsung S938B over a 5 GHz HE 80 MHz channel with iperf3 -t 30 to the TDLS peer: before fix: over the TDLS direct link, 7 TDLS Setup action frames and 3 RTS frames reach the peer; 0 QoS Data frames make it through (mgmt/control paths bypass HW encap, the data path does not). iperf3 stalls. after fix: 2.90 GBytes transferred at 830 Mbit/s sustained, 0 TCP retransmits. mt7915, mt7921, mt7922 and mt7996 are not regression-tested in this change for lack of hardware. Their HDR_TRANS handling mirrors the verified mt7925 change; the firmware behavior is shared across these chips. Signed-off-by: ElXreno Assisted-by: Claude:claude-opus-4-7 bpftrace tcpdump Link: https://patch.msgid.link/20260506-mt7925-tdls-fixes-v2-1-46aa826ba8bb@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 5 +++++ drivers/net/wireless/mediatek/mt76/mt7915/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7921/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 5 +++++ drivers/net/wireless/mediatek/mt76/mt7996/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 5 +++++ 8 files changed, 28 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 527bef97e122..07955555f84d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -361,6 +361,7 @@ enum mt76_wcid_flags { MT_WCID_FLAG_PS, MT_WCID_FLAG_4ADDR, MT_WCID_FLAG_HDR_TRANS, + MT_WCID_FLAG_TDLS_PEER, }; #define MT76_N_WCIDS 1088 diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 89bd52ea8bf7..f61d0625ef51 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -491,6 +491,11 @@ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb, htr->to_ds = true; htr->from_ds = true; } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + htr->to_ds = false; + htr->from_ds = false; + } } EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_hdr_trans_tlv); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index e1d83052aa6d..51643a48ed15 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -760,6 +760,9 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->wcid.phy_idx = ext_phy; msta->jiffies = jiffies; + if (sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta->wcid.flags); + ewma_avg_signal_init(&msta->avg_ack_signal); mt7915_mac_wtbl_update(dev, idx, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index a326f4c95c7c..adaf9a53f296 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -828,6 +828,9 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->deflink.last_txs = jiffies; msta->deflink.sta = msta; + if (sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta->deflink.wcid.flags); + ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index ec6eb2897aec..30832c4bd303 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -887,6 +887,9 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev, mlink->wcid.link_valid = !!link_sta->sta->valid_links; mlink->sta = msta; + if (link_sta->sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &mlink->wcid.flags); + wcid = &mlink->wcid; ewma_signal_init(&wcid->rssi); rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 22bad3cba8df..333cacfaca9c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -1091,6 +1091,11 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb, hdr_trans->to_ds = true; hdr_trans->from_ds = true; } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + hdr_trans->to_ds = false; + hdr_trans->from_ds = false; + } } int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index a8a6552d49f6..9b8db6efb5ac 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1164,6 +1164,9 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev, msta_link->wcid.link_valid = !!sta->valid_links; msta_link->wcid.def_wcid = &msta->deflink.wcid; + if (link_sta->sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta_link->wcid.flags); + ewma_avg_signal_init(&msta_link->avg_ack_signal); ewma_signal_init(&msta_link->wcid.rssi); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 16420375112d..6aaf3ed94221 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -2139,6 +2139,11 @@ mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb, hdr_trans->from_ds = true; hdr_trans->mesh = true; } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + hdr_trans->to_ds = false; + hdr_trans->from_ds = false; + } } static enum mcu_mmps_mode From 37d65384aa6f9cbe45f4052b13b378af1aab3e95 Mon Sep 17 00:00:00 2001 From: ElXreno Date: Wed, 6 May 2026 04:39:16 +0300 Subject: [PATCH 204/285] wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer On a STATION vif, removing a TDLS peer takes the mt7925_mac_sta_remove -> mt7925_mac_sta_remove_links path. The first loop in that function calls mt7925_mcu_add_bss_info(..., enable=false) for every link of the station being removed. For a non-MLO STATION vif there is exactly one link, link 0, whose bss_conf is the AP's. TDLS peers do not have their own bss_conf - they share the AP's BSS. The result is that every TDLS peer teardown sends a BSS_INFO_UPDATE with enable=0 for the AP's BSS to the firmware, which wipes the AP-side rate-control context. The connection stays associated and TX from the host still works at the negotiated rate, but the AP's downlink to us collapses to the lowest mandatory OFDM rate (HE-MCS 0 / 6 Mbit/s OFDM) and only slowly recovers as rate adaptation re-learns under sustained traffic. With brief or bursty traffic the link can stay at 6-72 Mbit/s indefinitely, requiring a manual reconnect. mt7925_mac_link_sta_remove() already guards its own mt7925_mcu_add_bss_info(..., false) call with "vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls". Add the equivalent guard at the top of the cleanup loop in mt7925_mac_sta_remove_links(), above the link_sta / link_conf / mlink / mconf lookups, so TDLS peer teardown skips the loop body entirely without doing the per-link work that would just be thrown away. Verified on mt7925e by triggering Samsung-S938B auto-TDLS via iperf3 and watching iw rx bitrate after teardown: Before: rx bitrate collapses to 6.0-72.0 Mbit/s, oscillates 17/72/ 137/288/432 Mbit/s for 30+ seconds, no full recovery without a manual reassoc. After: rx bitrate stays at 1200.9 Mbit/s HE-MCS 11 NSS 2 80 MHz across the entire TDLS lifecycle. bpftrace confirms a single mt7925_mcu_add_bss_info(enable=0) call per teardown before the fix; zero such calls after. Fixes: 3878b4333602 ("wifi: mt76: mt7925: update mt7925_mac_link_sta_[add, assoc, remove] for MLO") Cc: stable@vger.kernel.org Signed-off-by: ElXreno Assisted-by: Claude:claude-opus-4-7 bpftrace Link: https://patch.msgid.link/20260506-mt7925-tdls-fixes-v2-2-46aa826ba8bb@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 30832c4bd303..dcd2f07318fc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1268,6 +1268,9 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_AP) break; + if (vif->type == NL80211_IFTYPE_STATION && sta->tdls) + continue; + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); if (!link_sta) continue; From 7fae097aa9a56c30febf539d72ef3773165d3aa3 Mon Sep 17 00:00:00 2001 From: Rajat Gupta Date: Wed, 6 May 2026 21:35:31 -0700 Subject: [PATCH 205/285] wifi: mt76: use kfree_rcu for offchannel link in mt76_put_vif_phy_link mt76_put_vif_phy_link() frees the offchannel mlink with plain kfree() after rcu_assign_pointer(NULL). However, rcu_assign_pointer only prevents future RCU readers from obtaining the pointer -- it does not wait for existing readers that already hold it via rcu_dereference. The TX datapath (e.g. mt7996_mac_write_txwi) dereferences mlink->wcid and mlink->idx under rcu_read_lock. If a TX softirq obtained the pointer via rcu_dereference just before the NULL assignment, it will dereference freed memory after the kfree. struct mt76_vif_link already contains an rcu_head field that is unused at this free site -- a developer oversight, since the adjacent kfree_rcu_mightsleep call for rx_sc in the same function shows the pattern was understood. Replace kfree(mlink) with kfree_rcu(mlink, rcu_head). Fixes: a8f424c1287c ("wifi: mt76: add multi-radio remain_on_channel functions") Signed-off-by: Rajat Gupta Link: https://patch.msgid.link/20260507043531.492-1-rajat.gupta@oss.qualcomm.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index 05eee64706ea..6edcb3b8f279 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -307,7 +307,7 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, rcu_assign_pointer(mvif->offchannel_link, NULL); dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink); - kfree(mlink); + kfree_rcu(mlink, rcu_head); } void mt76_roc_complete(struct mt76_phy *phy) From b350ad117b4266f03e22c68b8057e98035aa059a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 10:33:31 +0200 Subject: [PATCH 206/285] wifi: mt76: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430083335.215239-2-johan@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/usb.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c index d91feffadda9..bab7b91f14be 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c @@ -151,7 +151,6 @@ static int mt7663u_probe(struct usb_interface *usb_intf, return -ENOMEM; dev = container_of(mdev, struct mt7615_dev, mt76); - udev = usb_get_dev(udev); usb_reset_device(udev); usb_set_intfdata(usb_intf, dev); @@ -193,7 +192,6 @@ static int mt7663u_probe(struct usb_interface *usb_intf, error: mt76u_queues_deinit(&dev->mt76); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); @@ -211,7 +209,6 @@ static void mt7663u_disconnect(struct usb_interface *usb_intf) mt7663u_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); } From 7f81b0214c4ee4fb39fbc92ca5aa96dfe890d2ea Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 10:33:32 +0200 Subject: [PATCH 207/285] wifi: mt76x0u: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430083335.215239-3-johan@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76x0/usb.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index 90e5666c0857..2acce121fb14 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -245,7 +245,6 @@ static int mt76x0u_probe(struct usb_interface *usb_intf, if (id->driver_info) dev->no_2ghz = true; - usb_dev = usb_get_dev(usb_dev); usb_reset_device(usb_dev); usb_set_intfdata(usb_intf, dev); @@ -284,7 +283,6 @@ static int mt76x0u_probe(struct usb_interface *usb_intf, err: usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76u_queues_deinit(&dev->mt76); mt76_free_device(&dev->mt76); @@ -303,7 +301,6 @@ static void mt76x0_disconnect(struct usb_interface *usb_intf) mt76x0u_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); } From 70a05728dbb68ab5b2917fee5bbb1f0b6c498225 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 10:33:33 +0200 Subject: [PATCH 208/285] wifi: mt76x2u: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430083335.215239-4-johan@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 01cb3b2830f3..8af360bca643 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -57,7 +57,6 @@ static int mt76x2u_probe(struct usb_interface *intf, dev = container_of(mdev, struct mt76x02_dev, mt76); - udev = usb_get_dev(udev); usb_reset_device(udev); usb_set_intfdata(intf, dev); @@ -84,14 +83,12 @@ static int mt76x2u_probe(struct usb_interface *intf, mt76u_queues_deinit(&dev->mt76); mt76_free_device(&dev->mt76); usb_set_intfdata(intf, NULL); - usb_put_dev(udev); return err; } static void mt76x2u_disconnect(struct usb_interface *intf) { - struct usb_device *udev = interface_to_usbdev(intf); struct mt76x02_dev *dev = usb_get_intfdata(intf); struct ieee80211_hw *hw = mt76_hw(dev); @@ -100,7 +97,6 @@ static void mt76x2u_disconnect(struct usb_interface *intf) mt76x2u_cleanup(dev); mt76_free_device(&dev->mt76); usb_set_intfdata(intf, NULL); - usb_put_dev(udev); } static int __maybe_unused mt76x2u_suspend(struct usb_interface *intf, From d246ff8e5958ffd407361a33fdf0021f000083ac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 10:33:34 +0200 Subject: [PATCH 209/285] wifi: mt76: mt792xu: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430083335.215239-5-johan@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/usb.c | 2 -- drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 2 -- drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 1 - 3 files changed, 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 17057e68bf21..9bfc234f306f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -197,7 +197,6 @@ static int mt7921u_probe(struct usb_interface *usb_intf, dev->fw_features = features; dev->hif_ops = &hif_ops; - udev = usb_get_dev(udev); usb_reset_device(udev); usb_set_intfdata(usb_intf, dev); @@ -246,7 +245,6 @@ static int mt7921u_probe(struct usb_interface *usb_intf, mt76u_queues_deinit(&dev->mt76); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index d9968f03856d..84bcebbf009a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -185,7 +185,6 @@ static int mt7925u_probe(struct usb_interface *usb_intf, dev->fw_features = features; dev->hif_ops = &hif_ops; - udev = usb_get_dev(udev); usb_reset_device(udev); usb_set_intfdata(usb_intf, dev); @@ -234,7 +233,6 @@ static int mt7925u_probe(struct usb_interface *usb_intf, mt76u_queues_deinit(&dev->mt76); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 47827d1c5ccb..0d9dbd0aaf9e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -341,7 +341,6 @@ void mt792xu_disconnect(struct usb_interface *usb_intf) mt792xu_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); mt76_free_device(&dev->mt76); } From 2172c96a1eb495966923ebbbe5965e3158875534 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 10:33:35 +0200 Subject: [PATCH 210/285] wifi: mt7601u: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430083335.215239-6-johan@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt7601u/usb.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c index c41ae251cb95..9306870cbc91 100644 --- a/drivers/net/wireless/mediatek/mt7601u/usb.c +++ b/drivers/net/wireless/mediatek/mt7601u/usb.c @@ -274,7 +274,6 @@ static int mt7601u_probe(struct usb_interface *usb_intf, if (!dev) return -ENOMEM; - usb_dev = usb_get_dev(usb_dev); usb_reset_device(usb_dev); usb_set_intfdata(usb_intf, dev); @@ -319,7 +318,6 @@ static int mt7601u_probe(struct usb_interface *usb_intf, mt7601u_cleanup(dev); err: usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); destroy_workqueue(dev->stat_wq); ieee80211_free_hw(dev->hw); @@ -334,7 +332,6 @@ static void mt7601u_disconnect(struct usb_interface *usb_intf) mt7601u_cleanup(dev); usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); destroy_workqueue(dev->stat_wq); ieee80211_free_hw(dev->hw); From 7ec087fef32a88410488b764b0f5eef68e51175f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 24 Mar 2026 15:49:03 +0000 Subject: [PATCH 211/285] wifi: mt76: mt7996: fix out-of-bounds array access during hardware restart During hardware restart, link_id can be IEEE80211_LINK_UNSPECIFIED, causing an out-of-bounds array access on msta->link[]. Add mt7996_sta_link() and mt7996_sta_link_protected() helper functions for accessing sta links with proper RCU handling and bounds checking. Use them for any sta link RCU access. Reported-by: Chad Monroe Link: https://patch.msgid.link/20260324154904.2555603-1-nbd@nbd.name Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7996/debugfs.c | 4 ++-- .../net/wireless/mediatek/mt76/mt7996/mac.c | 6 ++--- .../net/wireless/mediatek/mt76/mt7996/main.c | 17 +++++++------- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 22 +++++++++---------- .../wireless/mediatek/mt76/mt7996/mt7996.h | 19 ++++++++++++++++ 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c index 34af800964d1..ef9a9204adf5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -664,7 +664,7 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) if (!mlink) continue; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (!msta_link) continue; @@ -1042,7 +1042,7 @@ static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file, mutex_lock(&dev->mt76.mutex); - msta_link = mt76_dereference(msta->link[link_sta->link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_sta->link_id); if (!msta_link) { ret = -EINVAL; goto out; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index e2a83da3a09c..c98446057282 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -48,7 +48,7 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, if (mlink->band_idx != band_idx) continue; - msta_link = rcu_dereference(msta->link[i]); + msta_link = mt7996_sta_link(msta, i); break; } @@ -1038,7 +1038,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, if (link_id != wcid->link_id && link_id != IEEE80211_LINK_UNSPECIFIED) { if (msta) { struct mt7996_sta_link *msta_link = - rcu_dereference(msta->link[link_id]); + mt7996_sta_link(msta, link_id); if (msta_link) wcid = &msta_link->wcid; @@ -1346,7 +1346,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) IEEE80211_MLD_MAX_NUM_LINKS) { struct mt7996_sta_link *msta_link; - msta_link = rcu_dereference(msta->link[id]); + msta_link = mt7996_sta_link(msta, id); if (!msta_link) continue; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 9b8db6efb5ac..b3cbf68bb53d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -207,8 +207,7 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct mt7996_sta *msta; msta = (struct mt7996_sta *)sta->drv_priv; - msta_link = mt76_dereference(msta->link[link_id], - &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) return 0; @@ -1384,7 +1383,7 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, if (!link) continue; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -1576,7 +1575,7 @@ static void mt7996_tx(struct ieee80211_hw *hw, if (msta) { struct mt7996_sta_link *msta_link; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (msta_link) wcid = &msta_link->wcid; } @@ -1947,7 +1946,7 @@ static void mt7996_link_sta_rc_update(struct ieee80211_hw *hw, rcu_read_lock(); - msta_link = rcu_dereference(msta->link[link_sta->link_id]); + msta_link = mt7996_sta_link(msta, link_sta->link_id); if (msta_link) { struct mt7996_dev *dev = mt7996_hw_dev(hw); @@ -1964,7 +1963,7 @@ static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta) struct mt7996_sta_link *msta_link; u32 *changed = data; - msta_link = rcu_dereference(msta->link[msta->deflink_id]); + msta_link = mt7996_sta_link(msta, msta->deflink_id); if (msta_link) mt7996_link_rate_ctrl_update(&changed, msta_link); } @@ -2014,7 +2013,7 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, if (!link) continue; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -2052,7 +2051,7 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, if (!link) continue; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -2392,7 +2391,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, if (!link) return -EIO; - msta_link = rcu_dereference(msta->link[msta->deflink_id]); + msta_link = mt7996_sta_link(msta, msta->deflink_id); if (!msta_link) return -EIO; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 6aaf3ed94221..8be40d60ad29 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1136,7 +1136,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, struct mt7996_sta_link *msta_link; int link_id = link_conf->link_id; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (msta_link) sta_wlan_idx = msta_link->wcid.idx; } @@ -1429,7 +1429,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct mt7996_sta_link *msta_link; struct mt7996_vif_link *link; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -1463,7 +1463,7 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, struct mt7996_sta_link *msta_link; struct mt7996_vif_link *link; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -2205,7 +2205,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta, if (!mlink) goto error_unlock; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (!msta_link) goto error_unlock; @@ -2295,7 +2295,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta, if (!link) goto error_unlock; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (!msta_link) goto error_unlock; @@ -2513,7 +2513,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta, if (!link) goto error_unlock; - msta_link = rcu_dereference(msta->link[link_id]); + msta_link = mt7996_sta_link(msta, link_id); if (!msta_link) goto error_unlock; @@ -2668,7 +2668,7 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, unsigned int link_id; struct tlv *tlv; - msta_link = mt76_dereference(msta->link[msta->deflink_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, msta->deflink_id); if (!msta_link) return; @@ -2682,8 +2682,8 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx); if (nlinks > 1) { - msta_link = mt76_dereference(msta->link[msta->seclink_id], - &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, + msta->seclink_id); if (!msta_link) return; } @@ -2694,7 +2694,7 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, for_each_sta_active_link(vif, sta, link_sta, link_id) { struct mt7996_vif_link *link; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) continue; @@ -2842,7 +2842,7 @@ void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta) if (!link_sta) return; - msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + msta_link = mt7996_sta_link_protected(dev, msta, link_id); if (!msta_link) return; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index bdcf72457954..0dc4198fcf8b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -642,6 +642,25 @@ mt7996_vif_conf_link(struct mt7996_dev *dev, struct ieee80211_vif *vif, link_conf); } +static inline struct mt7996_sta_link * +mt7996_sta_link(struct mt7996_sta *msta, u8 link_id) +{ + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + return rcu_dereference(msta->link[link_id]); +} + +static inline struct mt7996_sta_link * +mt7996_sta_link_protected(struct mt7996_dev *dev, struct mt7996_sta *msta, + u8 link_id) +{ + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + return mt76_dereference(msta->link[link_id], &dev->mt76); +} + #define mt7996_for_each_phy(dev, phy) \ for (int __i = 0; __i < ARRAY_SIZE((dev)->radio_phy); __i++) \ if (((phy) = (dev)->radio_phy[__i]) != NULL) From ac41612e0044fa29cf9bc45b6808dda6d87ac2da Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 24 Mar 2026 15:49:04 +0000 Subject: [PATCH 212/285] wifi: mt76: mt7996: add missing max_remain_on_channel_duration Having this unset breaks remain-on-channel and mgmt TX. Move setting it to mt76 core to keep it in one place. Fixes: 69d54ce7491d0 ("wifi: mt76: mt7996: switch to single multi-radio wiphy") Link: https://patch.msgid.link/20260324154904.2555603-2-nbd@nbd.name Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 2 ++ drivers/net/wireless/mediatek/mt76/mt7615/init.c | 1 - drivers/net/wireless/mediatek/mt76/mt792x_core.c | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 4ae5e4715a9c..dd68776ada28 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -449,6 +449,8 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL); + if (!wiphy->max_remain_on_channel_duration) + wiphy->max_remain_on_channel_duration = 5000; if (!wiphy->available_antennas_tx) wiphy->available_antennas_tx = phy->antenna_mask; if (!wiphy->available_antennas_rx) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c index 42e11ba1206e..e437e088b2e9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c @@ -195,7 +195,6 @@ mt7615_check_offload_capability(struct mt7615_dev *dev) ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); wiphy->flags &= ~WIPHY_FLAG_4ADDR_STATION; - wiphy->max_remain_on_channel_duration = 5000; wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 152cfcca2f90..5a5d7534830b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -657,7 +657,6 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); - wiphy->max_remain_on_channel_duration = 5000; wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; wiphy->max_sched_scan_plan_interval = From 291b067a02b9c1fd272057ebf47c365260955fd2 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Wed, 27 May 2026 07:47:35 -0700 Subject: [PATCH 213/285] wifi: mt76: mt7925: add Netgear A8500 USB device ID Add USB device ID for the Netgear A8500 (0846:9050) which uses the mt7925 chipset. Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260527144735.10254-1-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index 84bcebbf009a..7071b6c93d14 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -12,6 +12,9 @@ static const struct usb_device_id mt7925u_device_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7925, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + /* Netgear, Inc. A8500 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9050, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, /* Netgear, Inc. A9000 */ { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9072, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, From 6794edb55b5f5ef834e03b0b241b1a8b725f82c0 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 18 Apr 2026 20:04:16 +0200 Subject: [PATCH 214/285] wifi: mt76: mt7996: Fix NULL pointer dereference in mt7996_init_tx_queues() When MT76_NPU and CONFIG_NET_MEDIATEK_SOC_WED are enabled and mt76 detects properly the Airoha NPU SoC, mt7996_init_tx_queues() will dereference a NULL WED pointer. Fix the issue by always passing the WED pointer from mt7996_dma_init(). Fixes: cd7951f242a7 ("wifi: mt76: mt7996: Integrate MT7990 dma configuration for NPU") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260418-mt7996-dma-init-npu-fix-v1-1-6b8dcffbcb57@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c index 8f5d297dafce..3d9353811a02 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c @@ -683,7 +683,7 @@ int mt7996_dma_init(struct mt7996_dev *dev) ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(0), MT7996_NPU_TX_RING_SIZE, MT_TXQ_RING_BASE(0) + hif1_ofs, - NULL); + wed); else ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(dev->mphy.band_idx), From 9f1accf3069a0cd42c14ca6c7d44d5b17cc48a80 Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:51 -0500 Subject: [PATCH 215/285] wifi: mt76: mt7925: fix stale pointer comparisons in change_vif_links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the error path of mt7925_change_vif_links(), the free: label iterates over link_ids to clean up, but compares against `mconf` and `mlink` which hold stale values from the last loop iteration rather than the current link_id being freed. Use array-indexed access (mconfs[link_id] / mlinks[link_id]) to compare against the correct per-link pointers. Fixes: 69acd6d910b0 ("wifi: mt76: mt7925: add mt7925_change_vif_links") Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index dcd2f07318fc..819fd104aa60 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -2185,9 +2185,9 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rcu_assign_pointer(mvif->link_conf[link_id], NULL); rcu_assign_pointer(mvif->sta.link[link_id], NULL); - if (mconf != &mvif->bss_conf) + if (mconfs[link_id] != &mvif->bss_conf) devm_kfree(dev->mt76.dev, mconfs[link_id]); - if (mlink != &mvif->sta.deflink) + if (mlinks[link_id] != &mvif->sta.deflink) devm_kfree(dev->mt76.dev, mlinks[link_id]); } From efc9a10d1de1763baecd2031c1c6dfe6aac9ac38 Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:52 -0500 Subject: [PATCH 216/285] wifi: mt76: mt7925: add 320MHz bandwidth to bss_rlm_tlv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bss_rlm_tlv() in mt7925_mcu_bss_rlm_tlv() has no case for NL80211_CHAN_WIDTH_320. When associated to a 320MHz BSS, the switch falls through to default and sends bw=0 (CMD_CBW_20MHZ) to firmware via BSS_RLM TLV. Firmware then configures the RX radio for 20MHz and cannot decode the AP's 320MHz frames, resulting in complete data path failure at 320MHz. Add the missing NL80211_CHAN_WIDTH_320 case with CMD_CBW_320MHZ and center_chan2. Tested on ASUS RT-BE92U: 320MHz throughput goes from 0 Mbps to 841 Mbps (iperf3 -t30 -P8), PHY 4803 Mbps EHT-MCS11. Reported-by: 张旭涵 Closes: https://github.com/openwrt/mt76/issues/927 Tested-by: 张旭涵 Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-3-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 333cacfaca9c..d8a956967c0c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2339,6 +2339,10 @@ void mt7925_mcu_bss_rlm_tlv(struct sk_buff *skb, struct mt76_phy *phy, case NL80211_CHAN_WIDTH_160: req->bw = CMD_CBW_160MHZ; break; + case NL80211_CHAN_WIDTH_320: + req->bw = CMD_CBW_320MHZ; + req->center_chan2 = ieee80211_frequency_to_channel(freq2); + break; case NL80211_CHAN_WIDTH_5: req->bw = CMD_CBW_5MHZ; break; From 2237b7b332990f135c1f1550b45ac77b29bcbf7b Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:53 -0500 Subject: [PATCH 217/285] wifi: mt76: mt7925: handle 320MHz bandwidth in RXV and TXS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RX vector (RXV) and TX status (TXS) parsing in mac.c lack handling for 320MHz channel width. When the hardware reports 320MHz in the bandwidth field, mt7925_mac_fill_rx_rate() returns -EINVAL and mt7925_mac_add_txs_skb() records no bandwidth stats. Add IEEE80211_STA_RX_BW_320 cases to both functions. The RXV parser also handles BW_320+1 since the hardware can report 320MHz in two adjacent encoding positions. Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-4-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index c47bd812b66b..d681005cc6ff 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -339,6 +339,11 @@ mt7925_mac_fill_rx_rate(struct mt792x_dev *dev, case IEEE80211_STA_RX_BW_160: status->bw = RATE_INFO_BW_160; break; + /* RXV can report 320 in two positions */ + case IEEE80211_STA_RX_BW_320: + case IEEE80211_STA_RX_BW_320 + 1: + status->bw = RATE_INFO_BW_320; + break; default: return -EINVAL; } @@ -997,6 +1002,10 @@ mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid, stats->tx_mode[mode]++; switch (FIELD_GET(MT_TXS0_BW, txs)) { + case IEEE80211_STA_RX_BW_320: + rate.bw = RATE_INFO_BW_320; + stats->tx_bw[4]++; + break; case IEEE80211_STA_RX_BW_160: rate.bw = RATE_INFO_BW_160; stats->tx_bw[3]++; From e7bbd3d8b4f01473676da3aa8be17099e1604459 Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:54 -0500 Subject: [PATCH 218/285] wifi: mt76: mt7925: populate EHT 320MHz MCS map in sta_rec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sta_rec_eht structure has a mcs_map_bw320 field, and the channel width mapping includes NL80211_CHAN_WIDTH_320, but the 320MHz MCS/NSS map was never copied from the station's EHT capabilities to the MCU TLV. This prevents negotiation of 320MHz channel width even when both the hardware and firmware advertise support for it. Add the missing memcpy for the 320MHz MCS map, matching the existing pattern for BW20, BW80, and BW160. Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-5-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index d8a956967c0c..db6df9da5b5d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -1672,6 +1672,7 @@ mt7925_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, sizeof(eht->mcs_map_bw20)); memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80)); memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160)); + memcpy(eht->mcs_map_bw320, &mcs_map->bw._320, sizeof(eht->mcs_map_bw320)); } static void From 77833c57a33450c0409e4b90d7721d255ea23a9e Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:55 -0500 Subject: [PATCH 219/285] wifi: mt76: mt7925: advertise EHT 320MHz capabilities for 6GHz band MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mt7925_init_eht_caps() only populates EHT MCS/NSS maps for BW <= 80 and BW = 160, but never sets BW = 320. This means iw phy shows no 320MHz MCS map entries even though the hardware supports 320MHz operation in the 6GHz band. Add the missing 320MHz capability bits for 6GHz: - PHY_CAP0: IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ - PHY_CAP1: beamformee SS for 320MHz - PHY_CAP2: sounding dimensions for 320MHz - PHY_CAP6: MCS15 support for 320MHz width - MCS/NSS: populate bw._320 maps for 6GHz band Introduce is_320mhz_supported() to gate 320MHz on MT7927 only, since MT7925 does not support 320MHz operation. Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-6-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt76_connac.h | 5 +++++ .../net/wireless/mediatek/mt76/mt7925/main.c | 22 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 51423c7740bd..802cb26ed6e3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -177,6 +177,11 @@ static inline bool is_mt7925(struct mt76_dev *dev) return mt76_chip(dev) == 0x7925; } +static inline bool is_320mhz_supported(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7927; +} + static inline bool is_mt7920(struct mt76_dev *dev) { return mt76_chip(dev) == 0x7920; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 819fd104aa60..5c4bf6126a03 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -183,6 +183,10 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE; + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[0] |= + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + eht_cap_elem->phy_cap_info[0] |= u8_encode_bits(u8_get_bits(sts - 1, BIT(0)), IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK); @@ -193,10 +197,20 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK); + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[1] |= + u8_encode_bits(sts - 1, + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK); + eht_cap_elem->phy_cap_info[2] = u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK) | u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK); + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[2] |= + u8_encode_bits(sts - 1, + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK); + eht_cap_elem->phy_cap_info[3] = IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | @@ -217,7 +231,8 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, u8_encode_bits(u8_get_bits(0x11, GENMASK(1, 0)), IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK); - val = width == NL80211_CHAN_WIDTH_160 ? 0x7 : + val = width == NL80211_CHAN_WIDTH_320 ? 0xf : + width == NL80211_CHAN_WIDTH_160 ? 0x7 : width == NL80211_CHAN_WIDTH_80 ? 0x3 : 0x1; eht_cap_elem->phy_cap_info[6] = u8_encode_bits(u8_get_bits(0x11, GENMASK(4, 2)), @@ -239,6 +254,11 @@ mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, eht_nss->bw._160.rx_tx_mcs9_max_nss = val; eht_nss->bw._160.rx_tx_mcs11_max_nss = val; eht_nss->bw._160.rx_tx_mcs13_max_nss = val; + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) { + eht_nss->bw._320.rx_tx_mcs9_max_nss = val; + eht_nss->bw._320.rx_tx_mcs11_max_nss = val; + eht_nss->bw._320.rx_tx_mcs13_max_nss = val; + } } int mt7925_init_mlo_caps(struct mt792x_phy *phy) From 752003c6d9b8531a5f9005d42821ccc24b553fbc Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:56 -0500 Subject: [PATCH 220/285] wifi: mt76: mt7925: add MT7927 chip ID helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MediaTek MT7927 (Filogic 380) combo chip uses MT7927 WiFi silicon that is architecturally compatible with MT7925. Extend is_mt7925() to match chip ID 0x7927, and add is_mt7927() for code paths that need MT7927-specific handling. Also add 0x7927 to is_mt76_fw_txp() to match MT7925's TXP format. Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-7-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 802cb26ed6e3..45479cc29134 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -174,7 +174,12 @@ extern const struct wiphy_wowlan_support mt76_connac_wowlan_support; static inline bool is_mt7925(struct mt76_dev *dev) { - return mt76_chip(dev) == 0x7925; + return mt76_chip(dev) == 0x7925 || mt76_chip(dev) == 0x7927; +} + +static inline bool is_mt7927(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7927; } static inline bool is_320mhz_supported(struct mt76_dev *dev) @@ -284,6 +289,7 @@ static inline bool is_mt76_fw_txp(struct mt76_dev *dev) case 0x7922: case 0x7902: case 0x7925: + case 0x7927: case 0x7663: case 0x7622: return false; From 3be5a0d417da36ce7445a3738c9c161aaf769123 Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:57 -0500 Subject: [PATCH 221/285] wifi: mt76: mt7925: add MT7927 firmware paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add firmware path definitions for MT7927 WiFi firmware (WIFI_RAM_CODE and PATCH_MCU) and the corresponding MODULE_FIRMWARE declarations. Add MT7927 cases to mt792x_ram_name() and mt792x_patch_name() so the driver loads the correct firmware for the 0x7927 chip ID. PCI device table entries are deferred to a later patch to allow infrastructure setup before device enablement. Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-8-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 2 ++ drivers/net/wireless/mediatek/mt76/mt792x.h | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 48837723024d..76139a548785 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -635,6 +635,8 @@ module_pci_driver(mt7925_pci_driver); MODULE_DEVICE_TABLE(pci, mt7925_pci_device_table); MODULE_FIRMWARE(MT7925_FIRMWARE_WM); MODULE_FIRMWARE(MT7925_ROM_PATCH); +MODULE_FIRMWARE(MT7927_FIRMWARE_WM); +MODULE_FIRMWARE(MT7927_ROM_PATCH); MODULE_AUTHOR("Deren Wu "); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_DESCRIPTION("MediaTek MT7925E (PCIe) wireless driver"); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 4ff93f2cd624..09840483fe2a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -46,12 +46,14 @@ #define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin" #define MT7922_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7922_1.bin" #define MT7925_FIRMWARE_WM "mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin" +#define MT7927_FIRMWARE_WM "mediatek/mt7927/WIFI_RAM_CODE_MT6639_2_1.bin" #define MT7902_ROM_PATCH "mediatek/WIFI_MT7902_patch_mcu_1_1_hdr.bin" #define MT7920_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1a_2_hdr.bin" #define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin" #define MT7922_ROM_PATCH "mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin" #define MT7925_ROM_PATCH "mediatek/mt7925/WIFI_MT7925_PATCH_MCU_1_1_hdr.bin" +#define MT7927_ROM_PATCH "mediatek/mt7927/WIFI_MT6639_PATCH_MCU_2_1_hdr.bin" #define MT792x_SDIO_HDR_TX_BYTES GENMASK(15, 0) #define MT792x_SDIO_HDR_PKT_TYPE GENMASK(17, 16) @@ -459,6 +461,8 @@ static inline char *mt792x_ram_name(struct mt792x_dev *dev) return MT7922_FIRMWARE_WM; case 0x7925: return MT7925_FIRMWARE_WM; + case 0x7927: + return MT7927_FIRMWARE_WM; default: return MT7921_FIRMWARE_WM; } @@ -475,6 +479,8 @@ static inline char *mt792x_patch_name(struct mt792x_dev *dev) return MT7922_ROM_PATCH; case 0x7925: return MT7925_ROM_PATCH; + case 0x7927: + return MT7927_ROM_PATCH; default: return MT7921_ROM_PATCH; } From 4a91a8c30bf409c06d56017e09f4fc58c5d044ce Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:58 -0500 Subject: [PATCH 222/285] wifi: mt76: mt7925: use irq_map for chip-specific interrupt handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mac_reset and resume paths use the hardcoded MT_INT_RX_DONE_ALL constant (bits 0-2) to re-enable RX interrupts. This is correct for MT7925 (RX rings 0, 1, 2) but wrong for chips using different ring indices. Define a per-chip irq_map with the correct RX interrupt enable bits and replace hardcoded MT_INT_RX_DONE_ALL with irq_map field reads in the resume and mac_reset paths. Add the MT7927 irq_map with interrupt bits matching its RX ring layout (rings 4, 6, 7), selected at probe time based on PCI device ID. This ensures the correct interrupt bits are enabled regardless of the chip variant. Tested-by: Marcin FM Tested-by: Cristian-Florin Radoi Tested-by: George Salukvadze Tested-by: Evgeny Kapusta <3193631@gmail.com> Tested-by: Samu Toljamo Tested-by: Ariel Rosenfeld Tested-by: Chapuis Dario Tested-by: Thibaut François Tested-by: 张旭涵 Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-9-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/pci.c | 21 +++++++++++++++++-- .../wireless/mediatek/mt76/mt7925/pci_mac.c | 5 ++++- .../net/wireless/mediatek/mt76/mt792x_regs.h | 3 +++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 76139a548785..7e0d0e247fa3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -266,6 +266,18 @@ static int mt7925_dma_init(struct mt792x_dev *dev) return mt792x_dma_enable(dev); } +static const struct mt792x_irq_map mt7927_irq_map = { + .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, + .tx = { + .all_complete_mask = MT_INT_TX_DONE_ALL, + .mcu_complete_mask = MT_INT_TX_DONE_MCU, + }, + .rx = { + .data_complete_mask = MT7927_RX_DONE_INT_ENA4, + .wm_complete_mask = MT7927_RX_DONE_INT_ENA6, + .wm2_complete_mask = MT7927_RX_DONE_INT_ENA7, + }, +}; static int mt7925_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -310,6 +322,7 @@ static int mt7925_pci_probe(struct pci_dev *pdev, struct mt76_bus_ops *bus_ops; struct mt792x_dev *dev; struct mt76_dev *mdev; + bool is_mt7927_hw; u8 features; int ret; u16 cmd; @@ -358,7 +371,8 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &mt7925_pcie_ops; - dev->irq_map = &irq_map; + is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927); + dev->irq_map = is_mt7927_hw ? &mt7927_irq_map : &irq_map; mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); @@ -551,7 +565,10 @@ static int _mt7925_pci_resume(struct device *device, bool restore) mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); mt76_connac_irq_enable(&dev->mt76, dev->irq_map->tx.all_complete_mask | - MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD); + dev->irq_map->rx.data_complete_mask | + dev->irq_map->rx.wm_complete_mask | + dev->irq_map->rx.wm2_complete_mask | + MT_INT_MCU_CMD); mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); /* put dma enabled */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c index 3072850c2752..1626a3684082 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c @@ -118,7 +118,10 @@ int mt7925e_mac_reset(struct mt792x_dev *dev) mt76_wr(dev, dev->irq_map->host_irq_enable, dev->irq_map->tx.all_complete_mask | - MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD); + dev->irq_map->rx.data_complete_mask | + dev->irq_map->rx.wm_complete_mask | + dev->irq_map->rx.wm2_complete_mask | + MT_INT_MCU_CMD); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); err = mt792xe_mcu_fw_pmctrl(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h index d2a8b2b0df32..20d27ae89aca 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -310,6 +310,9 @@ #define HOST_RX_DONE_INT_ENA1 BIT(1) #define HOST_RX_DONE_INT_ENA2 BIT(2) #define HOST_RX_DONE_INT_ENA3 BIT(3) +#define MT7927_RX_DONE_INT_ENA4 BIT(12) +#define MT7927_RX_DONE_INT_ENA6 BIT(14) +#define MT7927_RX_DONE_INT_ENA7 BIT(15) #define HOST_TX_DONE_INT_ENA0 BIT(4) #define HOST_TX_DONE_INT_ENA1 BIT(5) #define HOST_TX_DONE_INT_ENA2 BIT(6) From b0dddf55af8147699182d002af1f7ff017dc5cfd Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Sat, 25 Apr 2026 14:49:59 -0500 Subject: [PATCH 223/285] wifi: mt76: mt7925: disable ASPM and runtime PM for MT7927 Disable PCIe ASPM unconditionally for MT7927. The CONNINFRA power domain and WFDMA register access are unreliable with PCIe L1 active, causing throughput to drop from 1+ Gbps to ~200 Mbps. Disable runtime PM and deep sleep for MT7927. The combo chip shares a CONNINFRA power domain between WiFi (PCIe) and BT (USB). SET_OWN/CLR_OWN transitions on the LPCTL register crash the BT firmware, requiring a full power cycle to recover. PM enablement will be addressed in a follow-up once safe power state transitions are determined. Reviewed-by: Sean Wang Signed-off-by: Javier Tia Link: https://patch.msgid.link/20260425195011.790265-10-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/init.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index c0c5cb9aff75..a8c2ca7c0efc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -232,7 +232,8 @@ int mt7925_register_device(struct mt792x_dev *dev) dev->pm.idle_timeout = MT792x_PM_TIMEOUT; dev->pm.stats.last_wake_event = jiffies; dev->pm.stats.last_doze_event = jiffies; - if (!mt76_is_usb(&dev->mt76)) { + /* MT7927: runtime PM crashes BT firmware on the shared CONNINFRA domain */ + if (!mt76_is_usb(&dev->mt76) && !is_mt7927(&dev->mt76)) { dev->pm.enable_user = true; dev->pm.enable = true; dev->pm.ds_enable_user = true; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 7e0d0e247fa3..3e2afa0dac74 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -350,7 +350,10 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (ret) goto err_free_pci_vec; - if (mt7925_disable_aspm) + is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927); + + /* MT7927: ASPM L1 causes unreliable WFDMA register access */ + if (mt7925_disable_aspm || is_mt7927_hw) mt76_pci_disable_aspm(pdev); ops = mt792x_get_mac80211_ops(&pdev->dev, &mt7925_ops, @@ -371,7 +374,6 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &mt7925_pcie_ops; - is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927); dev->irq_map = is_mt7927_hw ? &mt7927_irq_map : &irq_map; mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); From 60dac6072dbcb35e077906c43e58ed71436af992 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:00 -0500 Subject: [PATCH 224/285] wifi: mt76: connac: replace is_mt7925() with is_connac3() Use is_connac3() instead of is_mt7925() to avoid confusion when more chips join. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-11-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac.h | 7 ++++++- drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 4 ++-- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 4 ++-- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 2 +- drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 45479cc29134..1994863dc59d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -172,11 +172,16 @@ struct mt76_connac_tx_free { extern const struct wiphy_wowlan_support mt76_connac_wowlan_support; -static inline bool is_mt7925(struct mt76_dev *dev) +static inline bool is_connac3(struct mt76_dev *dev) { return mt76_chip(dev) == 0x7925 || mt76_chip(dev) == 0x7927; } +static inline bool is_mt7925(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7925; +} + static inline bool is_mt7927(struct mt76_dev *dev) { return mt76_chip(dev) == 0x7927; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index 0339e2e7ab60..c341595e9138 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -173,7 +173,7 @@ void mt76_connac_write_hw_txp(struct mt76_dev *dev, txp->msdu_id[0] = cpu_to_le16(id | MT_MSDU_ID_VALID); - if (is_mt7663(dev) || is_connac2(dev) || is_mt7925(dev)) + if (is_mt7663(dev) || is_connac2(dev) || is_connac3(dev)) last_mask = MT_TXD_LEN_LAST; else last_mask = MT_TXD_LEN_AMSDU_LAST | @@ -217,7 +217,7 @@ mt76_connac_txp_skb_unmap_hw(struct mt76_dev *dev, u32 last_mask; int i; - if (is_mt7663(dev) || is_connac2(dev) || is_mt7925(dev)) + if (is_mt7663(dev) || is_connac2(dev) || is_connac3(dev)) last_mask = MT_TXD_LEN_LAST; else last_mask = MT_TXD_LEN_MSDU_LAST; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index f61d0625ef51..ff884697d62f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -67,7 +67,7 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || (is_connac2(dev) && addr == 0x900000) || - (is_mt7925(dev) && (addr == 0x900000 || addr == 0xe0002800)) || + (is_connac3(dev) && (addr == 0x900000 || addr == 0xe0002800)) || (is_mt799x(dev) && addr == 0x900000)) cmd = MCU_CMD(PATCH_START_REQ); else @@ -3089,7 +3089,7 @@ static u32 mt76_connac2_get_data_mode(struct mt76_dev *dev, u32 info) { u32 mode = DL_MODE_NEED_RSP; - if ((!is_connac2(dev) && !is_mt7925(dev)) || info == PATCH_SEC_NOT_SUPPORT) + if ((!is_connac2(dev) && !is_connac3(dev)) || info == PATCH_SEC_NOT_SUPPORT) return mode; switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index ac5126ab68ff..552cb94edaa0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1878,7 +1878,7 @@ mt76_connac_mcu_gen_dl_mode(struct mt76_dev *dev, u8 feature_set, bool is_wa) ret |= feature_set & FW_FEATURE_SET_ENCRYPT ? DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV : 0; - if (is_connac2(dev) || is_mt7925(dev)) + if (is_connac2(dev) || is_connac3(dev)) ret |= feature_set & FW_FEATURE_ENCRY_MODE ? DL_CONFIG_ENCRY_MODE_SEL : 0; ret |= FIELD_PREP(DL_MODE_KEY_IDX, diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 0d9dbd0aaf9e..be043e964c3c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -263,7 +263,7 @@ EXPORT_SYMBOL_GPL(mt792xu_dma_init); int mt792xu_wfsys_reset(struct mt792x_dev *dev) { - const struct mt792xu_wfsys_desc *desc = is_mt7925(&dev->mt76) ? + const struct mt792xu_wfsys_desc *desc = is_connac3(&dev->mt76) ? &mt7925_wfsys_desc : &mt7921_wfsys_desc; u32 val; From b2f5def2f133fca12897b0fc707a8d3df1c3cb05 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:01 -0500 Subject: [PATCH 225/285] wifi: mt76: mt7925: use link-specific removal for non-MLD STA Use the default link removal path for non-MLD STA instead of the multi-link flow. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-12-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 5c4bf6126a03..74d65497cf9b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1344,14 +1344,14 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - unsigned long rem; - rem = ieee80211_vif_is_mld(vif) ? msta->valid_links : BIT(0); - - mt7925_mac_sta_remove_links(dev, vif, sta, rem); - - if (ieee80211_vif_is_mld(vif)) + if (ieee80211_vif_is_mld(vif)) { + mt7925_mac_sta_remove_links(dev, vif, sta, msta->valid_links); mt7925_mcu_del_dev(mdev, vif); + } else { + mt7925_mac_link_sta_remove(mdev, vif, &sta->deflink, + &msta->deflink); + } if (vif->type == NL80211_IFTYPE_STATION) { mvif->wep_sta = NULL; From 04222f980301561edd77917edac570b1393ec45d Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:02 -0500 Subject: [PATCH 226/285] wifi: mt76: connac: tolerate inactive BSS deactivation Firmware may return wlan_failure when deactivating a BSS that is already inactive. This is a valid teardown case and should not fail the remove path. Keep activation failures unchanged since they still indicate that firmware failed to create or activate the BSS state. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-13-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.c | 9 +++++++- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 23 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/mcu.c | 6 +++-- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index ff884697d62f..be0ec1eaa746 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1232,6 +1232,9 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, len = enable ? sizeof(dev_req) : sizeof(basic_req); err = mt76_mcu_send_msg(dev, cmd, data, len, true); + if (err && cmd == MCU_UNI_CMD(BSS_INFO_UPDATE)) + err = mt76_connac_mcu_bss_deact_err(dev, err, enable); + if (err < 0) return err; @@ -1239,7 +1242,11 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, data = enable ? (void *)&basic_req : (void *)&dev_req; len = enable ? sizeof(basic_req) : sizeof(dev_req); - return mt76_mcu_send_msg(dev, cmd, data, len, true); + err = mt76_mcu_send_msg(dev, cmd, data, len, true); + if (err && cmd == MCU_UNI_CMD(BSS_INFO_UPDATE)) + err = mt76_connac_mcu_bss_deact_err(dev, err, enable); + + return err; } EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 552cb94edaa0..4691b9b5e2be 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1906,6 +1906,29 @@ mt76_connac_mcu_get_wlan_idx(struct mt76_dev *dev, struct mt76_wcid *wcid, } } +#define MT76_CONNAC_MCU_STATUS_WLAN_FAILURE 0xc0000001 + +static inline int +mt76_connac_mcu_bss_deact_err(struct mt76_dev *mdev, int err, bool enable) +{ + if (err != (int)MT76_CONNAC_MCU_STATUS_WLAN_FAILURE) + return err; + + /* Ignore wlan_failure state false alarm when deactivating an + * inactive network. It does not harm the firmware state. + */ + if (!enable) { + dev_dbg(mdev->dev, + "ignore wlan_failure when bss is deactivated\n"); + return 0; + } + + dev_warn(mdev->dev, + "wlan_failure when bss is activated\n"); + + return err; +} + struct sk_buff * __mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, struct mt76_wcid *wcid, int len); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index db6df9da5b5d..9a1891c653c0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2837,6 +2837,7 @@ int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt792x_dev *dev = phy->dev; struct sk_buff *skb; + int err; skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, MT7925_BSS_UPDATE_MAX_SIZE); @@ -2862,8 +2863,9 @@ int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy, mt7925_mcu_bss_mbssid_tlv(skb, link_conf, enable); } - return mt76_mcu_skb_send_msg(&dev->mt76, skb, - MCU_UNI_CMD(BSS_INFO_UPDATE), true); + err = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(BSS_INFO_UPDATE), true); + return mt76_connac_mcu_bss_deact_err(&dev->mt76, err, enable); } int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, From c08ffdaf32f7b68d81aebf42d5bb222587a35c76 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:03 -0500 Subject: [PATCH 227/285] wifi: mt76: mt792x: add MT7927 WFSYS reset support Add a dedicated MT7927 WFSYS reset path in mt792x_wfsys_reset(). Unlike the existing connac2/connac3 reset flow that toggles the WFSYS software reset bit and waits for init-done, MT7927 reset is driven through CBInfra and requires polling ROMCODE_INDEX until the MCU returns to the idle value after reset. Keep this dormant for now: no MT7927 PCI IDs are added by this patch, so it only prepares the reset logic without making the driver bind to MT7927 hardware yet. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-14-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt792x_dma.c | 57 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt792x_regs.h | 11 ++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 002aece857b2..2835bf273154 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -370,7 +370,54 @@ int mt792x_poll_rx(struct napi_struct *napi, int budget) } EXPORT_SYMBOL_GPL(mt792x_poll_rx); -int mt792x_wfsys_reset(struct mt792x_dev *dev) +static void mt7927_sema_status_read(struct mt792x_dev *dev, u32 addr) +{ + u32 remap; + + remap = mt76_rr(dev, MT7927_PCIE2AP_REMAP_WF_0_54); + mt76_wr(dev, MT7927_PCIE2AP_REMAP_WF_0_54, + (remap & ~MT7927_PCIE2AP_REMAP_WF_0_54_MASK) | + MT7927_PCIE2AP_REMAP_WF_0_54_VAL); + usleep_range(10, 20); + + mt76_rr(dev, addr); + + mt76_wr(dev, MT7927_PCIE2AP_REMAP_WF_0_54, remap); + usleep_range(10, 20); +} + +static int mt7927_wfsys_reset(struct mt792x_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 val; + + mt7927_sema_status_read(dev, MT7927_SEMA_OWN_STA); + + mt76_set(dev, MT7927_CBINFRA_RGU_WF_RST, + MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS); + usleep_range(1000, 2000); + + mt76_clear(dev, MT7927_CBINFRA_RGU_WF_RST, + MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS); + usleep_range(5000, 10000); + + mt76_wr(dev, MT7927_CBINFRA_MCU_OWN_SET, BIT(0)); + + if (!__mt76_poll_msec(mdev, MT7927_ROMCODE_INDEX, 0xffff, + MT7927_MCU_IDLE_VALUE, 200)) { + val = mt76_rr(dev, MT7927_ROMCODE_INDEX); + dev_err(mdev->dev, + "MT7927 WFSYS reset timeout (ROMCODE_INDEX=0x%04x)\n", + val & 0xffff); + return -ETIMEDOUT; + } + + mt7927_sema_status_read(dev, MT7927_SEMA_OWN_STA_REP); + + return 0; +} + +static int mt792x_wfsys_reset_default(struct mt792x_dev *dev) { u32 addr = is_connac2(&dev->mt76) ? 0x18000140 : 0x7c000140; @@ -384,5 +431,13 @@ int mt792x_wfsys_reset(struct mt792x_dev *dev) return 0; } + +int mt792x_wfsys_reset(struct mt792x_dev *dev) +{ + if (is_mt7927(&dev->mt76)) + return mt7927_wfsys_reset(dev); + + return mt792x_wfsys_reset_default(dev); +} EXPORT_SYMBOL_GPL(mt792x_wfsys_reset); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h index 20d27ae89aca..17504ef8e80d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -493,4 +493,15 @@ #define WFSYS_SW_RST_B BIT(0) #define WFSYS_SW_INIT_DONE BIT(4) +/* CBInfra registers - MT7927 combo chip */ +#define MT7927_CBINFRA_RGU_WF_RST 0x1f8600 +#define MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS BIT(4) +#define MT7927_CBINFRA_MCU_OWN_SET 0x1f5034 +#define MT7927_ROMCODE_INDEX 0xc1604 +#define MT7927_MCU_IDLE_VALUE 0x1d1e +#define MT7927_PCIE2AP_REMAP_WF_0_54 0x21008 +#define MT7927_PCIE2AP_REMAP_WF_0_54_MASK GENMASK(15, 0) +#define MT7927_PCIE2AP_REMAP_WF_0_54_VAL 0x00001807 +#define MT7927_SEMA_OWN_STA 0x40000 +#define MT7927_SEMA_OWN_STA_REP 0x40400 #endif /* __MT792X_REGS_H */ From 1e570aa871f0ce535770a9390b833dc865c28d59 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:04 -0500 Subject: [PATCH 228/285] wifi: mt76: mt792x: factor out common DMA queue allocation The mt792x PCIe DMA setup uses the same standard queue allocation sequence for data, MCU, firmware download and RX rings. Factor this part out into a small common helper so later chip support can reuse the existing flow without duplicating the queue setup logic. This keeps the common DMA skeleton in one place and makes follow-up chip-specific DMA changes smaller and easier to review. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-15-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x.h | 19 +++++++ .../net/wireless/mediatek/mt76/mt792x_dma.c | 56 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 09840483fe2a..59562567cc13 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -206,6 +206,23 @@ struct mt792x_irq_map { } rx; }; +struct mt792x_dma_ring { + u8 qid; + u16 n_desc; + u32 ring_base; +}; + +struct mt792x_dma_layout { + struct mt792x_dma_ring tx_data0; + struct mt792x_dma_ring tx_mcu; + struct mt792x_dma_ring tx_fwdl; + struct mt792x_dma_ring rx_data; + struct mt792x_dma_ring rx_mcu; +}; + +#define mt792x_dma_ring(_qid, _n_desc, _ring_base) \ + { .qid = (_qid), .n_desc = (_n_desc), .ring_base = (_ring_base) } + #define mt792x_init_reset(dev) ((dev)->hif_ops->init_reset(dev)) #define mt792x_dev_reset(dev) ((dev)->hif_ops->reset(dev)) #define mt792x_mcu_init(dev) ((dev)->hif_ops->mcu_init(dev)) @@ -421,6 +438,8 @@ void mt792x_sta_statistics(struct ieee80211_hw *hw, void mt792x_set_coverage_class(struct ieee80211_hw *hw, int radio_idx, s16 coverage_class); void mt792x_dma_cleanup(struct mt792x_dev *dev); +int mt792x_dma_alloc_queues(struct mt792x_dev *dev, + const struct mt792x_dma_layout *layout); int mt792x_dma_enable(struct mt792x_dev *dev); int mt792x_wpdma_reset(struct mt792x_dev *dev, bool force); int mt792x_wpdma_reinit_cond(struct mt792x_dev *dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 2835bf273154..c52d0c85913c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -87,6 +87,62 @@ void mt792x_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) } EXPORT_SYMBOL_GPL(mt792x_rx_poll_complete); +int mt792x_dma_alloc_queues(struct mt792x_dev *dev, + const struct mt792x_dma_layout *layout) +{ + int ret; + + mt76_dma_attach(&dev->mt76); + + ret = mt792x_dma_disable(dev, true); + if (ret) + return ret; + + /* init tx queue */ + ret = mt76_connac_init_tx_queues(dev->phy.mt76, layout->tx_data0.qid, + layout->tx_data0.n_desc, + layout->tx_data0.ring_base, + NULL, 0); + if (ret) + return ret; + + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4); + + /* command to WM */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, + layout->tx_mcu.qid, + layout->tx_mcu.n_desc, + layout->tx_mcu.ring_base); + if (ret) + return ret; + + /* firmware download */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, + layout->tx_fwdl.qid, + layout->tx_fwdl.n_desc, + layout->tx_fwdl.ring_base); + if (ret) + return ret; + + /* rx event */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], + layout->rx_mcu.qid, + layout->rx_mcu.n_desc, + MT_RX_BUF_SIZE, + layout->rx_mcu.ring_base); + if (ret) + return ret; + + /* rx data */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + layout->rx_data.qid, + layout->rx_data.n_desc, + MT_RX_BUF_SIZE, + layout->rx_data.ring_base); + return ret; +} +EXPORT_SYMBOL_GPL(mt792x_dma_alloc_queues); + #define PREFETCH(base, depth) ((base) << 16 | (depth)) static void mt792x_dma_prefetch(struct mt792x_dev *dev) { From e1fca65c826b2e4810c8f9fddddd8f284eda7f5c Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:05 -0500 Subject: [PATCH 229/285] wifi: mt76: mt7925: switch DMA init to common mt792x queue helpers Convert mt7925 DMA init to use the common mt792x queue allocation helper and isolate the mt7925-specific prefetch/WFDMA setup. This removes duplicated DMA setup code and prepares the path for follow-up chip-specific DMA support with smaller deltas. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-16-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/pci.c | 57 +++++++------------ .../net/wireless/mediatek/mt76/mt792x_dma.c | 46 +++++++++------ 2 files changed, 48 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 3e2afa0dac74..3888fa0fa522 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -210,48 +210,29 @@ static u32 mt7925_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) return dev->bus_ops->rmw(mdev, addr, mask, val); } +static const struct mt792x_dma_layout mt7925_dma_layout = { + .tx_data0 = mt792x_dma_ring(MT7925_TXQ_BAND0, + MT7925_TX_RING_SIZE, + MT_TX_RING_BASE), + .tx_mcu = mt792x_dma_ring(MT7925_TXQ_MCU_WM, + MT7925_TX_MCU_RING_SIZE, + MT_TX_RING_BASE), + .tx_fwdl = mt792x_dma_ring(MT7925_TXQ_FWDL, + MT7925_TX_FWDL_RING_SIZE, + MT_TX_RING_BASE), + .rx_mcu = mt792x_dma_ring(MT7925_RXQ_MCU_WM, + MT7925_RX_MCU_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_data = mt792x_dma_ring(MT7925_RXQ_BAND0, + MT7925_RX_RING_SIZE, + MT_RX_DATA_RING_BASE), +}; + static int mt7925_dma_init(struct mt792x_dev *dev) { int ret; - mt76_dma_attach(&dev->mt76); - - ret = mt792x_dma_disable(dev, true); - if (ret) - return ret; - - /* init tx queue */ - ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7925_TXQ_BAND0, - MT7925_TX_RING_SIZE, - MT_TX_RING_BASE, NULL, 0); - if (ret) - return ret; - - mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4); - - /* command to WM */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7925_TXQ_MCU_WM, - MT7925_TX_MCU_RING_SIZE, MT_TX_RING_BASE); - if (ret) - return ret; - - /* firmware download */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, MT7925_TXQ_FWDL, - MT7925_TX_FWDL_RING_SIZE, MT_TX_RING_BASE); - if (ret) - return ret; - - /* rx event */ - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], - MT7925_RXQ_MCU_WM, MT7925_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE); - if (ret) - return ret; - - /* rx data */ - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], - MT7925_RXQ_BAND0, MT7925_RX_RING_SIZE, - MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE); + ret = mt792x_dma_alloc_queues(dev, &mt7925_dma_layout); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index c52d0c85913c..7b36f0761dd1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -144,21 +144,35 @@ int mt792x_dma_alloc_queues(struct mt792x_dev *dev, EXPORT_SYMBOL_GPL(mt792x_dma_alloc_queues); #define PREFETCH(base, depth) ((base) << 16 | (depth)) + +static void mt7925_dma_prefetch_setup(struct mt792x_dev *dev) +{ + /* rx ring */ + mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4)); + + /* tx ring */ + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0200, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0300, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4)); +} + +static void mt7925_wfdma_setup(struct mt792x_dev *dev) +{ + mt76_rmw(dev, MT_UWFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28)); + mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00); + mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); +} + static void mt792x_dma_prefetch(struct mt792x_dev *dev) { if (is_mt7925(&dev->mt76)) { - /* rx ring */ - mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); - mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4)); - mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4)); - mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4)); - /* tx ring */ - mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x10)); - mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0200, 0x10)); - mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0300, 0x10)); - mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10)); - mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4)); - mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4)); + mt7925_dma_prefetch_setup(dev); } else if (is_mt7902(&dev->mt76)) { /* rx ring */ mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); @@ -222,11 +236,9 @@ int mt792x_dma_enable(struct mt792x_dev *dev) mt76_set(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - if (is_mt7925(&dev->mt76)) { - mt76_rmw(dev, MT_UWFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28)); - mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00); - mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); - } + if (is_mt7925(&dev->mt76)) + mt7925_wfdma_setup(dev); + mt76_set(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT); /* enable interrupts for TX/RX rings */ From 0683825c48d9754832dd1e6a1e88946a5e4f2fd3 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:06 -0500 Subject: [PATCH 230/285] wifi: mt76: mt792x: add MT7927-specific PCIe DMA support Add the MT7927-specific PCIe DMA queue layout and local DMA setup, including the extra RX ring and WFDMA/prefetch programming. MT7927 does not follow the mt7925 DMA configuration, so provide a separate DMA init path while reusing the existing common queue setup. This keeps the chip-specific changes local and prepares the driver for later MT7927 enablement without affecting existing devices. Co-developed-by: Javier Tia Signed-off-by: Javier Tia Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-17-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7925/mt7925.h | 13 +++++ .../net/wireless/mediatek/mt76/mt7925/pci.c | 53 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt792x_dma.c | 41 +++++++++++++- .../net/wireless/mediatek/mt76/mt792x_regs.h | 9 ++++ 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 46b480f7d813..984be40ca76d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -126,6 +126,19 @@ enum mt7925_rxq_id { MT7925_RXQ_MCU_WM2, /* for tx done */ }; +enum mt7927_txq_id { + MT7927_TXQ_BAND0 = MT7925_TXQ_BAND0, + MT7927_TXQ_BAND1 = MT7925_TXQ_BAND1, + MT7927_TXQ_MCU_WM = MT7925_TXQ_MCU_WM, + MT7927_TXQ_FWDL = MT7925_TXQ_FWDL, +}; + +enum mt7927_rxq_id { + MT7927_RXQ_BAND0 = 4, + MT7927_RXQ_MCU_WM = 6, + MT7927_RXQ_DATA2 = 7, +}; + enum { MODE_OPEN = 0, MODE_SHARED = 1, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index 3888fa0fa522..fd6d0b029ef4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -228,6 +228,51 @@ static const struct mt792x_dma_layout mt7925_dma_layout = { MT_RX_DATA_RING_BASE), }; +static const struct mt792x_dma_layout mt7927_dma_layout = { + .tx_data0 = mt792x_dma_ring(MT7927_TXQ_BAND0, + MT7925_TX_RING_SIZE, + MT_TX_RING_BASE), + .tx_mcu = mt792x_dma_ring(MT7927_TXQ_MCU_WM, + MT7925_TX_MCU_RING_SIZE, + MT_TX_RING_BASE), + .tx_fwdl = mt792x_dma_ring(MT7927_TXQ_FWDL, + MT7925_TX_FWDL_RING_SIZE, + MT_TX_RING_BASE), + .rx_mcu = mt792x_dma_ring(MT7927_RXQ_MCU_WM, + MT7925_RX_MCU_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_data = mt792x_dma_ring(MT7927_RXQ_BAND0, + MT7925_RX_RING_SIZE, + MT_RX_DATA_RING_BASE), +}; + +static int mt7927_dma_init(struct mt792x_dev *dev) +{ + int ret; + + ret = mt792x_dma_alloc_queues(dev, &mt7927_dma_layout); + if (ret) + return ret; + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + MT7927_RXQ_DATA2, + MT7925_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RX_DATA_RING_BASE); + if (ret) + return ret; + + ret = mt76_init_queues(dev, mt792x_poll_rx); + if (ret < 0) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt792x_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + return mt792x_dma_enable(dev); +} + static int mt7925_dma_init(struct mt792x_dev *dev) { int ret; @@ -406,7 +451,13 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (ret) goto err_free_dev; - ret = mt7925_dma_init(dev); + if (is_mt7927(&dev->mt76)) + ret = mt7927_dma_init(dev); + else if (is_mt7925(&dev->mt76)) + ret = mt7925_dma_init(dev); + else + ret = -EINVAL; + if (ret) goto err_free_irq; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c index 7b36f0761dd1..fc326447c792 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -169,9 +169,39 @@ static void mt7925_wfdma_setup(struct mt792x_dev *dev) mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); } +static void mt7927_dma_prefetch_setup(struct mt792x_dev *dev) +{ + mt76_wr(dev, MT_WFDMA_PREFETCH_CTRL, + mt76_rr(dev, MT_WFDMA_PREFETCH_CTRL)); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG0, 0x660077); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG1, 0x1100); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG2, 0x30004f); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG3, 0x542200); + mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0x0000, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING6_EXT_CTRL, PREFETCH(0x0080, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING7_EXT_CTRL, PREFETCH(0x0100, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0140, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0180, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0280, 0x4)); +} + +static void mt7927_wfdma_setup(struct mt792x_dev *dev) +{ + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_ADDR_EXT_EN | + MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL); + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN); + mt76_rmw(dev, MT_WFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28)); + mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00); + mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); +} + static void mt792x_dma_prefetch(struct mt792x_dev *dev) { - if (is_mt7925(&dev->mt76)) { + if (is_mt7927(&dev->mt76)) { + mt7927_dma_prefetch_setup(dev); + } else if (is_mt7925(&dev->mt76)) { mt7925_dma_prefetch_setup(dev); } else if (is_mt7902(&dev->mt76)) { /* rx ring */ @@ -236,7 +266,9 @@ int mt792x_dma_enable(struct mt792x_dev *dev) mt76_set(dev, MT_WFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - if (is_mt7925(&dev->mt76)) + if (is_mt7927(&dev->mt76)) + mt7927_wfdma_setup(dev); + else if (is_mt7925(&dev->mt76)) mt7925_wfdma_setup(dev); mt76_set(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT); @@ -349,6 +381,11 @@ int mt792x_dma_disable(struct mt792x_dev *dev, bool force) MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 100, 1)) return -ETIMEDOUT; + if (is_mt7927(&dev->mt76)) { + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); + mt76_wr(dev, MT_WFDMA0_RST_DRX_PTR, ~0); + } + /* disable dmashdl */ mt76_clear(dev, MT_WFDMA0_GLO_CFG_EXT0, MT_WFDMA0_CSR_TX_DMASHDL_ENABLE); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h index 17504ef8e80d..4cd5b33b640e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -301,7 +301,9 @@ #define MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12) #define MT_WFDMA0_GLO_CFG_RX_WB_DDONE BIT(13) #define MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN BIT(15) +#define MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN BIT(20) #define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) +#define MT_WFDMA0_GLO_CFG_ADDR_EXT_EN BIT(26) #define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27) #define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28) #define MT_WFDMA0_GLO_CFG_CLK_GAT_DIS BIT(30) @@ -368,6 +370,13 @@ #define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44) #define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0) +#define MT_WFDMA_PREFETCH_CTRL MT_WFDMA_EXT_CSR(0x30) +#define MT_WFDMA_PREFETCH_CFG0 MT_WFDMA_EXT_CSR(0xf0) +#define MT_WFDMA_PREFETCH_CFG1 MT_WFDMA_EXT_CSR(0xf4) +#define MT_WFDMA_PREFETCH_CFG2 MT_WFDMA_EXT_CSR(0xf8) +#define MT_WFDMA_PREFETCH_CFG3 MT_WFDMA_EXT_CSR(0xfc) +#define MT_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4) + #define MT_SWDEF_BASE 0x41f200 #define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs)) #define MT_SWDEF_MODE MT_SWDEF(0x3c) From 05fd148853036ea3b3f688f35872fbd01cdb5892 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:07 -0500 Subject: [PATCH 231/285] wifi: mt76: mt7925: sync MT7927 BSS band assignment MT7927 needs DBDC enabled and uses a fixed firmware band assignment for 2.4GHz and 5/6GHz BSS contexts. Reprogram the STA dev context when the channel context is assigned so the firmware sees the updated band_idx before the BSS is used. This avoids stale band programming after band changes. Co-developed-by: Javier Tia Signed-off-by: Javier Tia Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-18-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/init.c | 9 ++++ .../net/wireless/mediatek/mt76/mt7925/main.c | 42 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/mcu.c | 1 + .../wireless/mediatek/mt76/mt7925/mt7925.h | 12 ++++++ .../wireless/mediatek/mt76/mt7925/pci_mac.c | 7 ++++ 5 files changed, 71 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index a8c2ca7c0efc..e85b0d104fbe 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -117,6 +117,15 @@ static int __mt7925_init_hardware(struct mt792x_dev *dev) if (ret) goto out; + if (is_mt7927(&dev->mt76)) { + ret = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (ret) { + dev_warn(dev->mt76.dev, + "MT7927 DBDC enable failed: %d\n", ret); + ret = 0; + } + } + out: return ret; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 74d65497cf9b..2599b738496f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -396,6 +396,18 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, mconf->mt76.omac_idx = ieee80211_vif_is_mld(vif) ? 0 : mconf->mt76.idx; mconf->mt76.band_idx = 0xff; + + if (is_mt7927(&dev->mt76)) { + struct ieee80211_channel *chan; + + if (link_conf->chanreq.oper.chan) + chan = link_conf->chanreq.oper.chan; + else + chan = mvif->phy->mt76->chandef.chan; + + mconf->mt76.band_idx = mt7927_band_idx(chan->band); + } + mconf->mt76.wmm_idx = ieee80211_vif_is_mld(vif) ? 0 : mconf->mt76.idx % MT76_CONNAC_MAX_WMM_SETS; mconf->mt76.link_idx = hweight16(mvif->valid_links); @@ -2244,6 +2256,29 @@ mt7925_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return err; } +static int +mt7927_reconfig_band(struct mt792x_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt792x_bss_conf *mconf, + u8 band_idx) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_link_sta *mlink = &mvif->sta.deflink; + int ret; + + ret = mt76_connac_mcu_uni_add_dev(&dev->mphy, link_conf, + &mconf->mt76, &mlink->wcid, + false); + if (ret) + return ret; + + mconf->mt76.band_idx = band_idx; + + return mt76_connac_mcu_uni_add_dev(&dev->mphy, link_conf, + &mconf->mt76, &mlink->wcid, + true); +} + static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, @@ -2254,6 +2289,7 @@ static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw, struct mt792x_dev *dev = mt792x_hw_dev(hw); struct ieee80211_bss_conf *pri_link_conf; struct mt792x_bss_conf *mconf; + u8 band_idx; mutex_lock(&dev->mt76.mutex); @@ -2267,6 +2303,12 @@ static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw, NULL, true); } else { mconf = &mvif->bss_conf; + + if (is_mt7927(&dev->mt76)) { + band_idx = mt7927_band_idx(ctx->def.chan->band); + + mt7927_reconfig_band(dev, vif, link_conf, mconf, band_idx); + } } mconf->mt76.ctx = ctx; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 9a1891c653c0..1cd234737adb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -2923,6 +2923,7 @@ int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable) return err; } +EXPORT_SYMBOL_GPL(mt7925_mcu_set_dbdc); static void mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 984be40ca76d..242f83f90dd4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -252,6 +252,18 @@ struct mt7925_txpwr { s8 eht996x3_484[16][2]; }; +static inline u8 mt7927_band_idx(enum nl80211_band band) +{ + switch (band) { + case NL80211_BAND_2GHZ: + return 0; + case NL80211_BAND_5GHZ: + case NL80211_BAND_6GHZ: + default: + return 1; + } +} + extern const struct ieee80211_ops mt7925_ops; int __mt7925_start(struct mt792x_phy *phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c index 1626a3684082..97683949a305 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c @@ -3,6 +3,7 @@ #include "mt7925.h" #include "../dma.h" +#include "mcu.h" #include "mac.h" int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, @@ -144,6 +145,12 @@ int mt7925e_mac_reset(struct mt792x_dev *dev) if (err) goto out; + if (is_mt7927(&dev->mt76)) { + err = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (err) + goto out; + } + err = __mt7925_start(&dev->phy); out: clear_bit(MT76_RESET, &dev->mphy.state); From bcc6e3358a1f946d5f2f8a2547c2a997375100cd Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:08 -0500 Subject: [PATCH 232/285] wifi: mt76: mt7925: add MBMC event handling Handle MBMC unsolicited events from firmware to report the current MBMC state. This helps validate MT7927 band setup. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-19-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + .../net/wireless/mediatek/mt76/mt7925/mcu.c | 31 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/mcu.h | 3 +- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 4691b9b5e2be..6d0429f40b0f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1069,6 +1069,7 @@ enum { MCU_UNI_EVENT_SCAN_DONE = 0x0e, MCU_UNI_EVENT_RDD_REPORT = 0x11, MCU_UNI_EVENT_ROC = 0x27, + MCU_UNI_EVENT_MBMC = 0x28, MCU_UNI_EVENT_TX_DONE = 0x2d, MCU_UNI_EVENT_THERMAL = 0x35, MCU_UNI_EVENT_RSSI_MONITOR = 0x41, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 1cd234737adb..f667eba99a51 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -566,6 +566,34 @@ mt7925_mcu_uni_debug_msg_event(struct mt792x_dev *dev, struct sk_buff *skb) } } +static void +mt7925_mcu_handle_mbmc_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mbmc_conf_tlv *tlv; + u32 tlv_len; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv_len = skb->len; + tlv = (struct mbmc_conf_tlv *)skb->data; + + while (tlv_len >= sizeof(*tlv) && + le16_to_cpu(tlv->len) >= sizeof(*tlv) && + le16_to_cpu(tlv->len) <= tlv_len) { + u16 tag = le16_to_cpu(tlv->tag); + + if (tag == UNI_MBMC_SETTING || tag == UNI_MBMC_NO_RESP_SETTING) { + dev_dbg(dev->mt76.dev, + "MBMC event: tag=%u mbmc_en=%u\n", + tag, tlv->mbmc_en); + break; + } + + tlv_len -= le16_to_cpu(tlv->len); + tlv = (struct mbmc_conf_tlv *) + ((u8 *)tlv + le16_to_cpu(tlv->len)); + } +} + static void mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, struct sk_buff *skb) @@ -584,6 +612,9 @@ mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, case MCU_UNI_EVENT_ROC: mt7925_mcu_uni_roc_event(dev, skb); break; + case MCU_UNI_EVENT_MBMC: + mt7925_mcu_handle_mbmc_event(dev, skb); + break; case MCU_UNI_EVENT_SCAN_DONE: mt7925_mcu_scan_event(dev, skb); return; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index 56e2772f3ffe..293f173b23dd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -143,7 +143,8 @@ enum { }; enum { - UNI_MBMC_SETTING, + UNI_MBMC_SETTING = 0, + UNI_MBMC_NO_RESP_SETTING = 1, }; enum { From 298efd8f9f24727269a95c2f9dd94d604c98ff90 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:09 -0500 Subject: [PATCH 233/285] wifi: mt76: mt792x: enable CNM ops for MT7927 Enable CNM support for MT7927 so mac80211 keeps remain-on-channel callbacks available. MT7927 needs them at runtime even when the capability is not advertised through the normal firmware feature path. Co-developed-by: Javier Tia Signed-off-by: Javier Tia Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-20-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x_core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 5a5d7534830b..1d807abc7125 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -758,6 +758,13 @@ mt792x_get_offload_capability(struct device *dev, const char *fw_wm) return offload_caps; } +static bool mt792x_needs_cnm_runtime(const void *drv_data) +{ + const char *fw_wm = drv_data; + + return fw_wm && !strcmp(fw_wm, MT7927_FIRMWARE_WM); +} + struct ieee80211_ops * mt792x_get_mac80211_ops(struct device *dev, const struct ieee80211_ops *mac80211_ops, @@ -771,6 +778,10 @@ mt792x_get_mac80211_ops(struct device *dev, return NULL; *fw_features = mt792x_get_offload_capability(dev, drv_data); + + if (mt792x_needs_cnm_runtime(drv_data)) + *fw_features |= MT792x_FW_CAP_CNM; + if (!(*fw_features & MT792x_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; From 35a5dcc71735eb42606be34e313f9eb8148b6a42 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:10 -0500 Subject: [PATCH 234/285] wifi: mt76: mt7925: add MT7927 PCIe support Add the missing MT7927 device support in the mt7925 PCI path. This ensures MT7927 is identified correctly and uses the proper initialization flow. Co-developed-by: Javier Tia Signed-off-by: Javier Tia Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-21-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index fd6d0b029ef4..ea64303283ed 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -16,6 +16,12 @@ static const struct pci_device_id mt7925_pci_device_table[] = { .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM }, { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0717), .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7927), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x6639), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0738), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, { }, }; @@ -376,7 +382,8 @@ static int mt7925_pci_probe(struct pci_dev *pdev, if (ret) goto err_free_pci_vec; - is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927); + is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927 || + pdev->device == 0x0738); /* MT7927: ASPM L1 causes unreliable WFDMA register access */ if (mt7925_disable_aspm || is_mt7927_hw) @@ -436,6 +443,13 @@ static int mt7925_pci_probe(struct pci_dev *pdev, dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + if (is_mt7927_hw && mt76_chip(mdev) != 0x7927) { + dev_info(mdev->dev, + "MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n", + mt76_chip(mdev)); + mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff); + } + mt76_rmw_field(dev, MT_HW_EMI_CTL, MT_HW_EMI_CTL_SLPPROT_EN, 1); ret = mt792x_wfsys_reset(dev); From bef8367b48e94cc16f033d0a37711e38d401f739 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 14:50:11 -0500 Subject: [PATCH 235/285] wifi: mt76: mt7925: add MT7927 USB support Add the MT7927 USB device ID and normalize the raw 0x6639 CHIPID to MT7927 so the shared mt7925u code can use the proper chip-specific paths. Restore the required band setup after USB MAC reset before restarting the device. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425195011.790265-22-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 15 +++++++++++++++ drivers/net/wireless/mediatek/mt76/usb.c | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index 7071b6c93d14..caacc54be71d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -10,6 +10,8 @@ #include "mac.h" static const struct usb_device_id mt7925u_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x6639, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7925, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, /* Netgear, Inc. A8500 */ @@ -123,6 +125,12 @@ static int mt7925u_mac_reset(struct mt792x_dev *dev) if (err) goto out; + if (is_mt7927(&dev->mt76)) { + err = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (err) + goto out; + } + err = __mt7925_start(&dev->phy); out: clear_bit(MT76_RESET, &dev->mphy.state); @@ -200,6 +208,13 @@ static int mt7925u_probe(struct usb_interface *usb_intf, (mt76_rr(dev, MT_HW_REV) & 0xff); dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + if (mt76_chip(mdev) == 0x6639) { + dev_dbg(mdev->dev, + "MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n", + mt76_chip(mdev)); + mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff); + } + if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) { ret = mt792xu_wfsys_reset(dev); if (ret) diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 632ae755c7a6..9bd61c9e94ed 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -925,6 +925,7 @@ mt76u_ac_to_hwq(struct mt76_dev *dev, struct mt76_queue *q, u8 qid) q->ep = q->hw_idx + 1; break; } + case 0x6639: case 0x7961: case 0x7925: q->hw_idx = mt76_ac_to_hwq(ac); From d3c854068bad22a25db6515f12784f64c663fed2 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 10:47:19 -0500 Subject: [PATCH 236/285] wifi: mt76: mt7925: keep TX BA state in the primary WCID For MLO, the same TID can run over different links. Keeping TX BA state in a link WCID makes the state depend on which link starts aggregation first. Store it in the primary WCID instead, so the BA state stays stable across links. Fixes: 44eb173bdd4f ("wifi: mt76: mt7925: add link handling in mt7925_txwi_free") Tested-by: Yao Ting Hsieh Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425154721.738101-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index d681005cc6ff..5dd895c3f205 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -846,7 +846,6 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_link_sta *link_sta; - struct mt792x_link_sta *mlink; struct mt792x_sta *msta; bool is_8023; u16 fc, tid; @@ -885,14 +884,14 @@ static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, msta = (struct mt792x_sta *)sta->drv_priv; - if (sta->mlo && msta->deflink_id != IEEE80211_LINK_UNSPECIFIED) - mlink = rcu_dereference(msta->link[msta->deflink_id]); - else - mlink = &msta->deflink; - - if (!test_and_set_bit(tid, &mlink->wcid.ampdu_state)) { + /* Packets belonging to the same TID can be transmitted over multiple + * links. Keep the TX BA session state in the primary link so all links + * share the same AMPDU bookkeeping. + */ + if (!test_and_set_bit(tid, &msta->deflink.wcid.ampdu_state)) { if (ieee80211_start_tx_ba_session(sta, tid, 0)) - clear_bit(tid, &mlink->wcid.ampdu_state); + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + } } From a13d3411390b10f5fcb81a654cbe0b19000144c5 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 10:47:20 -0500 Subject: [PATCH 237/285] wifi: mt76: mt7925: pass WCID explicitly to mt7925_mcu_sta_ba() mt7925_mcu_sta_ba() currently hides the BA target behind an implicit WCID lookup. Pass the WCID explicitly so the caller controls which BA context is being programmed. No functional change intended. Tested-by: Yao Ting Hsieh Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425154721.738101-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index f667eba99a51..8f0970d2ba31 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -655,9 +655,8 @@ void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) static int mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, struct ieee80211_ampdu_params *params, - bool enable, bool tx) + struct mt76_wcid *wcid, bool enable, bool tx) { - struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; struct sta_rec_ba_uni *ba; struct sk_buff *skb; struct tlv *tlv; @@ -695,6 +694,7 @@ int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, msta->deflink.wcid.amsdu = false; return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, + &msta->deflink.wcid, enable, true); } @@ -706,6 +706,7 @@ int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, struct mt792x_vif *mvif = msta->vif; return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, + &msta->deflink.wcid, enable, false); } From 733d8aa8d09be3068bcf2f09278446aca7921805 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 10:47:21 -0500 Subject: [PATCH 238/285] wifi: mt76: mt7925: program BA state on active links With MLO, traffic for one TID can be sent on any active link. Programming BA state only on the default link leaves the other active links out of sync. Program BA state on all active links instead. Fixes: 766ea2cf5a39 ("Revert "wifi: mt76: mt7925: Update mt7925_mcu_uni_[tx,rx]_ba for MLO"") Tested-by: Yao Ting Hsieh Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425154721.738101-3-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/main.c | 10 +-- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 67 ++++++++++++++----- .../wireless/mediatek/mt76/mt7925/mt7925.h | 4 +- 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 2599b738496f..c20e5316fae0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1410,22 +1410,22 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_RX_START: mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn, params->buf_size); - mt7925_mcu_uni_rx_ba(dev, params, true); + mt7925_mcu_uni_rx_ba(dev, params, vif, true); break; case IEEE80211_AMPDU_RX_STOP: mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid); - mt7925_mcu_uni_rx_ba(dev, params, false); + mt7925_mcu_uni_rx_ba(dev, params, vif, false); break; case IEEE80211_AMPDU_TX_OPERATIONAL: mtxq->aggr = true; mtxq->send_bar = false; - mt7925_mcu_uni_tx_ba(dev, params, true); + mt7925_mcu_uni_tx_ba(dev, params, vif, true); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: mtxq->aggr = false; clear_bit(tid, &msta->deflink.wcid.ampdu_state); - mt7925_mcu_uni_tx_ba(dev, params, false); + mt7925_mcu_uni_tx_ba(dev, params, vif, false); break; case IEEE80211_AMPDU_TX_START: set_bit(tid, &msta->deflink.wcid.ampdu_state); @@ -1434,7 +1434,7 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_TX_STOP_CONT: mtxq->aggr = false; clear_bit(tid, &msta->deflink.wcid.ampdu_state); - mt7925_mcu_uni_tx_ba(dev, params, false); + mt7925_mcu_uni_tx_ba(dev, params, vif, false); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 8f0970d2ba31..e94fa544ff20 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -682,32 +682,69 @@ mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, MCU_UNI_CMD(STA_REC_UPDATE), true); } -/** starec & wtbl **/ int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, - bool enable) + struct ieee80211_vif *vif, bool enable) { - struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; - struct mt792x_vif *mvif = msta->vif; + struct ieee80211_sta *sta = params->sta; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - if (enable && !params->amsdu) - msta->deflink.wcid.amsdu = false; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt792x_link_sta *mlink; + struct mt792x_bss_conf *mconf; + int ret; - return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, - &msta->deflink.wcid, - enable, true); + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + return -EINVAL; + + mconf = mt792x_vif_to_link(msta->vif, link_id); + if (!mconf) + return -EINVAL; + + if (enable && !params->amsdu) + mlink->wcid.amsdu = false; + + ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, params, + &mlink->wcid, enable, true); + if (ret) + return ret; + } + + return 0; } int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, - bool enable) + struct ieee80211_vif *vif, bool enable) { - struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; - struct mt792x_vif *mvif = msta->vif; + struct ieee80211_sta *sta = params->sta; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, - &msta->deflink.wcid, - enable, false); + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt792x_link_sta *mlink; + struct mt792x_bss_conf *mconf; + int ret; + + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + return -EINVAL; + + mconf = mt792x_vif_to_link(msta->vif, link_id); + if (!mconf) + return -EINVAL; + + ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, params, + &mlink->wcid, enable, false); + if (ret) + return ret; + } + + return 0; } static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 242f83f90dd4..4cc259418afc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -318,10 +318,10 @@ int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, bool enable); int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, - bool enable); + struct ieee80211_vif *vif, bool enable); int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, struct ieee80211_ampdu_params *params, - bool enable); + struct ieee80211_vif *vif, bool enable); void mt7925_mlo_pm_work(struct work_struct *work); void mt7925_scan_work(struct work_struct *work); void mt7925_roc_work(struct work_struct *work); From a1152244702bb31b64650e5ca8308142286c0e4a Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 09:46:48 -0500 Subject: [PATCH 239/285] wifi: mt76: mt792x: skip MLD header rewrite for 802.3 encap TX mt792x_tx() rewrites addr1/addr2/addr3 by treating skb->data as an 802.11 header for MLD traffic. That is only valid for native 802.11 frames. Direct 802.3 TX can also reach this path with IEEE80211_TX_CTL_HW_80211_ENCAP set, where skb->data is not an 802.11 header. Skip the MLD header rewrite for HW-encap packets to avoid corrupting 802.3 frame contents. Fixes: ebb1406813c6 ("wifi: mt76: mt7925: add link handling to txwi") Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425144648.734030-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt792x_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 1d807abc7125..e5ef724dd2d3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -105,7 +105,8 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, wcid = &mvif->sta.deflink.wcid; } - if (vif && control->sta && ieee80211_vif_is_mld(vif)) { + if (vif && control->sta && ieee80211_vif_is_mld(vif) && + !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_link_sta *link_sta; struct ieee80211_bss_conf *conf; From c7369a00860a0704461d440e7c3bf9b49bfdbaee Mon Sep 17 00:00:00 2001 From: Aviel Zohar Date: Mon, 13 Apr 2026 06:31:34 +0300 Subject: [PATCH 240/285] wifi: mt76: mt7925: validate skb length in testmode query In mt7925_tm_query(), the response skb from mt76_mcu_send_and_get_msg() is used in a memcpy without validating its length: memcpy(evt_resp, skb->data + 8, MT7925_EVT_RSP_LEN); where MT7925_EVT_RSP_LEN is 512. If the firmware returns a response shorter than 520 bytes (8 + 512), this reads beyond the skb data buffer. The over-read data is then returned to userspace via nla_put() in mt7925_testmode_dump(). Add a length check before the memcpy to ensure the skb contains sufficient data. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Aviel Zohar Link: https://patch.msgid.link/20260413033136.5417-2-avielzohar123@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/testmode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c index 3d40aacfc011..22a8f1ddc321 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c @@ -105,6 +105,11 @@ mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req, if (ret) goto out; + if (skb->len < MT7925_EVT_RSP_LEN + 8) { + ret = -EINVAL; + goto out; + } + memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN); out: From 4497665cf4f680f8befbe3c980c93917a195d26b Mon Sep 17 00:00:00 2001 From: Aviel Zohar Date: Mon, 13 Apr 2026 06:31:35 +0300 Subject: [PATCH 241/285] wifi: mt76: mt7915: validate skb length in txpower SKU query In mt7915_mcu_get_txpower_sku(), the response skb from mt76_mcu_send_and_get_msg() is used in memcpy without validating its length: For TX_POWER_INFO_RATE: memcpy(res, skb->data + 4, sizeof(res)); where sizeof(res) is MT7915_SKU_RATE_NUM * 2 = 322 bytes. For TX_POWER_INFO_PATH: memcpy(txpower, skb->data + 4, len); In both cases, if the firmware returns a response shorter than the expected size, the memcpy reads beyond the skb data buffer. The data surfaces to userspace via debugfs (txpower_sku and txpower_path). Add length checks for both code paths before the memcpy. Signed-off-by: Aviel Zohar Link: https://patch.msgid.link/20260413033136.5417-3-avielzohar123@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 318c38149463..4a381d351e61 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3534,10 +3534,18 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len, if (category == TX_POWER_INFO_RATE) { s8 res[MT7915_SKU_RATE_NUM][2]; + if (skb->len < sizeof(res) + 4) { + dev_kfree_skb(skb); + return -EINVAL; + } memcpy(res, skb->data + 4, sizeof(res)); for (i = 0; i < len; i++) txpower[i] = res[i][req.band_idx]; } else if (category == TX_POWER_INFO_PATH) { + if (skb->len < len + 4) { + dev_kfree_skb(skb); + return -EINVAL; + } memcpy(txpower, skb->data + 4, len); } From 831074096d0450308357271fc0ffd3f600a2487e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 11:10:59 +0200 Subject: [PATCH 242/285] wifi: mt76: mt7996: Fix possible token leak in mt7996_tx_prepare_skb() If link_conf or link_sta lookup fails in mt7996_tx_prepare_skb routine, mt7996 driver leaks an already allocated tx token. Fix the issue releasing the token in case of error. Fixes: 7ef0c7ad735b0 ("wifi: mt76: mt7996: Implement MLD address translation for EAPOL") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt7996_tx_prepare_skb-token-leack-v1-1-2b9c9f59ceb1@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 8 ++++++-- drivers/net/wireless/mediatek/mt76/tx.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index c98446057282..ade8706623b0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1067,11 +1067,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, link_conf = rcu_dereference(vif->link_conf[wcid->link_id]); if (!link_conf) - return -EINVAL; + goto error_release_token; link_sta = rcu_dereference(sta->link[wcid->link_id]); if (!link_sta) - return -EINVAL; + goto error_release_token; dma_sync_single_for_cpu(mdev->dma_dev, tx_info->buf[1].addr, tx_info->buf[1].len, DMA_TO_DEVICE); @@ -1176,6 +1176,10 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, tx_info->nbuf = MT_CT_DMA_BUF_NUM; return 0; + +error_release_token: + mt76_token_release(mdev, id, NULL); + return -EINVAL; } u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id) diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 22f9690634c9..f96d9c471853 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -933,7 +933,7 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake) #endif } - if (dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && + if (wake && dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && dev->phy.q_tx[0]->blocked) *wake = true; From 61370e6674b5253de5686813ceeceebc35a7d3e5 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 11:38:57 +0200 Subject: [PATCH 243/285] wifi: mt76: mt7996: Fix possible NULL pointer dereference in mt7996_mac_write_txwi_80211() For injected frames (e.g. via radiotap), mac80211 can pass info->control.vif = NULL, as explicitly noted in struct ieee80211_tx_info. Check vif pointer before executing ieee80211_vif_is_mld() in mt7996_mac_write_txwi_80211 routine in order to avoid a possible NULL pointer dereference. Fixes: f0b0b239b8f36 ("wifi: mt76: mt7996: rework mt7996_mac_write_txwi() for MLO support") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt7996_mac_write_txwi_80211-null-ptr-deref-v1-1-6dd38e1d3422@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index ade8706623b0..2224fbe04391 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -757,6 +757,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, bool multicast = is_multicast_ether_addr(hdr->addr1); u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; __le16 fc = hdr->frame_control, sc = hdr->seq_ctrl; + struct ieee80211_vif *vif = info->control.vif; u16 seqno = le16_to_cpu(sc); bool hw_bigtk = false; u8 fc_type, fc_stype; @@ -819,7 +820,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); } - if (multicast && ieee80211_vif_is_mld(info->control.vif)) { + if (multicast && vif && ieee80211_vif_is_mld(vif)) { val = MT_TXD3_SN_VALID | FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); txwi[3] |= cpu_to_le32(val); @@ -839,12 +840,12 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); } - if (ieee80211_vif_is_mld(info->control.vif) && + if (vif && ieee80211_vif_is_mld(vif) && (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))) txwi[5] |= cpu_to_le32(MT_TXD5_FL); if (ieee80211_is_nullfunc(fc) && ieee80211_has_a4(fc) && - ieee80211_vif_is_mld(info->control.vif)) { + vif && ieee80211_vif_is_mld(vif)) { txwi[5] |= cpu_to_le32(MT_TXD5_FL); txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); } From 729c83a3330c0a56662cd0d8e40db96d41c00a54 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 31 May 2026 10:55:04 +0200 Subject: [PATCH 244/285] wifi: mt76: mt7996: fix reading zeroed info->control.flags after mt76_tx_status_skb_add() mt76_tx_status_skb_add() zeroes the mt76_tx_cb struct stored at info->status.status_driver_data via memset(). Since info->control and info->status are members of the same union in ieee80211_tx_info, this overwrites info->control.flags. In mt7996_tx_prepare_skb(), mt76_tx_status_skb_add() is called before mt7996_mac_write_txwi(), which re-reads info->control.flags to extract IEEE80211_TX_CTRL_MLO_LINK. Because the field has been zeroed, the link_id always resolves to 0 for frames using global_wcid, leading to incorrect TXWI configuration. Fix this by passing link_id as an explicit parameter to mt7996_mac_write_txwi(). In mt7996_tx_prepare_skb(), the link_id is already extracted from info->control.flags before the destructive mt76_tx_status_skb_add() call. For the beacon and inband discovery callers in mcu.c, use link_conf->link_id directly. Fixes: f0b0b239b8f36 ("wifi: mt76: mt7996: rework mt7996_mac_write_txwi() for MLO support") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260531-mt76_tx_status_skb_add-overwrite-fix-v2-1-b73c4b4a9798@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 14 ++++---------- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 5 +++-- drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 3 ++- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 2224fbe04391..44713a7a86f8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -857,7 +857,8 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, - enum mt76_txq_id qid, u32 changed) + enum mt76_txq_id qid, u32 changed, + unsigned int link_id) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -867,7 +868,6 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; struct mt76_vif_link *mlink = NULL; struct mt7996_vif *mvif; - unsigned int link_id; u16 tx_count = 15; u32 val; bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | @@ -875,17 +875,11 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool beacon = !!(changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc); - if (wcid != &dev->mt76.global_wcid) - link_id = wcid->link_id; - else - link_id = u32_get_bits(info->control.flags, - IEEE80211_TX_CTRL_MLO_LINK); - mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL; if (mvif) { if (wcid->offchannel) mlink = rcu_dereference(mvif->mt76.offchannel_link); - if (!mlink) + if (!mlink && link_id != IEEE80211_LINK_UNSPECIFIED) mlink = rcu_dereference(mvif->mt76.link[link_id]); } @@ -1097,7 +1091,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, /* Transmit non qos data by 802.11 header and need to fill txd by host*/ if (!is_8023 || pid >= MT_PACKET_ID_FIRST) mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key, - pid, qid, 0); + pid, qid, 0, link_id); /* MT7996 and MT7992 require driver to provide the MAC TXP for AddBA * req diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 8be40d60ad29..a14c63438923 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -3103,7 +3103,7 @@ mt7996_mcu_beacon_cont(struct mt7996_dev *dev, buf = (u8 *)bcn + sizeof(*bcn); mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, - BSS_CHANGED_BEACON); + BSS_CHANGED_BEACON, link_conf->link_id); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); } @@ -3249,7 +3249,8 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, buf = (u8 *)tlv + sizeof(*discov); - mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, changed); + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, + changed, link_conf->link_id); memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 0dc4198fcf8b..0d6488522ba7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -874,7 +874,8 @@ void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, - enum mt76_txq_id qid, u32 changed); + enum mt76_txq_id qid, u32 changed, + unsigned int link_id); void mt7996_mac_update_beacons(struct mt7996_phy *phy); void mt7996_mac_set_coverage_class(struct mt7996_phy *phy); void mt7996_mac_work(struct work_struct *work); From 6b294950eaac246e6b0f42d74aa643ff36384c6e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 22 May 2026 09:24:52 +0200 Subject: [PATCH 245/285] wifi: mt76: mt7996: remove redundant pdev->bus check in probe Drop the unnecessary pdev->bus NULL check in mt7996_pci_probe() since the pointer is already dereferenced earlier in mt76_pci_disable_aspm(), making the check dead code. Silences the related Smatch warning. Fixes: 377aa17d2aed ("wifi: mt76: mt7996: Add NPU offload support to MT7996 driver") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260522-mt7996-pdev-bus-fix-v1-1-c91716484365@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c index 12523ddba630..b7d9193e042f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c @@ -141,7 +141,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev, dev->hif2 = hif2; mt76_npu_init(mdev, pci_resource_start(pdev, 0), - pdev->bus && pci_domain_nr(pdev->bus) ? 3 : 2); + pci_domain_nr(pdev->bus) ? 3 : 2); ret = mt7996_mmio_wed_init(dev, pdev, false, &irq); if (ret < 0) From 20b126920a259df4d7dcae19fcfe2c57a74d6b2e Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Thu, 28 May 2026 11:38:14 +0800 Subject: [PATCH 246/285] wifi: mt76: add wcid publish check in mt76_sta_add Since mt7925_mac_sta_add publishes wcid, add publish check in mt76_sta_add to avoid reinitializing the wcid->poll_list. Found dev->sta_poll_list corruption when using mt7925 and 7.1-rc4. According to the corruption information, prev->next was changed to itself. wlan0: disconnect from AP 90:fb:5d:94:8b:e3 for new auth to 90:fb:5d:94:8b:e2 wlan0: authenticate with 90:fb:5d:94:8b:e2 (local address=84:9e:56:9c:7e:6b) wlan0: send auth to 90:fb:5d:94:8b:e2 (try 1/3) slab kmalloc-8k start ffff8c80958a6000 pointer offset 4160 size 8192 list_add corruption. prev->next should be next (ffff8c808a7488f8), but was ffff8c80958a7040. (prev=ffff8c80958a7040). mt76_wcid_add_poll+0x95/0xd0 [mt76] mt7925_mac_add_txs.part.0+0xa5/0xe0 [mt7925_common] mt7925_rx_check+0xa7/0xc0 [mt7925_common] mt76_dma_rx_poll+0x50d/0x790 [mt76] mt792x_poll_rx+0x52/0xe0 [mt792x_lib] Signed-off-by: Jiajia Liu Link: https://patch.msgid.link/20260528033814.46418-1-liujiajia@kylinos.cn Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index dd68776ada28..dd09ef1bd0c2 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1578,6 +1578,7 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, { struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; struct mt76_dev *dev = phy->dev; + struct mt76_wcid *published; int ret; int i; @@ -1597,11 +1598,19 @@ mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, mtxq->wcid = wcid->idx; } - ewma_signal_init(&wcid->rssi); - rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + published = rcu_dereference_protected(dev->wcid[wcid->idx], + lockdep_is_held(&dev->mutex)); + if (published != wcid) { + WARN_ON_ONCE(published); + ewma_signal_init(&wcid->rssi); + rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + mt76_wcid_init(wcid, phy->band_idx); + } else { + wcid->phy_idx = phy->band_idx; + } + phy->num_sta++; - mt76_wcid_init(wcid, phy->band_idx); out: mutex_unlock(&dev->mutex); From 2dd78856223484895306351df1f903a4b75d213f Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Tue, 2 Jun 2026 13:43:49 +0800 Subject: [PATCH 247/285] wifi: mt76: transform aspm_conf for pci_disable_link_state commit b478e162f227 ("PCI/ASPM: Consolidate link state defines") changed PCIE_LINK_STATE_L0S (1) to (BIT(0) | BIT(1)). PCI_EXP_LNKCTL_ASPM_L0S (1) and PCI_EXP_LNKCTL_ASPM_L1 (2) are no longer matched with PCIE_LINK_STATE_L0S (3) and PCIE_LINK_STATE_L1 (4). On the platform enabling ASPM L0s and L1, mt76_pci_disable_aspm is not able to disable L1. Fix this by transforming aspm_conf to pcie link state. Signed-off-by: Jiajia Liu Link: https://patch.msgid.link/20260602054349.42429-1-liujia6264@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/pci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/pci.c b/drivers/net/wireless/mediatek/mt76/pci.c index 833923ab2483..11fe62aa8113 100644 --- a/drivers/net/wireless/mediatek/mt76/pci.c +++ b/drivers/net/wireless/mediatek/mt76/pci.c @@ -30,8 +30,14 @@ void mt76_pci_disable_aspm(struct pci_dev *pdev) if (IS_ENABLED(CONFIG_PCIEASPM)) { int err; + int state = 0; - err = pci_disable_link_state(pdev, aspm_conf); + if (aspm_conf & PCI_EXP_LNKCTL_ASPM_L0S) + state |= PCIE_LINK_STATE_L0S; + if (aspm_conf & PCI_EXP_LNKCTL_ASPM_L1) + state |= PCIE_LINK_STATE_L1; + + err = pci_disable_link_state(pdev, state); if (!err) return; } From 945c8028299a4712f12759f1ea14e63bba841e9f Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 25 Apr 2026 11:09:30 -0500 Subject: [PATCH 248/285] wifi: mt76: mt7921u: add MT7902 USB support Add the 0e8d:7902 USB ID and select the MT7902 WM firmware. Use the same USB queue mapping as mt7921/mt7925 so MT7902U can bind and probe through mt7921u driver. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260425160930.739237-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/usb.c | 2 ++ drivers/net/wireless/mediatek/mt76/usb.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 9bfc234f306f..4105c1baec1e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -13,6 +13,8 @@ #include "../mt76_connac2_mac.h" static const struct usb_device_id mt7921u_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7902, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7902_FIRMWARE_WM }, { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, /* Comfast CF-952AX */ diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 9bd61c9e94ed..d9638a9b749b 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -926,6 +926,7 @@ mt76u_ac_to_hwq(struct mt76_dev *dev, struct mt76_queue *q, u8 qid) break; } case 0x6639: + case 0x7902: case 0x7961: case 0x7925: q->hw_idx = mt76_ac_to_hwq(ac); From 0cb78a6dbe7deef60ae6427554ebca6e4b536855 Mon Sep 17 00:00:00 2001 From: Dylan Eskew Date: Wed, 15 Apr 2026 08:53:55 -0700 Subject: [PATCH 249/285] wifi: mt76: mt7996: reduce phy work in set_coverage In mt7996_set_coverage_class(), each phy is iterated over in calling mt7996_mac_set_coverage_class(). Thus, the phy2 and phy3 configuration logic in mt7996_mac_set_coverage_class() can be dropped. Signed-off-by: Dylan Eskew Link: https://patch.msgid.link/20260415155354.1135548-2-dylan.eskew@candelatech.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 44713a7a86f8..83e8e1e7feb4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2058,8 +2058,6 @@ void mt7996_mac_set_coverage_class(struct mt7996_phy *phy) { s16 coverage_class = phy->coverage_class; struct mt7996_dev *dev = phy->dev; - struct mt7996_phy *phy2 = mt7996_phy2(dev); - struct mt7996_phy *phy3 = mt7996_phy3(dev); u32 reg_offset; u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); @@ -2071,14 +2069,6 @@ void mt7996_mac_set_coverage_class(struct mt7996_phy *phy) if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) return; - if (phy2) - coverage_class = max_t(s16, dev->phy.coverage_class, - phy2->coverage_class); - - if (phy3) - coverage_class = max_t(s16, coverage_class, - phy3->coverage_class); - offset = 3 * coverage_class; reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); From 5fd3385505600934f5faa9635e5b30fa38e548b9 Mon Sep 17 00:00:00 2001 From: Dylan Eskew Date: Wed, 8 Apr 2026 07:50:58 -0700 Subject: [PATCH 250/285] wifi: mt76: mt7996: limit work in set_bitrate_mask Calls to mt7996_set_bitrate_mask() would propagate work for all stations on the ieee80211_hw regardless of the vif specified in the call. To prevent unnecessary work in FW, limit setting the sta_rate to only the specified vif in mt7996_sta_rate_ctrl_update(). Fixes: afff4325548f0 ("wifi: mt76: mt7996: Use proper link_id in link_sta_rc_update callback") Signed-off-by: Dylan Eskew Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260408145057.2356878-2-dylan.eskew@candelatech.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index b3cbf68bb53d..afbcc8c7b18b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1961,7 +1961,11 @@ static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_sta_link *msta_link; - u32 *changed = data; + struct mt7996_vif *mvif = data; + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + + if (msta->vif != mvif) + return; msta_link = mt7996_sta_link(msta, msta->deflink_id); if (msta_link) @@ -1974,7 +1978,6 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; mvif->deflink.bitrate_mask = *mask; @@ -1987,7 +1990,7 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * then multiple MCS setting (MCS 4,5,6) is not supported. */ ieee80211_iterate_stations_atomic(hw, mt7996_sta_rate_ctrl_update, - &changed); + mvif); ieee80211_queue_work(hw, &dev->rc_work); return 0; From 8286bbf62dcc2fc328c3960e32c4d3da66720463 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 1 Apr 2026 13:23:20 -0500 Subject: [PATCH 251/285] wifi: mt76: connac: use a helper to cache txpower_cur The cached txpower value is derived from the bounded channel power after applying the chainmask path delta. Use a helper for that conversion so callers do not open-code it. -- v2: - Rebased onto the latest mt76 tree - Added Reported-by, Tested-by, Co-developed-by and Signed-off-by tags Reported-by: Devin Wittmayer Closes: https://lore.kernel.org/linux-wireless/20260130215839.53270-1-lucid_duck@justthetip.ca/ Tested-by: Devin Wittmayer Tested-by: Satadru Pramanik Co-developed-by: Devin Wittmayer Signed-off-by: Devin Wittmayer Signed-off-by: Sean Wang Reported-by: Lucid Duck Tested-by: Lucid Duck Link: https://patch.msgid.link/20260401182322.64355-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac.h | 2 +- drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 9 +++++++++ drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 11 ++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 1994863dc59d..4afcc6477a99 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -436,7 +436,7 @@ mt76_connac_mutex_release(struct mt76_dev *dev, struct mt76_connac_pm *pm) void mt76_connac_gen_ppe_thresh(u8 *he_ppet, int nss, enum nl80211_band band); int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc, int ring_base, void *wed, u32 flags); - +void mt76_connac_set_txpower_cur(struct mt76_phy *phy, s8 max_power); void mt76_connac_write_hw_txp(struct mt76_dev *dev, struct mt76_tx_info *tx_info, void *txp_ptr, u32 id); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index c341595e9138..fc9f782032ef 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -275,6 +275,15 @@ int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc, } EXPORT_SYMBOL_GPL(mt76_connac_init_tx_queues); +void mt76_connac_set_txpower_cur(struct mt76_phy *phy, s8 max_power) +{ + int delta; + + delta = mt76_tx_power_path_delta(hweight16(phy->chainmask)); + phy->txpower_cur = max_power - delta; +} +EXPORT_SYMBOL_GPL(mt76_connac_set_txpower_cur); + #define __bitrate_mask_check(_mcs, _mode) \ ({ \ u8 i = 0; \ diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index be0ec1eaa746..03348463f2db 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -2235,14 +2235,19 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy, .hw_value = ch_list[idx], .band = band, }; - s8 reg_power, sar_power; + s8 reg_power, sar_power, max_power; reg_power = mt76_connac_get_ch_power(phy, &chan, tx_power); sar_power = mt76_get_sar_power(phy, &chan, reg_power); - mt76_get_rate_power_limits(phy, &chan, limits, - sar_power); + max_power = mt76_get_rate_power_limits(phy, &chan, limits, + sar_power); + + if (phy->chandef.chan && + phy->chandef.chan->hw_value == ch_list[idx] && + phy->chandef.chan->band == band) + mt76_connac_set_txpower_cur(phy, max_power); tx_power_tlv.last_msg = ch_list[idx] == last_ch; sku_tlbv.channel = ch_list[idx]; From 317bc1a0590e15c5a47c07e61f89edffcde65098 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 1 Apr 2026 13:23:21 -0500 Subject: [PATCH 252/285] wifi: mt76: connac: factor out rate power limit calculation Factor out the per-channel rate power limit calculation into a shared helper. This avoids duplicating the same regulatory, SAR and rate-limit logic in multiple paths. Reported-by: Devin Wittmayer Closes: https://lore.kernel.org/linux-wireless/20260130215839.53270-1-lucid_duck@justthetip.ca/ Tested-by: Devin Wittmayer Tested-by: Satadru Pramanik Co-developed-by: Devin Wittmayer Signed-off-by: Devin Wittmayer Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260401182322.64355-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt76_connac.h | 3 ++ .../wireless/mediatek/mt76/mt76_connac_mcu.c | 28 +++++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 4afcc6477a99..2aa6078993e9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -437,6 +437,9 @@ void mt76_connac_gen_ppe_thresh(u8 *he_ppet, int nss, enum nl80211_band band); int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc, int ring_base, void *wed, u32 flags); void mt76_connac_set_txpower_cur(struct mt76_phy *phy, s8 max_power); +s8 mt76_connac_get_rate_power_limit(struct mt76_phy *phy, + struct ieee80211_channel *chan, + struct mt76_power_limits *limits); void mt76_connac_write_hw_txp(struct mt76_dev *dev, struct mt76_tx_info *tx_info, void *txp_ptr, u32 id); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 03348463f2db..6596c9e198f4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -2235,15 +2235,10 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy, .hw_value = ch_list[idx], .band = band, }; - s8 reg_power, sar_power, max_power; - - reg_power = mt76_connac_get_ch_power(phy, &chan, - tx_power); - sar_power = mt76_get_sar_power(phy, &chan, reg_power); - - max_power = mt76_get_rate_power_limits(phy, &chan, limits, - sar_power); + s8 max_power; + max_power = mt76_connac_get_rate_power_limit(phy, &chan, + limits); if (phy->chandef.chan && phy->chandef.chan->hw_value == ch_list[idx] && phy->chandef.chan->band == band) @@ -2979,6 +2974,23 @@ int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_rdd_cmd); +s8 mt76_connac_get_rate_power_limit(struct mt76_phy *phy, + struct ieee80211_channel *chan, + struct mt76_power_limits *limits) +{ + s8 reg_power, sar_power; + int tx_power; + + tx_power = 2 * phy->hw->conf.power_level; + if (!tx_power) + tx_power = 127; + + reg_power = mt76_connac_get_ch_power(phy, chan, tx_power); + sar_power = mt76_get_sar_power(phy, chan, reg_power); + return mt76_get_rate_power_limits(phy, chan, limits, sar_power); +} +EXPORT_SYMBOL_GPL(mt76_connac_get_rate_power_limit); + static int mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev, const struct mt76_connac2_fw_trailer *hdr, From 879d754e48f6fda26ad06c9d183cd9f7f4f0affd Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 1 Apr 2026 13:23:22 -0500 Subject: [PATCH 253/285] wifi: mt76: mt792x: report txpower for the requested vif link mt792x currently reports txpower from the generic PHY cached state, which may not match the requested vif/link context. Resolve the requested link channel and derive txpower from that channel instead, with fallback to the current PHY chandef if no valid chanctx is available. Reported-by: Devin Wittmayer Closes: https://lore.kernel.org/linux-wireless/20260130215839.53270-1-lucid_duck@justthetip.ca/ Tested-by: Devin Wittmayer Tested-by: Satadru Pramanik Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260401182322.64355-3-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/main.c | 2 +- .../net/wireless/mediatek/mt76/mt7925/main.c | 2 +- drivers/net/wireless/mediatek/mt76/mt792x.h | 2 + .../net/wireless/mediatek/mt76/mt792x_core.c | 41 +++++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index adaf9a53f296..6ffccc023728 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1558,7 +1558,7 @@ const struct ieee80211_ops mt7921_ops = { .wake_tx_queue = mt76_wake_tx_queue, .release_buffered_frames = mt76_release_buffered_frames, .channel_switch_beacon = mt7921_channel_switch_beacon, - .get_txpower = mt76_get_txpower, + .get_txpower = mt792x_get_txpower, .get_stats = mt792x_get_stats, .get_et_sset_count = mt792x_get_et_sset_count, .get_et_strings = mt792x_get_et_strings, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index c20e5316fae0..a9059866b701 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -2504,7 +2504,7 @@ const struct ieee80211_ops mt7925_ops = { .wake_tx_queue = mt76_wake_tx_queue, .release_buffered_frames = mt76_release_buffered_frames, .channel_switch_beacon = mt7925_channel_switch_beacon, - .get_txpower = mt76_get_txpower, + .get_txpower = mt792x_get_txpower, .get_stats = mt792x_get_stats, .get_et_sset_count = mt792x_get_et_sset_count, .get_et_strings = mt792x_get_et_strings, diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 59562567cc13..4f823fb705d5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -416,6 +416,8 @@ void mt792x_roc_timer(struct timer_list *timer); void mt792x_csa_timer(struct timer_list *timer); void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop); +int mt792x_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm); int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index e5ef724dd2d3..b50825eccdaf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -330,6 +330,47 @@ void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } EXPORT_SYMBOL_GPL(mt792x_flush); +int mt792x_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm) +{ + struct mt76_power_limits limits = {}; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_channel *chan; + struct mt792x_bss_conf *mconf; + struct mt792x_vif *mvif; + struct mt76_phy *phy; + s8 max_power; + + if (!vif) + return mt76_get_txpower(hw, vif, link_id, dbm); + + mvif = (struct mt792x_vif *)vif->drv_priv; + phy = mvif->phy->mt76; + + mt792x_mutex_acquire(mvif->phy->dev); + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + mconf = link_conf ? mt792x_link_conf_to_mconf(link_conf) : NULL; + if (mconf && mconf->mt76.ctx && mconf->mt76.ctx->def.chan) + chan = mconf->mt76.ctx->def.chan; + else + /* Fall back to the current PHY chandef if the requested link + * does not have a valid channel context. + */ + chan = phy->chandef.chan; + + mt792x_mutex_release(mvif->phy->dev); + + if (!chan) + return -EINVAL; + + max_power = mt76_connac_get_rate_power_limit(phy, chan, &limits); + *dbm = DIV_ROUND_UP(max_power, 2); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_get_txpower); + int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, From d23353688dd06a3c019ad322884b94713f2a9466 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 1 Apr 2026 14:06:31 -0500 Subject: [PATCH 254/285] wifi: mt76: mt792x: add common USB transport reset helpers Add per-device USB reset work and a control-path access check helper for mt7921u and mt7925u. This prepares common infrastructure for transport-level recovery while keeping the reset state per-device for correct lifetime handling. No functional change intended. Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260401190632.147042-1-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/usb.c | 2 + .../net/wireless/mediatek/mt76/mt7925/usb.c | 2 + drivers/net/wireless/mediatek/mt76/mt792x.h | 5 ++ .../net/wireless/mediatek/mt76/mt792x_usb.c | 50 +++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 4105c1baec1e..01fa1b03e210 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -198,6 +198,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &hif_ops; + mt792xu_reset_work_init(dev); usb_reset_device(udev); @@ -245,6 +246,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf, error: mt76u_queues_deinit(&dev->mt76); + mt792xu_reset_work_cleanup(dev); usb_set_intfdata(usb_intf, NULL); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index caacc54be71d..e9f58492bf7d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -195,6 +195,7 @@ static int mt7925u_probe(struct usb_interface *usb_intf, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &hif_ops; + mt792xu_reset_work_init(dev); usb_reset_device(udev); @@ -249,6 +250,7 @@ static int mt7925u_probe(struct usb_interface *usb_intf, error: mt76u_queues_deinit(&dev->mt76); + mt792xu_reset_work_cleanup(dev); usb_set_intfdata(usb_intf, NULL); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 4f823fb705d5..95e7fb64599d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -262,6 +262,8 @@ struct mt792x_dev { wait_queue_head_t wait; struct work_struct init_work; + struct work_struct usb_reset_work; + atomic_t usb_reset_pending; u8 fw_debug; u8 fw_features; @@ -516,6 +518,9 @@ int mt792xu_dma_init(struct mt792x_dev *dev, bool resume); int mt792xu_mcu_power_on(struct mt792x_dev *dev); int mt792xu_wfsys_reset(struct mt792x_dev *dev); int mt792xu_init_reset(struct mt792x_dev *dev); +void mt792xu_reset_work_init(struct mt792x_dev *dev); +void mt792xu_reset_work_cleanup(struct mt792x_dev *dev); +int mt792xu_check_bus(struct mt792x_dev *dev); u32 mt792xu_rr(struct mt76_dev *dev, u32 addr); void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val); u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index be043e964c3c..58d442b39536 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -11,6 +11,55 @@ #include "mt792x.h" #include "mt76_connac2_mac.h" +static int mt792xu_read32(struct mt76_dev *dev, u32 addr, void *buf) +{ + return __mt76u_vendor_request(dev, MT_VEND_READ_EXT, + USB_DIR_IN | MT_USB_TYPE_VENDOR, + (u16)(addr >> 16), (u16)addr, + buf, sizeof(__le32)); +} + +static void mt792xu_reset_work(struct work_struct *work) +{ + struct mt792x_dev *dev = + container_of(work, struct mt792x_dev, usb_reset_work); + struct usb_interface *usb_intf = to_usb_interface(dev->mt76.dev); + + if (usb_intf && usb_get_intfdata(usb_intf) == dev) + usb_queue_reset_device(usb_intf); + + atomic_set(&dev->usb_reset_pending, 0); +} + +void mt792xu_reset_work_init(struct mt792x_dev *dev) +{ + INIT_WORK(&dev->usb_reset_work, mt792xu_reset_work); + atomic_set(&dev->usb_reset_pending, 0); +} +EXPORT_SYMBOL_GPL(mt792xu_reset_work_init); + +void mt792xu_reset_work_cleanup(struct mt792x_dev *dev) +{ + cancel_work_sync(&dev->usb_reset_work); + atomic_set(&dev->usb_reset_pending, 0); +} +EXPORT_SYMBOL_GPL(mt792xu_reset_work_cleanup); + +int mt792xu_check_bus(struct mt792x_dev *dev) +{ + int ret; + + mutex_lock(&dev->mt76.usb.usb_ctrl_mtx); + ret = mt792xu_read32(&dev->mt76, MT_HW_CHIPID, dev->mt76.usb.data); + mutex_unlock(&dev->mt76.usb.usb_ctrl_mtx); + + if (ret == sizeof(__le32)) + return 0; + + return ret < 0 ? ret : -EIO; +} +EXPORT_SYMBOL_GPL(mt792xu_check_bus); + u32 mt792xu_rr(struct mt76_dev *dev, u32 addr) { u32 ret; @@ -333,6 +382,7 @@ void mt792xu_disconnect(struct usb_interface *usb_intf) { struct mt792x_dev *dev = usb_get_intfdata(usb_intf); + mt792xu_reset_work_cleanup(dev); cancel_work_sync(&dev->init_work); if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) return; From 161596f033e8483863ed3e21380f9ea9d925bbc3 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 1 Apr 2026 14:06:32 -0500 Subject: [PATCH 255/285] wifi: mt76: mt7921u: escalate broken USB transport to device reset Check the USB control path before running the normal WFSYS reset flow. If USB access is no longer reliable, stop the WFSYS-only reset path, mark the device as bus_hung, and queue a USB device reset instead. Reuse the existing bus_hung state to represent transport-level failure, keeping the semantics consistent with the SDIO path. Also initialize bus_hung explicitly during probe for consistency. Reported-by: Bryam Vargas Closes: https://lore.kernel.org/r/CANAPQziOh3sB7B8G+U3AZsFfeFN1uAg4munhwA_feZi56D7W+Q@mail.gmail.com Signed-off-by: Sean Wang Link: https://patch.msgid.link/20260401190632.147042-2-sean.wang@kernel.org Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/mac.c | 4 ++- .../net/wireless/mediatek/mt76/mt7921/usb.c | 5 ++++ drivers/net/wireless/mediatek/mt76/mt792x.h | 1 + .../net/wireless/mediatek/mt76/mt792x_usb.c | 26 +++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 03b4960db73f..d27dbee8df1b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -675,7 +675,9 @@ void mt7921_mac_reset_work(struct work_struct *work) if (!ret) break; } - if (mt76_is_sdio(&dev->mt76) && atomic_read(&dev->mt76.bus_hung)) + + if ((mt76_is_sdio(&dev->mt76) || mt76_is_usb(&dev->mt76)) && + atomic_read(&dev->mt76.bus_hung)) return; if (i == 10) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 01fa1b03e210..2fdecf239125 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -90,6 +90,10 @@ static int mt7921u_mac_reset(struct mt792x_dev *dev) { int err; + mt792xu_reset_on_bus_error(dev); + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + mt76_txq_schedule_all(&dev->mphy); mt76_worker_disable(&dev->mt76.tx_worker); @@ -198,6 +202,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf, dev = container_of(mdev, struct mt792x_dev, mt76); dev->fw_features = features; dev->hif_ops = &hif_ops; + atomic_set(&dev->mt76.bus_hung, false); mt792xu_reset_work_init(dev); usb_reset_device(udev); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 95e7fb64599d..70073b43af54 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -521,6 +521,7 @@ int mt792xu_init_reset(struct mt792x_dev *dev); void mt792xu_reset_work_init(struct mt792x_dev *dev); void mt792xu_reset_work_cleanup(struct mt792x_dev *dev); int mt792xu_check_bus(struct mt792x_dev *dev); +int mt792xu_reset_on_bus_error(struct mt792x_dev *dev); u32 mt792xu_rr(struct mt76_dev *dev, u32 addr); void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val); u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c index 58d442b39536..910132e94956 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -60,6 +60,32 @@ int mt792xu_check_bus(struct mt792x_dev *dev) } EXPORT_SYMBOL_GPL(mt792xu_check_bus); +int mt792xu_reset_on_bus_error(struct mt792x_dev *dev) +{ + int err = 0; + + if (!atomic_read(&dev->mt76.bus_hung)) + err = mt792xu_check_bus(dev); + + if (err) { + atomic_set(&dev->mt76.bus_hung, true); + + if (!atomic_xchg(&dev->usb_reset_pending, 1)) { + dev_warn(dev->mt76.dev, + "USB transport access failed (%d), queueing device reset\n", + err); + + schedule_work(&dev->usb_reset_work); + } + + return err; + } + + atomic_set(&dev->mt76.bus_hung, false); + return 0; +} +EXPORT_SYMBOL_GPL(mt792xu_reset_on_bus_error); + u32 mt792xu_rr(struct mt76_dev *dev, u32 addr) { u32 ret; From 5832743279da8c6ae72f715bad2f7141eca6f4b8 Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Tue, 31 Mar 2026 22:05:47 +0000 Subject: [PATCH 256/285] wifi: mt76: fix argument to ieee80211_is_first_frag() ieee80211_is_first_frag() operates on the seq_ctrl not the frame_control header field. Pass the correct one in; otherwise the results may vary. Sponsored by: The FreeBSD Foundation Fixes: 30ce7f4456ae4 ("mt76: validate rx CCMP PN") Link: https://cgit.freebsd.org/src/commit/sys/contrib/dev/mediatek/mt76/mac80211.c?id=c67fd35e58c6ee1e19877a7fe5998885683abedc Signed-off-by: Bjoern A. Zeeb Link: https://patch.msgid.link/83s4psnr-popo-8789-757o-npr2n9n7rs2o@SerrOFQ.bet Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index dd09ef1bd0c2..13c4e8abe281 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1323,7 +1323,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb) * All further fragments will be validated by mac80211 only. */ if (ieee80211_is_frag(hdr) && - !ieee80211_is_first_frag(hdr->frame_control)) + !ieee80211_is_first_frag(hdr->seq_ctrl)) return; } From f4ce0664e9f0387873b181777891741c33e19465 Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Tue, 7 Apr 2026 23:44:30 +0800 Subject: [PATCH 257/285] wifi: mt76: mt76x2u: Add support for ELECOM WDC-867SU3S Add the ID 056e:400a to the table to support an additional MT7612U adapter: ELECOM WDC-867SU3S. Compile tested only. Cc: stable@vger.kernel.org # 5.10.x Signed-off-by: Zenm Chen Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260407154430.9184-1-zenmchen@gmail.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 8af360bca643..ade15bddefc5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -16,6 +16,7 @@ static const struct usb_device_id mt76x2u_device_table[] = { { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */ { USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */ { USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */ + { USB_DEVICE(0x056e, 0x400a) }, /* ELECOM WDC-867SU3S */ { USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */ { USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */ { USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */ From b7744e889f62a0a5fbf744a2a78a0560d7e5542f Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Wed, 25 Mar 2026 10:17:23 -0700 Subject: [PATCH 258/285] wifi: mt76: mt7996: disable UNI_BSS_INFO_PROTECT_INFO for mt7996 The current MT7996 firmware causes TX failure and need further investigation, so it is temporarily disabled. MT7992 and MT7990 are working normally. Signed-off-by: Ryder Lee Link: https://patch.msgid.link/6427326eb4e8f375c63379f7a0df7e2ae9d120a4.1774458901.git.ryder.lee@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index a14c63438923..f119f023bcd5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -1281,6 +1281,10 @@ int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *li PROT_NONGF_STA = BIT(7), }; + /* The current firmware causes TX failure. Need further investigation */ + if (is_mt7996(&dev->mt76)) + return 0; + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, MT7996_BSS_UPDATE_MAX_SIZE); if (IS_ERR(skb)) From c5c32d63068c4d849a7f66929331a06eec3df041 Mon Sep 17 00:00:00 2001 From: JB Tsai Date: Tue, 3 Mar 2026 13:36:33 +0800 Subject: [PATCH 259/285] wifi: mt76: mt7921: refactor regulatory domain handling to regd.[ch] Move regd logic to regd.c and regd.h files Co-developed-by: Sean Wang Signed-off-by: Sean Wang Signed-off-by: JB Tsai Link: https://patch.msgid.link/20260303053637.465465-1-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7921/Makefile | 2 +- .../net/wireless/mediatek/mt76/mt7921/init.c | 98 +---------------- .../net/wireless/mediatek/mt76/mt7921/main.c | 1 + .../wireless/mediatek/mt76/mt7921/mt7921.h | 1 - .../net/wireless/mediatek/mt76/mt7921/pci.c | 1 + .../net/wireless/mediatek/mt76/mt7921/regd.c | 103 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7921/regd.h | 15 +++ 7 files changed, 122 insertions(+), 99 deletions(-) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.h diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile index 2ad3c1cc3779..3ef7c9c45386 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_MT7921E) += mt7921e.o obj-$(CONFIG_MT7921S) += mt7921s.o obj-$(CONFIG_MT7921U) += mt7921u.o -mt7921-common-y := mac.o mcu.o main.o init.o debugfs.o +mt7921-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o mt7921e-y := pci.o pci_mac.o pci_mcu.o mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index 8e7790702191..49be2fa4e3fa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -9,6 +9,7 @@ #include "mt7921.h" #include "../mt76_connac2_mac.h" #include "mcu.h" +#include "regd.h" static ssize_t mt7921_thermal_temp_show(struct device *dev, struct device_attribute *attr, @@ -60,103 +61,6 @@ static int mt7921_thermal_init(struct mt792x_phy *phy) return PTR_ERR_OR_ZERO(hwmon); } -static void -mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) -{ -#define IS_UNII_INVALID(idx, sfreq, efreq) \ - (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) - struct ieee80211_supported_band *sband; - struct mt76_dev *mdev = &dev->mt76; - struct device_node *np, *band_np; - struct ieee80211_channel *ch; - int i, cfreq; - - np = mt76_find_power_limits_node(mdev); - - sband = wiphy->bands[NL80211_BAND_5GHZ]; - band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL; - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - cfreq = ch->center_freq; - - if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { - ch->flags |= IEEE80211_CHAN_DISABLED; - continue; - } - - /* UNII-4 */ - if (IS_UNII_INVALID(0, 5845, 5925)) - ch->flags |= IEEE80211_CHAN_DISABLED; - } - - sband = wiphy->bands[NL80211_BAND_6GHZ]; - if (!sband) - return; - - band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL; - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - cfreq = ch->center_freq; - - if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { - ch->flags |= IEEE80211_CHAN_DISABLED; - continue; - } - - /* UNII-5/6/7/8 */ - if (IS_UNII_INVALID(1, 5925, 6425) || - IS_UNII_INVALID(2, 6425, 6525) || - IS_UNII_INVALID(3, 6525, 6875) || - IS_UNII_INVALID(4, 6875, 7125)) - ch->flags |= IEEE80211_CHAN_DISABLED; - } -} - -void mt7921_regd_update(struct mt792x_dev *dev) -{ - struct mt76_dev *mdev = &dev->mt76; - struct ieee80211_hw *hw = mdev->hw; - struct wiphy *wiphy = hw->wiphy; - - mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env); - mt7921_regd_channel_update(wiphy, dev); - mt76_connac_mcu_set_channel_domain(hw->priv); - mt7921_set_tx_sar_pwr(hw, NULL); -} -EXPORT_SYMBOL_GPL(mt7921_regd_update); - -static void -mt7921_regd_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct mt792x_dev *dev = mt792x_hw_dev(hw); - struct mt76_connac_pm *pm = &dev->pm; - - memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); - dev->mt76.region = request->dfs_region; - dev->country_ie_env = request->country_ie_env; - - if (request->initiator == NL80211_REGDOM_SET_BY_USER) { - if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0') - wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE; - else - wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; - } - - if (pm->suspended) - return; - - dev->regd_in_progress = true; - - mt792x_mutex_acquire(dev); - mt7921_regd_update(dev); - mt792x_mutex_release(dev); - - dev->regd_in_progress = false; - wake_up(&dev->wait); -} - int mt7921_mac_init(struct mt792x_dev *dev) { int i; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 6ffccc023728..4ac26391a721 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -7,6 +7,7 @@ #include #include #include "mt7921.h" +#include "regd.h" #include "mcu.h" static int diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 64f60c4fc60c..fb8654b7c5e7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -260,7 +260,6 @@ mt7921_l1_rmw(struct mt792x_dev *dev, u32 addr, u32 mask, u32 val) #define mt7921_l1_set(dev, addr, val) mt7921_l1_rmw(dev, addr, 0, val) #define mt7921_l1_clear(dev, addr, val) mt7921_l1_rmw(dev, addr, val, 0) -void mt7921_regd_update(struct mt792x_dev *dev); int mt7921_mac_init(struct mt792x_dev *dev); bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask); int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 49a37185f056..7ac0ca07278d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -12,6 +12,7 @@ #include "../mt76_connac2_mac.h" #include "../dma.h" #include "mcu.h" +#include "regd.h" static const struct pci_device_id mt7921_pci_device_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961), diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c new file mode 100644 index 000000000000..51dbbadbc63a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2025 MediaTek Inc. */ + +#include +#include "mt7921.h" +#include "regd.h" +#include "mcu.h" + +static void +mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) +{ +#define IS_UNII_INVALID(idx, sfreq, efreq) \ + (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct device_node *np, *band_np; + struct ieee80211_channel *ch; + int i, cfreq; + + np = mt76_find_power_limits_node(mdev); + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL; + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + cfreq = ch->center_freq; + + if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { + ch->flags |= IEEE80211_CHAN_DISABLED; + continue; + } + + /* UNII-4 */ + if (IS_UNII_INVALID(0, 5845, 5925)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } + + sband = wiphy->bands[NL80211_BAND_6GHZ]; + if (!sband) + return; + + band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL; + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + cfreq = ch->center_freq; + + if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { + ch->flags |= IEEE80211_CHAN_DISABLED; + continue; + } + + /* UNII-5/6/7/8 */ + if (IS_UNII_INVALID(1, 5925, 6425) || + IS_UNII_INVALID(2, 6425, 6525) || + IS_UNII_INVALID(3, 6525, 6875) || + IS_UNII_INVALID(4, 6875, 7125)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } +} + +void mt7921_regd_update(struct mt792x_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_hw *hw = mdev->hw; + struct wiphy *wiphy = hw->wiphy; + + mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env); + mt7921_regd_channel_update(wiphy, dev); + mt76_connac_mcu_set_channel_domain(hw->priv); + mt7921_set_tx_sar_pwr(hw, NULL); +} +EXPORT_SYMBOL_GPL(mt7921_regd_update); + +void mt7921_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_connac_pm *pm = &dev->pm; + + memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); + dev->mt76.region = request->dfs_region; + dev->country_ie_env = request->country_ie_env; + + if (request->initiator == NL80211_REGDOM_SET_BY_USER) { + if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0') + wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE; + else + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; + } + + if (pm->suspended) + return; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + mt7921_regd_update(dev); + mt792x_mutex_release(dev); + + dev->regd_in_progress = false; + wake_up(&dev->wait); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h new file mode 100644 index 000000000000..2907b78d375b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2025 MediaTek Inc. */ + +#ifndef __MT7921_REGD_H +#define __MT7921_REGD_H + +struct mt792x_dev; +struct wiphy; +struct regulatory_request; + +void mt7921_regd_update(struct mt792x_dev *dev); +void mt7921_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request); + +#endif From fbb493cd953a664e4f007638837c7a02e4ec7774 Mon Sep 17 00:00:00 2001 From: JB Tsai Date: Tue, 3 Mar 2026 13:36:34 +0800 Subject: [PATCH 260/285] wifi: mt76: mt7921: refactor CLC support check flow Move the disable_clc module parameter to regd.c and introduce mt7921_regd_clc_supported() to centralize CLC support checks. Co-developed-by: Sean Wang Signed-off-by: Sean Wang Signed-off-by: JB Tsai Link: https://patch.msgid.link/20260303053637.465465-2-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 8 ++------ drivers/net/wireless/mediatek/mt76/mt7921/regd.c | 13 +++++++++++++ drivers/net/wireless/mediatek/mt76/mt7921/regd.h | 1 + 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 8442dbd2ee23..9777c899e503 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -4,6 +4,7 @@ #include #include #include "mt7921.h" +#include "regd.h" #include "mcu.h" #include "../mt76_connac2_mac.h" #include "../mt792x_trace.h" @@ -11,10 +12,6 @@ #define MT_STA_BFER BIT(0) #define MT_STA_BFEE BIT(1) -static bool mt7921_disable_clc; -module_param_named(disable_clc, mt7921_disable_clc, bool, 0644); -MODULE_PARM_DESC(disable_clc, "disable CLC support"); - int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd, struct sk_buff *skb, int seq) { @@ -422,8 +419,7 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) u8 *clc_base = NULL, hw_encap = 0; dev->phy.clc_chan_conf = 0xff; - if (mt7921_disable_clc || - mt76_is_usb(&dev->mt76)) + if (!mt7921_regd_clc_supported(dev)) return 0; if (mt76_is_mmio(&dev->mt76)) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index 51dbbadbc63a..943342984965 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -6,6 +6,19 @@ #include "regd.h" #include "mcu.h" +static bool mt7921_disable_clc; +module_param_named(disable_clc, mt7921_disable_clc, bool, 0644); +MODULE_PARM_DESC(disable_clc, "disable CLC support"); + +bool mt7921_regd_clc_supported(struct mt792x_dev *dev) +{ + if (mt7921_disable_clc || + mt76_is_usb(&dev->mt76)) + return false; + + return true; +} + static void mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h index 2907b78d375b..ece85feb7a23 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -11,5 +11,6 @@ struct regulatory_request; void mt7921_regd_update(struct mt792x_dev *dev); void mt7921_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); +bool mt7921_regd_clc_supported(struct mt792x_dev *dev); #endif From e88098133ed45f5fbe1cd29fde19ee3ef827de1b Mon Sep 17 00:00:00 2001 From: JB Tsai Date: Tue, 3 Mar 2026 13:36:35 +0800 Subject: [PATCH 261/285] wifi: mt76: mt7921: refactor regulatory notifier flow Rename mt7921_regd_update() to mt7921_mcu_regd_update() to centralize regd updates with error handling. Co-developed-by: Sean Wang Signed-off-by: Sean Wang Signed-off-by: JB Tsai Link: https://patch.msgid.link/20260303053637.465465-3-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/main.c | 2 +- .../net/wireless/mediatek/mt76/mt7921/pci.c | 2 +- .../net/wireless/mediatek/mt76/mt7921/regd.c | 45 +++++++++++++------ .../net/wireless/mediatek/mt76/mt7921/regd.h | 3 +- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 4ac26391a721..55ffd8eb8c6a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -802,7 +802,7 @@ mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add) out: if (vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) - mt7921_regd_update(dev); + mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); } int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 7ac0ca07278d..7728c5ae6791 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -589,7 +589,7 @@ static int mt7921_pci_resume(struct device *device) if (err < 0) goto failed; - mt7921_regd_update(dev); + mt7921_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env); err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_ON_LED); failed: pm->suspended = false; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index 943342984965..fa753e8e041d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -71,18 +71,43 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) } } -void mt7921_regd_update(struct mt792x_dev *dev) +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) { struct mt76_dev *mdev = &dev->mt76; struct ieee80211_hw *hw = mdev->hw; struct wiphy *wiphy = hw->wiphy; + int ret = 0; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + if (!dev->regd_change) + goto err; + + ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env); + if (ret < 0) + goto err; - mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env); mt7921_regd_channel_update(wiphy, dev); - mt76_connac_mcu_set_channel_domain(hw->priv); - mt7921_set_tx_sar_pwr(hw, NULL); + + ret = mt76_connac_mcu_set_channel_domain(hw->priv); + if (ret < 0) + goto err; + + ret = mt7921_set_tx_sar_pwr(hw, NULL); + if (ret < 0) + goto err; + +err: + mt792x_mutex_release(dev); + dev->regd_change = false; + dev->regd_in_progress = false; + wake_up(&dev->wait); + + return ret; } -EXPORT_SYMBOL_GPL(mt7921_regd_update); +EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update); void mt7921_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request) @@ -105,12 +130,6 @@ void mt7921_regd_notifier(struct wiphy *wiphy, if (pm->suspended) return; - dev->regd_in_progress = true; - - mt792x_mutex_acquire(dev); - mt7921_regd_update(dev); - mt792x_mutex_release(dev); - - dev->regd_in_progress = false; - wake_up(&dev->wait); + mt7921_mcu_regd_update(dev, request->alpha2, + request->country_ie_env); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h index ece85feb7a23..c7dcf747843c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -8,7 +8,8 @@ struct mt792x_dev; struct wiphy; struct regulatory_request; -void mt7921_regd_update(struct mt792x_dev *dev); +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env); void mt7921_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); bool mt7921_regd_clc_supported(struct mt792x_dev *dev); From ae05055d30f6843c6362449f201b005697d4be41 Mon Sep 17 00:00:00 2001 From: JB Tsai Date: Tue, 3 Mar 2026 13:36:36 +0800 Subject: [PATCH 262/285] wifi: mt76: mt7921: add auto regdomain switch support Implement 802.11d-based automatic regulatory domain switching to dynamically determine the regulatory domain at runtime. The scan-done event structure by reusing reserved padding and appending new fields; the layout and values remains backward-compatible with existing users. Co-developed-by: Sean Wang Signed-off-by: Sean Wang Signed-off-by: JB Tsai Link: https://patch.msgid.link/20260303053637.465465-4-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 3 +- .../net/wireless/mediatek/mt76/mt7921/mac.c | 3 + .../net/wireless/mediatek/mt76/mt7921/main.c | 12 ++- .../net/wireless/mediatek/mt76/mt7921/mcu.c | 3 +- .../net/wireless/mediatek/mt76/mt7921/regd.c | 80 +++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7921/regd.h | 2 + 6 files changed, 92 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 6d0429f40b0f..78f633ad81a0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1599,7 +1599,7 @@ struct mt76_connac_hw_scan_done { u8 pno_enabled; u8 pad2[3]; u8 sparse_channel_valid_num; - u8 pad3[3]; + u8 alpha2[3]; u8 channel_num[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; /* idle format for channel_idle_time * 0: first bytes: idle time(ms) 2nd byte: dwell time(ms) @@ -1612,6 +1612,7 @@ struct mt76_connac_hw_scan_done { u8 mdrdy_count[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; __le32 beacon_2g_num; __le32 beacon_5g_num; + __le16 channel_scan_time[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; } __packed; struct mt76_connac_sched_scan_req { diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index d27dbee8df1b..85434996f8e7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -7,6 +7,7 @@ #include "mt7921.h" #include "../dma.h" #include "../mt76_connac2_mac.h" +#include "regd.h" #include "mcu.h" #define MT_WTBL_TXRX_CAP_RATE_OFFSET 7 @@ -699,6 +700,8 @@ void mt7921_mac_reset_work(struct work_struct *work) IEEE80211_IFACE_ITER_RESUME_ALL, mt7921_vif_connect_iter, NULL); mt76_connac_power_save_sched(&dev->mt76.phy, pm); + + mt7921_regd_change(&dev->phy, "00"); } void mt7921_coredump_work(struct work_struct *work) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 55ffd8eb8c6a..0e19ba2b094d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1031,8 +1031,16 @@ void mt7921_scan_work(struct work_struct *work) rxd = (struct mt76_connac2_mcu_rxd *)skb->data; if (rxd->eid == MCU_EVENT_SCHED_SCAN_DONE) { ieee80211_sched_scan_results(phy->mt76->hw); - } else if (test_and_clear_bit(MT76_HW_SCANNING, - &phy->mt76->state)) { + } else if (rxd->eid == MCU_EVENT_SCAN_DONE) { + struct mt76_connac_hw_scan_done *event = NULL; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt76_connac_hw_scan_done *)skb->data; + mt7921_regd_change(phy, event->alpha2); + } + + if (test_and_clear_bit(MT76_HW_SCANNING, + &phy->mt76->state)) { struct cfg80211_scan_info info = { .aborted = false, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 9777c899e503..3e605a9ab919 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -484,7 +484,8 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) goto out; } } - ret = mt7921_mcu_set_clc(dev, "00", ENVIRON_INDOOR); + + ret = mt7921_regd_init(phy); out: release_firmware(fw); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index fa753e8e041d..d14ed100345b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -110,26 +110,92 @@ int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update); void mt7921_regd_notifier(struct wiphy *wiphy, - struct regulatory_request *request) + struct regulatory_request *req) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct mt792x_dev *dev = mt792x_hw_dev(hw); struct mt76_connac_pm *pm = &dev->pm; + struct mt76_dev *mdev = &dev->mt76; - memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); - dev->mt76.region = request->dfs_region; - dev->country_ie_env = request->country_ie_env; + /* do not need to update the same country twice */ + if (!memcmp(req->alpha2, mdev->alpha2, 2) && + dev->country_ie_env == req->country_ie_env) + return; - if (request->initiator == NL80211_REGDOM_SET_BY_USER) { + memcpy(mdev->alpha2, req->alpha2, 2); + mdev->region = req->dfs_region; + dev->country_ie_env = req->country_ie_env; + + if (req->initiator == NL80211_REGDOM_SET_BY_USER) { if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0') wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE; else wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; } + dev->regd_change = true; + if (pm->suspended) return; - mt7921_mcu_regd_update(dev, request->alpha2, - request->country_ie_env); + mt7921_mcu_regd_update(dev, req->alpha2, + req->country_ie_env); +} + +static bool +mt7921_regd_is_valid_alpha2(const char *alpha2) +{ + if (!alpha2) + return false; + + if (alpha2[0] == '0' && alpha2[1] == '0') + return true; + + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + + return false; +} + +int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + if (dev->hw_full_reset) + return 0; + + if (!mt7921_regd_is_valid_alpha2(alpha2) || + !mt7921_regd_clc_supported(dev)) + return -EINVAL; + + if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0') + return 0; + + /* do not need to update the same country twice */ + if (!memcmp(alpha2, mdev->alpha2, 2)) + return 0; + + if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) + return regulatory_hint(wiphy, alpha2); + else + return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR); +} + +int mt7921_regd_init(struct mt792x_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE | + REGULATORY_DISABLE_BEACON_HINTS; + else + memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2)); + + return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h index c7dcf747843c..571f31629e9e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -13,5 +13,7 @@ int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, void mt7921_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); bool mt7921_regd_clc_supported(struct mt792x_dev *dev); +int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2); +int mt7921_regd_init(struct mt792x_phy *phy); #endif From 9c082046c36286013ddb3d353db727868f95fab8 Mon Sep 17 00:00:00 2001 From: JB Tsai Date: Tue, 3 Mar 2026 13:36:37 +0800 Subject: [PATCH 263/285] wifi: mt76: mt7921: disable auto regd changes after user set Add regd_user flag to block automatic regulatory domain updates if set by user. Co-developed-by: Sean Wang Signed-off-by: Sean Wang Signed-off-by: JB Tsai Link: https://patch.msgid.link/20260303053637.465465-5-jb.tsai@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 1 + drivers/net/wireless/mediatek/mt76/mt7921/regd.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 3e605a9ab919..25b9437250f7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -419,6 +419,7 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) u8 *clc_base = NULL, hw_encap = 0; dev->phy.clc_chan_conf = 0xff; + dev->regd_user = false; if (!mt7921_regd_clc_supported(dev)) return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index d14ed100345b..c0e2b48a50bf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -117,6 +117,10 @@ void mt7921_regd_notifier(struct wiphy *wiphy, struct mt76_connac_pm *pm = &dev->pm; struct mt76_dev *mdev = &dev->mt76; + if (req->initiator == NL80211_REGDOM_SET_BY_USER && + !dev->regd_user) + dev->regd_user = true; + /* do not need to update the same country twice */ if (!memcmp(req->alpha2, mdev->alpha2, 2) && dev->country_ie_env == req->country_ie_env) @@ -168,7 +172,8 @@ int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2) return 0; if (!mt7921_regd_is_valid_alpha2(alpha2) || - !mt7921_regd_clc_supported(dev)) + !mt7921_regd_clc_supported(dev) || + dev->regd_user) return -EINVAL; if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0') From 05e72b6167970043348bfbe8f72a3b67a38a9f1c Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:03 -0700 Subject: [PATCH 264/285] wifi: mt76: mt7915: fix potential tx_retries underflow When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 943e4fb96e6f ("wifi: mt76: mt7915: report tx retries/failed counts for non-WED path") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-1-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index cec2c4208255..334c19ab2b22 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -912,16 +912,16 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) } if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) { - u32 tx_retries = 0, tx_failed = 0; + u32 tx_retries = 0, tx_failed = 0, count; if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) { - tx_retries = - FIELD_GET(MT_TX_FREE_COUNT_V3, info) - 1; + count = FIELD_GET(MT_TX_FREE_COUNT_V3, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TX_FREE_STAT_V3, info); } else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) { - tx_retries = - FIELD_GET(MT_TX_FREE_COUNT, info) - 1; + count = FIELD_GET(MT_TX_FREE_COUNT, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TX_FREE_STAT, info); } From 3c5671ed81b1fff97fa868dae771690599db94f7 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:04 -0700 Subject: [PATCH 265/285] wifi: mt76: mt7921: fix potential tx_retries underflow When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 9aecfa754c7f ("wifi: mt76: mt7921e: report tx retries/failed counts in tx free event") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-2-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 85434996f8e7..1c2377d0a53d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -531,8 +531,9 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len) stat = FIELD_GET(MT_TX_FREE_STATUS, info); if (wcid) { - wcid->stats.tx_retries += - FIELD_GET(MT_TX_FREE_COUNT, info) - 1; + u32 count = FIELD_GET(MT_TX_FREE_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; wcid->stats.tx_failed += !!stat; } From 1e1fd84571e62a2961cea44c053340ec5c99b2cb Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:05 -0700 Subject: [PATCH 266/285] wifi: mt76: mt7925: fix potential tx_retries underflow When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-3-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 5dd895c3f205..0641a7131d7c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1149,8 +1149,9 @@ mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len) if (info & MT_TXFREE_INFO_HEADER) { if (wcid) { - wcid->stats.tx_retries += - FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1; + u32 count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; wcid->stats.tx_failed += !!FIELD_GET(MT_TXFREE_INFO_STAT, info); } From 4d8bba99d645bcb46a442b18eb42402610cba03a Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 5 Jun 2026 04:33:06 -0700 Subject: [PATCH 267/285] wifi: mt76: mt7996: fix potential tx_retries underflow When FIELD_GET returns 0 for the retry count, subtracting 1 causes an unsigned integer underflow, resulting in tx_retries becoming a very large value (0xFFFFFFFF for u32). Fix by checking if count is non-zero before subtracting 1. Fixes: 2461599f835e ("wifi: mt76: mt7996: get tx_retries and tx_failed from txfree") Signed-off-by: Ryder Lee Link: https://patch.msgid.link/20260605113306.3485554-4-ryder.lee@mediatek.com Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 83e8e1e7feb4..0eebc8182ca9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1359,13 +1359,13 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) cur_info++; continue; } else if (info & MT_TXFREE_INFO_HEADER) { - u32 tx_retries = 0, tx_failed = 0; + u32 tx_retries = 0, tx_failed = 0, count; if (!wcid) continue; - tx_retries = - FIELD_GET(MT_TXFREE_INFO_COUNT, info) - 1; + count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + tx_retries = count ? count - 1 : 0; tx_failed = tx_retries + !!FIELD_GET(MT_TXFREE_INFO_STAT, info); From a7d35545c2ce11849de4b4397abaf664d79acd28 Mon Sep 17 00:00:00 2001 From: Devin Wittmayer Date: Fri, 15 May 2026 11:39:21 -0700 Subject: [PATCH 268/285] wifi: mt76: mt7921: assert sniffer on chanctx change mt7921_change_chanctx() configures the channel for monitor vifs but does not re-assert sniffer mode. mt7925_change_chanctx() does. Match mt7925 by adding the missing mt7921_mcu_set_sniffer(true) call, completing the architectural pattern from commit 914189af23b8 ("wifi: mt76: mt7921: fix channel switch fail in monitor mode"). The user-visible regression this asymmetry produced on v6.17 and v6.18 was addressed by commit cdb2941a516c ("Revert "wifi: mt76: mt792x: improve monitor interface handling"") in v6.19 and backported to the 6.17.y and 6.18.y stable trees. This patch is defense in depth in case the NO_VIRTUAL_MONITOR change is reintroduced in a future series. Tested-by: Nick Morrow Tested-on: RasPi4B, RasPiOS 64 bit, Alfa AWUS036AXML mt7921u Tested-on: RasPi4B, RasPiOS 64 bit, Netgear A9000 mt7925u Signed-off-by: Devin Wittmayer Link: https://patch.msgid.link/20260515183921.23484-1-lucid_duck@justthetip.ca Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 0e19ba2b094d..3480205d5fb9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1417,10 +1417,12 @@ mt7921_change_chanctx(struct ieee80211_hw *hw, vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); mt792x_mutex_acquire(phy->dev); - if (vif->type == NL80211_IFTYPE_MONITOR) + if (vif->type == NL80211_IFTYPE_MONITOR) { + mt7921_mcu_set_sniffer(mvif->phy->dev, vif, true); mt7921_mcu_config_sniffer(mvif, ctx); - else + } else { mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx); + } mt792x_mutex_release(phy->dev); } From 50a7f9f9d48eb50c0e95bef53358acb5af5cb3c6 Mon Sep 17 00:00:00 2001 From: Ingyu Jang Date: Tue, 19 May 2026 17:52:14 +0900 Subject: [PATCH 269/285] wifi: mt76: Drop unneeded mt76_register_debugfs_fops() return checks mt76_register_debugfs_fops() returns the dentry from debugfs_create_dir(), which yields an error pointer on failure (notably ERR_PTR(-ENODEV) when CONFIG_DEBUG_FS=n), never NULL. Per commit ff9fb72bc077 ("debugfs: return error values, not NULL"), callers do not need to check the return value. Drop the dead !dir checks in mt7615/mt7915/mt7921/mt7925/mt7996 _init_debugfs(). Converting them to IS_ERR() instead would have exposed a probe abort on CONFIG_DEBUG_FS=n, since each *_init_debugfs() caller propagates the helper's return value. This patch supersedes an earlier proposal that converted the checks to IS_ERR(). Link: https://lore.kernel.org/all/20260514193243.2518979-1-ingyujang25@korea.ac.kr Signed-off-by: Ingyu Jang Link: https://patch.msgid.link/20260519085214.164846-1-ingyujang25@korea.ac.kr Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c | 2 -- drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c | 3 +-- drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c | 2 -- drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c | 2 -- drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c | 2 -- 5 files changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c index 0f7b20152279..6a1475e3c899 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c @@ -550,8 +550,6 @@ int mt7615_init_debugfs(struct mt7615_dev *dev) struct dentry *dir; dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); - if (!dir) - return -ENOMEM; if (is_mt7615(&dev->mt76)) debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", dir, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 26ed3745af43..4d0854fe785b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -1296,8 +1296,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) struct dentry *dir; dir = mt76_register_debugfs_fops(phy->mt76, NULL); - if (!dir) - return -ENOMEM; + debugfs_create_file("muru_debug", 0600, dir, dev, &fops_muru_debug); debugfs_create_file("muru_stats", 0400, dir, phy, &mt7915_muru_stats_fops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c index 4333005b3ad9..a5a70d8e8544 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c @@ -266,8 +266,6 @@ int mt7921_init_debugfs(struct mt792x_dev *dev) struct dentry *dir; dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); - if (!dir) - return -ENOMEM; if (mt76_is_mmio(&dev->mt76)) debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c index e2498659c884..d01ff49de47a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c @@ -291,8 +291,6 @@ int mt7925_init_debugfs(struct mt792x_dev *dev) struct dentry *dir; dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); - if (!dir) - return -ENOMEM; if (mt76_is_mmio(&dev->mt76)) debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c index ef9a9204adf5..b85c3e549f8b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -875,8 +875,6 @@ int mt7996_init_debugfs(struct mt7996_dev *dev) struct dentry *dir; dir = mt76_register_debugfs_fops(&dev->mphy, NULL); - if (!dir) - return -ENOMEM; debugfs_create_file("hw-queues", 0400, dir, dev, &mt7996_hw_queues_fops); From 1c316d02c399e5efb1279666c078f99b3f72b0ca Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 8 Jun 2026 06:21:32 -0700 Subject: [PATCH 270/285] wifi: ath12k: Update Qualcomm copyrights Update Qualcomm copyrights per current legal guidance. Assisted-by: Claude:claude-sonnet-4-6 Link: https://patch.msgid.link/20260608-ath12k-copyright-v2-1-37504d70b03c@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/acpi.c | 2 +- drivers/net/wireless/ath/ath12k/acpi.h | 2 +- drivers/net/wireless/ath/ath12k/coredump.c | 2 +- drivers/net/wireless/ath/ath12k/coredump.h | 2 +- drivers/net/wireless/ath/ath12k/dbring.h | 2 +- drivers/net/wireless/ath/ath12k/debug.h | 2 +- drivers/net/wireless/ath/ath12k/debugfs.h | 2 +- drivers/net/wireless/ath/ath12k/debugfs_sta.h | 2 +- drivers/net/wireless/ath/ath12k/hif.h | 2 +- drivers/net/wireless/ath/ath12k/p2p.c | 1 - drivers/net/wireless/ath/ath12k/p2p.h | 2 +- drivers/net/wireless/ath/ath12k/reg.c | 2 +- drivers/net/wireless/ath/ath12k/reg.h | 2 +- drivers/net/wireless/ath/ath12k/testmode.h | 2 +- drivers/net/wireless/ath/ath12k/trace.c | 2 +- drivers/net/wireless/ath/ath12k/trace.h | 2 +- drivers/net/wireless/ath/ath12k/wow.h | 2 +- 17 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index d81367ce6929..c1b9be3e03ac 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h index 3a26fea6af1a..dd80eebff96a 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.h +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_ACPI_H #define ATH12K_ACPI_H diff --git a/drivers/net/wireless/ath/ath12k/coredump.c b/drivers/net/wireless/ath/ath12k/coredump.c index ce1beeb54836..070512338f13 100644 --- a/drivers/net/wireless/ath/ath12k/coredump.c +++ b/drivers/net/wireless/ath/ath12k/coredump.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include "hif.h" diff --git a/drivers/net/wireless/ath/ath12k/coredump.h b/drivers/net/wireless/ath/ath12k/coredump.h index 13f46a605113..584ce0a085a7 100644 --- a/drivers/net/wireless/ath/ath12k/coredump.h +++ b/drivers/net/wireless/ath/ath12k/coredump.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_COREDUMP_H_ #define _ATH12K_COREDUMP_H_ diff --git a/drivers/net/wireless/ath/ath12k/dbring.h b/drivers/net/wireless/ath/ath12k/dbring.h index e1c0eba774ec..780c27c570e5 100644 --- a/drivers/net/wireless/ath/ath12k/dbring.h +++ b/drivers/net/wireless/ath/ath12k/dbring.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_DBRING_H diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index bf254e43a68d..658b8083c9c3 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUG_H_ diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index 21641a8a0346..f5dbca1b8d75 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUGFS_H_ diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.h b/drivers/net/wireless/ath/ath12k/debugfs_sta.h index 8de924f4d7d5..44c78f3ab904 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_sta.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH12K_DEBUGFS_STA_H_ diff --git a/drivers/net/wireless/ath/ath12k/hif.h b/drivers/net/wireless/ath/ath12k/hif.h index e8840fab6061..4de8667690e9 100644 --- a/drivers/net/wireless/ath/ath12k/hif.h +++ b/drivers/net/wireless/ath/ath12k/hif.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_HIF_H diff --git a/drivers/net/wireless/ath/ath12k/p2p.c b/drivers/net/wireless/ath/ath12k/p2p.c index 19ebcd1d8eb2..ae9a8be17be4 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.c +++ b/drivers/net/wireless/ath/ath12k/p2p.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath12k/p2p.h b/drivers/net/wireless/ath/ath12k/p2p.h index 03ee877e6d6b..3041f88508db 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.h +++ b/drivers/net/wireless/ath/ath12k/p2p.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_P2P_H diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index d784d9ec5061..89abf2e87ad1 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h index da5128b8c97f..7c37c0d2a6f9 100644 --- a/drivers/net/wireless/ath/ath12k/reg.h +++ b/drivers/net/wireless/ath/ath12k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_REG_H diff --git a/drivers/net/wireless/ath/ath12k/testmode.h b/drivers/net/wireless/ath/ath12k/testmode.h index ef6ab21d19b8..342ed0a7fa98 100644 --- a/drivers/net/wireless/ath/ath12k/testmode.h +++ b/drivers/net/wireless/ath/ath12k/testmode.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath12k/trace.c b/drivers/net/wireless/ath/ath12k/trace.c index 0d0edf4204b7..772a5a7db98b 100644 --- a/drivers/net/wireless/ath/ath12k/trace.c +++ b/drivers/net/wireless/ath/ath12k/trace.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath12k/trace.h b/drivers/net/wireless/ath/ath12k/trace.h index 253c67accb0e..053d681302f0 100644 --- a/drivers/net/wireless/ath/ath12k/trace.h +++ b/drivers/net/wireless/ath/ath12k/trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) diff --git a/drivers/net/wireless/ath/ath12k/wow.h b/drivers/net/wireless/ath/ath12k/wow.h index af9be5fadcc3..273705956815 100644 --- a/drivers/net/wireless/ath/ath12k/wow.h +++ b/drivers/net/wireless/ath/ath12k/wow.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH12K_WOW_H From 053a93808d4654fae18633b01a747caa7a281aaa Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 8 Jun 2026 06:21:33 -0700 Subject: [PATCH 271/285] wifi: ath11k: Update Qualcomm copyrights Update Qualcomm copyrights per current legal guidance. Assisted-by: Claude:claude-sonnet-4-6 Link: https://patch.msgid.link/20260608-ath12k-copyright-v2-2-37504d70b03c@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath11k/ahb.c | 2 +- drivers/net/wireless/ath/ath11k/ahb.h | 2 +- drivers/net/wireless/ath/ath11k/ce.c | 1 - drivers/net/wireless/ath/ath11k/ce.h | 2 +- drivers/net/wireless/ath/ath11k/coredump.c | 1 - drivers/net/wireless/ath/ath11k/coredump.h | 2 +- drivers/net/wireless/ath/ath11k/debug.c | 1 - drivers/net/wireless/ath/ath11k/debugfs.c | 1 - drivers/net/wireless/ath/ath11k/debugfs.h | 2 +- drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c | 1 - drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h | 2 +- drivers/net/wireless/ath/ath11k/debugfs_sta.h | 2 +- drivers/net/wireless/ath/ath11k/dp.c | 1 - drivers/net/wireless/ath/ath11k/dp.h | 2 +- drivers/net/wireless/ath/ath11k/dp_rx.h | 2 +- drivers/net/wireless/ath/ath11k/dp_tx.c | 1 - drivers/net/wireless/ath/ath11k/dp_tx.h | 2 +- drivers/net/wireless/ath/ath11k/fw.c | 1 - drivers/net/wireless/ath/ath11k/fw.h | 2 +- drivers/net/wireless/ath/ath11k/hal_desc.h | 2 +- drivers/net/wireless/ath/ath11k/hal_rx.c | 2 +- drivers/net/wireless/ath/ath11k/hal_rx.h | 2 +- drivers/net/wireless/ath/ath11k/hal_tx.c | 2 +- drivers/net/wireless/ath/ath11k/hal_tx.h | 2 +- drivers/net/wireless/ath/ath11k/hif.h | 2 +- drivers/net/wireless/ath/ath11k/htc.c | 2 +- drivers/net/wireless/ath/ath11k/htc.h | 2 +- drivers/net/wireless/ath/ath11k/hw.c | 2 +- drivers/net/wireless/ath/ath11k/mac.h | 2 +- drivers/net/wireless/ath/ath11k/mhi.h | 2 +- drivers/net/wireless/ath/ath11k/p2p.c | 2 +- drivers/net/wireless/ath/ath11k/p2p.h | 2 +- drivers/net/wireless/ath/ath11k/pcic.c | 1 - drivers/net/wireless/ath/ath11k/pcic.h | 2 +- drivers/net/wireless/ath/ath11k/peer.c | 2 +- drivers/net/wireless/ath/ath11k/peer.h | 2 +- drivers/net/wireless/ath/ath11k/qmi.h | 2 +- drivers/net/wireless/ath/ath11k/reg.h | 2 +- drivers/net/wireless/ath/ath11k/rx_desc.h | 2 +- drivers/net/wireless/ath/ath11k/spectral.c | 1 - drivers/net/wireless/ath/ath11k/spectral.h | 2 +- drivers/net/wireless/ath/ath11k/testmode.c | 2 +- drivers/net/wireless/ath/ath11k/testmode.h | 2 +- drivers/net/wireless/ath/ath11k/thermal.c | 2 +- drivers/net/wireless/ath/ath11k/thermal.h | 2 +- drivers/net/wireless/ath/ath11k/trace.h | 2 +- drivers/net/wireless/ath/ath11k/wow.c | 2 +- drivers/net/wireless/ath/ath11k/wow.h | 2 +- 48 files changed, 38 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 08d3a0c8f105..f566d699d074 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/ahb.h b/drivers/net/wireless/ath/ath11k/ahb.h index 415ddfd26654..8c1eb1e8e6b1 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.h +++ b/drivers/net/wireless/ath/ath11k/ahb.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_AHB_H #define ATH11K_AHB_H diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c index f2d5106adb01..c56dd8a86760 100644 --- a/drivers/net/wireless/ath/ath11k/ce.c +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h index bcde2fcf02cf..308f425fcec0 100644 --- a/drivers/net/wireless/ath/ath11k/ce.h +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_CE_H diff --git a/drivers/net/wireless/ath/ath11k/coredump.c b/drivers/net/wireless/ath/ath11k/coredump.c index 1949d57b007a..3de34295ddc8 100644 --- a/drivers/net/wireless/ath/ath11k/coredump.c +++ b/drivers/net/wireless/ath/ath11k/coredump.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/coredump.h b/drivers/net/wireless/ath/ath11k/coredump.h index 3960d9385261..1be82207690f 100644 --- a/drivers/net/wireless/ath/ath11k/coredump.h +++ b/drivers/net/wireless/ath/ath11k/coredump.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_COREDUMP_H_ #define _ATH11K_COREDUMP_H_ diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c index 37d23a559ba3..f65e6cf846f7 100644 --- a/drivers/net/wireless/ath/ath11k/debug.c +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 0c1138407838..70922612d3fb 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h index ed7fec177588..859071938a65 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.h +++ b/drivers/net/wireless/ath/ath11k/debugfs.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_DEBUGFS_H_ diff --git a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c index 11d28c42227e..731f3ca86711 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h index 476689bbd4da..856aa74ba365 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef DEBUG_HTT_STATS_H diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.h b/drivers/net/wireless/ath/ath11k/debugfs_sta.h index ace877e19275..6d4cf365d937 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs_sta.h +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_DEBUGFS_STA_H_ diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index 5a50b623bd07..f389b97acbdd 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index 1bd513f68a3c..84f66839f0c6 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_DP_H diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.h b/drivers/net/wireless/ath/ath11k/dp_rx.h index c322e30caa96..f69d9301e0a1 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.h +++ b/drivers/net/wireless/ath/ath11k/dp_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_DP_RX_H #define ATH11K_DP_RX_H diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 9c2310665713..cac970c92806 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.h b/drivers/net/wireless/ath/ath11k/dp_tx.h index 795fe3b8fa0d..9303b5ba6e01 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.h +++ b/drivers/net/wireless/ath/ath11k/dp_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021, 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_DP_TX_H diff --git a/drivers/net/wireless/ath/ath11k/fw.c b/drivers/net/wireless/ath/ath11k/fw.c index 07d775a7b528..ef1b4c81843a 100644 --- a/drivers/net/wireless/ath/ath11k/fw.c +++ b/drivers/net/wireless/ath/ath11k/fw.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/fw.h b/drivers/net/wireless/ath/ath11k/fw.h index d9893ceb2c3d..df2fab2c1143 100644 --- a/drivers/net/wireless/ath/ath11k/fw.h +++ b/drivers/net/wireless/ath/ath11k/fw.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_FW_H diff --git a/drivers/net/wireless/ath/ath11k/hal_desc.h b/drivers/net/wireless/ath/ath11k/hal_desc.h index b2fd180bd28e..c8b01f6921c0 100644 --- a/drivers/net/wireless/ath/ath11k/hal_desc.h +++ b/drivers/net/wireless/ath/ath11k/hal_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 51e0840bc0d1..75f7b8d8a0d7 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "debug.h" diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.h b/drivers/net/wireless/ath/ath11k/hal_rx.h index 0fa9aef9d533..475807b89a1a 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.h +++ b/drivers/net/wireless/ath/ath11k/hal_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_HAL_RX_H diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c index b919df6ce743..b7e9b1cdd76a 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.c +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "hal_desc.h" diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.h b/drivers/net/wireless/ath/ath11k/hal_tx.h index 46d17abd808b..f2dc6e270534 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.h +++ b/drivers/net/wireless/ath/ath11k/hal_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_HAL_TX_H diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h index cd9c4b838246..017fed1b4bd1 100644 --- a/drivers/net/wireless/ath/ath11k/hif.h +++ b/drivers/net/wireless/ath/ath11k/hif.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _HIF_H_ diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c index 4571d01cc33d..5f9ee5e633da 100644 --- a/drivers/net/wireless/ath/ath11k/htc.c +++ b/drivers/net/wireless/ath/ath11k/htc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include #include diff --git a/drivers/net/wireless/ath/ath11k/htc.h b/drivers/net/wireless/ath/ath11k/htc.h index 86f77eacaea7..945c409bc08f 100644 --- a/drivers/net/wireless/ath/ath11k/htc.h +++ b/drivers/net/wireless/ath/ath11k/htc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_HTC_H diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c index d19c4b372a2a..93f9a03c48dc 100644 --- a/drivers/net/wireless/ath/ath11k/hw.c +++ b/drivers/net/wireless/ath/ath11k/hw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 5e61eea1bb03..59f83c7175fd 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_MAC_H diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h index 5c5c2b03c81f..7e7561ce883c 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.h +++ b/drivers/net/wireless/ath/ath11k/mhi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_MHI_H #define _ATH11K_MHI_H diff --git a/drivers/net/wireless/ath/ath11k/p2p.c b/drivers/net/wireless/ath/ath11k/p2p.c index 01e14523f1fe..77672099c602 100644 --- a/drivers/net/wireless/ath/ath11k/p2p.c +++ b/drivers/net/wireless/ath/ath11k/p2p.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath11k/p2p.h b/drivers/net/wireless/ath/ath11k/p2p.h index d907940a9b09..449f0c61b600 100644 --- a/drivers/net/wireless/ath/ath11k/p2p.h +++ b/drivers/net/wireless/ath/ath11k/p2p.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_P2P_H diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index fc6e7da05c60..2259adc3bbdc 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/pcic.h b/drivers/net/wireless/ath/ath11k/pcic.h index ac012e88bf6d..7a654a6ef9d1 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.h +++ b/drivers/net/wireless/ath/ath11k/pcic.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_PCI_CMN_H diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c index 027cc7aa58c8..b30a90698544 100644 --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath11k/peer.h b/drivers/net/wireless/ath/ath11k/peer.h index f5ef1a27f8f2..20c68ef52301 100644 --- a/drivers/net/wireless/ath/ath11k/peer.h +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_PEER_H diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index eae416db8b52..350816c81ee5 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_QMI_H diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h index 72b483594015..fed9bedc52ef 100644 --- a/drivers/net/wireless/ath/ath11k/reg.h +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_REG_H diff --git a/drivers/net/wireless/ath/ath11k/rx_desc.h b/drivers/net/wireless/ath/ath11k/rx_desc.h index 2da6da727278..3780bd909bbf 100644 --- a/drivers/net/wireless/ath/ath11k/rx_desc.h +++ b/drivers/net/wireless/ath/ath11k/rx_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_RX_DESC_H #define ATH11K_RX_DESC_H diff --git a/drivers/net/wireless/ath/ath11k/spectral.c b/drivers/net/wireless/ath/ath11k/spectral.c index b6b0516819a6..03af39071839 100644 --- a/drivers/net/wireless/ath/ath11k/spectral.c +++ b/drivers/net/wireless/ath/ath11k/spectral.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath11k/spectral.h b/drivers/net/wireless/ath/ath11k/spectral.h index 789cff7c64a7..b582f2c36c08 100644 --- a/drivers/net/wireless/ath/ath11k/spectral.h +++ b/drivers/net/wireless/ath/ath11k/spectral.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_SPECTRAL_H diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index c72eed358f6d..21a85301bf9f 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "testmode.h" diff --git a/drivers/net/wireless/ath/ath11k/testmode.h b/drivers/net/wireless/ath/ath11k/testmode.h index 2f62f2c4422f..45019b79fcf1 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.h +++ b/drivers/net/wireless/ath/ath11k/testmode.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" diff --git a/drivers/net/wireless/ath/ath11k/thermal.c b/drivers/net/wireless/ath/ath11k/thermal.c index 18d6eab5cce3..67246b5ca5bf 100644 --- a/drivers/net/wireless/ath/ath11k/thermal.c +++ b/drivers/net/wireless/ath/ath11k/thermal.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/thermal.h b/drivers/net/wireless/ath/ath11k/thermal.h index cdaf4e01d92e..15e4e97c944f 100644 --- a/drivers/net/wireless/ath/ath11k/thermal.h +++ b/drivers/net/wireless/ath/ath11k/thermal.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _ATH11K_THERMAL_ diff --git a/drivers/net/wireless/ath/ath11k/trace.h b/drivers/net/wireless/ath/ath11k/trace.h index 75246b0a82e3..d43776229bc9 100644 --- a/drivers/net/wireless/ath/ath11k/trace.h +++ b/drivers/net/wireless/ath/ath11k/trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c index f02edc9b8a9e..d6c2725903cc 100644 --- a/drivers/net/wireless/ath/ath11k/wow.c +++ b/drivers/net/wireless/ath/ath11k/wow.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath11k/wow.h b/drivers/net/wireless/ath/ath11k/wow.h index c85811e3f42b..13dbcfec9227 100644 --- a/drivers/net/wireless/ath/ath11k/wow.h +++ b/drivers/net/wireless/ath/ath11k/wow.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _WOW_H_ From ae667b0f98b80146ea22830a1b53c5758ad742f1 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 8 Jun 2026 06:21:34 -0700 Subject: [PATCH 272/285] wifi: ath10k: Update Qualcomm copyrights Update Qualcomm copyrights per current legal guidance. Assisted-by: Claude:claude-sonnet-4-6 Link: https://patch.msgid.link/20260608-ath12k-copyright-v2-3-37504d70b03c@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath10k/bmi.c | 1 - drivers/net/wireless/ath/ath10k/ce.c | 1 - drivers/net/wireless/ath/ath10k/coredump.c | 1 - drivers/net/wireless/ath/ath10k/coredump.h | 2 +- drivers/net/wireless/ath/ath10k/debug.c | 1 - drivers/net/wireless/ath/ath10k/debugfs_sta.c | 1 - drivers/net/wireless/ath/ath10k/htc.c | 1 - drivers/net/wireless/ath/ath10k/htt.c | 2 +- drivers/net/wireless/ath/ath10k/htt.h | 2 +- drivers/net/wireless/ath/ath10k/htt_rx.c | 1 - drivers/net/wireless/ath/ath10k/htt_tx.c | 1 - drivers/net/wireless/ath/ath10k/hw.c | 2 +- drivers/net/wireless/ath/ath10k/hw.h | 2 +- drivers/net/wireless/ath/ath10k/pci.c | 1 - drivers/net/wireless/ath/ath10k/pci.h | 2 +- drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c | 2 +- drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h | 2 +- drivers/net/wireless/ath/ath10k/rx_desc.h | 2 +- drivers/net/wireless/ath/ath10k/sdio.c | 2 +- drivers/net/wireless/ath/ath10k/thermal.c | 2 +- drivers/net/wireless/ath/ath10k/usb.h | 2 +- drivers/net/wireless/ath/ath10k/wmi-tlv.h | 2 +- drivers/net/wireless/ath/ath10k/wow.c | 2 +- 23 files changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 52118867ecde..e4c84be44ba6 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index a9cbe955e084..5a796e704ae9 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -3,7 +3,6 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c index 50d0c4213ecf..7fdc06fff79f 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.c +++ b/drivers/net/wireless/ath/ath10k/coredump.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/coredump.h b/drivers/net/wireless/ath/ath10k/coredump.h index 8d274e0f374b..95ee70d23370 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.h +++ b/drivers/net/wireless/ath/ath10k/coredump.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _COREDUMP_H_ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index b7520220465a..fb61e53ff4c3 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -3,7 +3,6 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index b9fb192e0b48..7f7be7c69232 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index ce9b248c12dc..821a12090ba9 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index dbaf262cd7c1..1c2e137b6002 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 603f6de62b0a..25c6b2e2f81c 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2021, 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _HTT_H_ diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 25ab945fecef..faac359aa9ac 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -3,7 +3,6 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 29e99fbf36fd..e46f579d745d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 59b6cebfdd8f..6f5019bf0b7d 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: ISC /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index da71dce9babf..cd468b24bd33 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _HW_H_ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 97b49bf4ad80..335bc7c488e4 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 4c3f536f2ea1..d114778edb41 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _PCI_H_ diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c index 0e85c75d2278..279aeb2a7dbc 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: ISC /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h index 88d58f78989d..8d4b929e8437 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2018 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef WCN3990_QMI_SVC_V01_H diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index 564293df1e9a..e6d92a29d2a0 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _RX_DESC_H_ diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 43a6b1ba8fb8..65e941b52751 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -3,7 +3,7 @@ * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012,2017 Qualcomm Atheros, Inc. * Copyright (c) 2016-2017 Erik Stromdahl - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index 8b15ec07b107..9bb486f8e920 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: ISC /* * Copyright (c) 2014-2015 Qualcomm Atheros, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include diff --git a/drivers/net/wireless/ath/ath10k/usb.h b/drivers/net/wireless/ath/ath10k/usb.h index 7e4cfbb673c9..517afbd73764 100644 --- a/drivers/net/wireless/ath/ath10k/usb.h +++ b/drivers/net/wireless/ath/ath10k/usb.h @@ -3,7 +3,7 @@ * Copyright (c) 2004-2011 Atheros Communications Inc. * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. * Copyright (c) 2016-2017 Erik Stromdahl - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _USB_H_ diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 8a2f87d0a3a3..ea18ed8939db 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _WMI_TLV_H #define _WMI_TLV_H diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index 78653f257925..92af8bc63362 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2015-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "mac.h" From 21d83c6e9e45156f4cad8c7cd6fc2d5bbc76f73e Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 8 Jun 2026 06:21:35 -0700 Subject: [PATCH 273/285] wifi: ath: Update copyright in testmode_i.h Update Qualcomm copyrights per current legal guidance. Assisted-by: Claude:claude-sonnet-4-6 Link: https://patch.msgid.link/20260608-ath12k-copyright-v2-4-37504d70b03c@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/testmode_i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/testmode_i.h b/drivers/net/wireless/ath/testmode_i.h index 980ef2f3f05f..78109d21a655 100644 --- a/drivers/net/wireless/ath/testmode_i.h +++ b/drivers/net/wireless/ath/testmode_i.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ /* "API" level of the ath testmode interface. Bump it after every From fdea4d44e4b9c3f7021c85f8cd766e84e224472d Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Tue, 9 Jun 2026 10:10:47 +0800 Subject: [PATCH 274/285] wifi: ath12k: fix EAPOL TX failure caused by stale tcl_metadata bits On WCN7850, after the following sequence: 1. load ath12k and connect to a non-MLO AP 2. disconnect and connect to an MLO AP 3. disconnect and reconnect to the non-MLO AP the third connection always fails with a 4-Way handshake timeout. The supplicant transmits message 2 of 4 four times in response to AP retries of message 1, but the AP never sees any of them. ath12k_dp_vdev_tx_attach() composes dp_link_vif->tcl_metadata using |=, but dp_link_vif is embedded in struct ath12k_dp_vif and its slots are reused across vif/peer teardown and setup. Since tcl_metadata is never cleared on detach, vdev_id bits from a previous attach remain set when the same link slot is reused with a different vdev_id. In this specific issue, the same link slot is used for vdev_id 0, then vdev_id 1, then vdev_id 0 again, the OR yields tcl_metadata == 0x9, which encodes vdev_id 1 in the HTT_TCL_META_DATA_VDEV_ID field even though ti.vdev_id is 0. Firmware then routes the EAPOL frame to the wrong vdev and the AP never receives message 2. Use plain assignment instead of |= so the field is fully recomputed from the current arvif on every attach. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3 Fixes: af66c7640cf9 ("wifi: ath12k: Refactor ath12k_vif structure") Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260609-ath12k-fix-eapol-tcl-metadata-v1-1-d47e6f90d4ee@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 90802ed1aa59..af5f11fc1d84 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -943,11 +943,11 @@ void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif) dp_link_vif = ath12k_dp_vif_to_dp_link_vif(&ahvif->dp_vif, link_id); - dp_link_vif->tcl_metadata |= u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) | - u32_encode_bits(arvif->vdev_id, - HTT_TCL_META_DATA_VDEV_ID) | - u32_encode_bits(ar->pdev->pdev_id, - HTT_TCL_META_DATA_PDEV_ID); + dp_link_vif->tcl_metadata = u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) | + u32_encode_bits(arvif->vdev_id, + HTT_TCL_META_DATA_VDEV_ID) | + u32_encode_bits(ar->pdev->pdev_id, + HTT_TCL_META_DATA_PDEV_ID); /* set HTT extension valid bit to 0 by default */ dp_link_vif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; From 63abe299b12b317dfee5bcd09037da4668a4431a Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Thu, 4 Jun 2026 15:28:31 +0530 Subject: [PATCH 275/285] wifi: ath12k: enable IEEE80211_VHT_EXT_NSS_BW_CAPABLE when NSS ratio is reported When firmware reports NSS ratio support, SUPPORTS_VHT_EXT_NSS_BW is enabled in ath12k. However, IEEE80211_VHT_EXT_NSS_BW_CAPABLE must also be set to make the advertisement valid. According to IEEE Std 802.11-2024, Subclause 9.4.2.156.3 (Supported VHT-MCS and NSS Set subfields), the VHT Extended NSS BW Capable bit indicates whether a STA is capable of interpreting the Extended NSS BW Support subfield of the VHT capabilities information field. Advertising extended NSS BW support without setting this capability bit is therefore invalid. Without this change, mac80211 detects the inconsistency and logs: ieee80211 phy0: copying sband (band 1) due to VHT EXT NSS BW flag This indicates that mac80211 implicitly aligns IEEE80211_VHT_EXT_NSS_BW_CAPABLE during ieee80211_register_hw(). Explicitly setting the bit in ath12k avoids this fixup and ensures capabilities are advertised correctly by the driver. This change follows the same approach as the existing ath11k fix. https://lore.kernel.org/all/20211013073704.15888-1-wgong@codeaurora.org/ Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1 Fixes: 18ab9d038fad ("wifi: ath12k: add support for 160 MHz bandwidth") Signed-off-by: Wen Gong Signed-off-by: Maharaja Kennadyrajan Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260604095831.2674298-1-maharaja.kennadyrajan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index a6e4b660da81..af354bef5c0d 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -8489,6 +8489,10 @@ ath12k_create_vht_cap(struct ath12k *ar, u32 rate_cap_tx_chainmask, vht_cap.vht_supported = 1; vht_cap.cap = ar->pdev->cap.vht_cap; + if (ar->pdev->cap.nss_ratio_enabled) + vht_cap.vht_mcs.tx_highest |= + cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); + ath12k_set_vht_txbf_cap(ar, &vht_cap.cap); /* 80P80 is not supported */ From 2fd192668620d0b367561c66df5dfe31de958dce Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 7 Jun 2026 22:28:54 -0700 Subject: [PATCH 276/285] wifi: brcm80211: change current_bss to value Change to a single allocation and remove some boilerplate. Signed-off-by: Rosen Penev Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260608052854.11718-1-rosenp@gmail.com Signed-off-by: Johannes Berg --- .../broadcom/brcm80211/brcmsmac/main.c | 40 +++---------------- .../broadcom/brcm80211/brcmsmac/main.h | 2 +- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c index c7eaf160e1fa..ec3aeaa7bab9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c @@ -418,21 +418,12 @@ static int brcms_chspec_bw(u16 chanspec) return BRCMS_10_MHZ; } -static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg) -{ - if (cfg == NULL) - return; - - kfree(cfg->current_bss); - kfree(cfg); -} - static void brcms_c_detach_mfree(struct brcms_c_info *wlc) { if (wlc == NULL) return; - brcms_c_bsscfg_mfree(wlc->bsscfg); + kfree(wlc->bsscfg); kfree(wlc->pub); kfree(wlc->modulecb); kfree(wlc->default_bss); @@ -453,25 +444,6 @@ static void brcms_c_detach_mfree(struct brcms_c_info *wlc) kfree(wlc); } -static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit) -{ - struct brcms_bss_cfg *cfg; - - cfg = kzalloc_obj(*cfg, GFP_ATOMIC); - if (cfg == NULL) - goto fail; - - cfg->current_bss = kzalloc_obj(*cfg->current_bss, GFP_ATOMIC); - if (cfg->current_bss == NULL) - goto fail; - - return cfg; - - fail: - brcms_c_bsscfg_mfree(cfg); - return NULL; -} - static struct brcms_c_info * brcms_c_attach_malloc(uint unit, uint *err, uint devid) { @@ -527,7 +499,7 @@ brcms_c_attach_malloc(uint unit, uint *err, uint devid) goto fail; } - wlc->bsscfg = brcms_c_bsscfg_malloc(unit); + wlc->bsscfg = kzalloc_obj(*wlc->bsscfg, GFP_ATOMIC); if (wlc->bsscfg == NULL) { *err = 1011; goto fail; @@ -3813,7 +3785,7 @@ static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) wlc->home_chanspec = chanspec; if (wlc->pub->associated) - wlc->bsscfg->current_bss->chanspec = chanspec; + wlc->bsscfg->current_bss.chanspec = chanspec; } } @@ -5423,7 +5395,7 @@ void brcms_c_get_current_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs; if (wlc->pub->associated) - rs = &wlc->bsscfg->current_bss->rateset; + rs = &wlc->bsscfg->current_bss.rateset; else rs = &wlc->default_bss->rateset; @@ -5450,7 +5422,7 @@ int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs) if (wlc->pub->_n_enab & SUPPORT_11N) { struct brcms_bss_info *mcsset_bss; if (wlc->pub->associated) - mcsset_bss = wlc->bsscfg->current_bss; + mcsset_bss = &wlc->bsscfg->current_bss; else mcsset_bss = wlc->default_bss; memcpy(internal_rs.mcs, &mcsset_bss->rateset.mcs[0], @@ -7809,7 +7781,7 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) u32 bi; /* get beacon period and convert to uS */ - bi = wlc->bsscfg->current_bss->beacon_period << 10; + bi = wlc->bsscfg->current_bss.beacon_period << 10; /* * update since init path would reset * to default value diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h index b7ca0d9891c4..2d133264f4a7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h @@ -614,7 +614,7 @@ struct brcms_bss_cfg { u8 SSID_len; u8 SSID[IEEE80211_MAX_SSID_LEN]; u8 BSSID[ETH_ALEN]; - struct brcms_bss_info *current_bss; + struct brcms_bss_info current_bss; }; int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p); From 201dd93a177806747af7b99f1021b6fb4d042ed7 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 7 Jun 2026 22:11:02 -0700 Subject: [PATCH 277/285] wifi: brcmfmac: flowring: simplify flow allocation Use a flexible array member and kzalloc_flex to combine allocations. Simplifies code slightly. Add __counted_by for extra runtime analysis. Signed-off-by: Rosen Penev Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260608051102.6698-1-rosenp@gmail.com Signed-off-by: Johannes Berg --- .../wireless/broadcom/brcm80211/brcmfmac/flowring.c | 10 ++-------- .../wireless/broadcom/brcm80211/brcmfmac/flowring.h | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c index df7e3bee19f2..35cbcea0abc9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c @@ -360,20 +360,15 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) struct brcmf_flowring *flow; u32 i; - flow = kzalloc_obj(*flow); + flow = kzalloc_flex(*flow, rings, nrofrings); if (flow) { - flow->dev = dev; flow->nrofrings = nrofrings; + flow->dev = dev; spin_lock_init(&flow->block_lock); for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) flow->addr_mode[i] = ADDR_INDIRECT; for (i = 0; i < ARRAY_SIZE(flow->hash); i++) flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; - flow->rings = kzalloc_objs(*flow->rings, nrofrings); - if (!flow->rings) { - kfree(flow); - flow = NULL; - } } return flow; @@ -399,7 +394,6 @@ void brcmf_flowring_detach(struct brcmf_flowring *flow) search = search->next; kfree(remove); } - kfree(flow->rings); kfree(flow); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h index 818882b0fd01..f3d511f9a3c9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h @@ -38,12 +38,12 @@ struct brcmf_flowring_tdls_entry { struct brcmf_flowring { struct device *dev; struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; - struct brcmf_flowring_ring **rings; spinlock_t block_lock; enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; u16 nrofrings; bool tdls_active; struct brcmf_flowring_tdls_entry *tdls_entry; + struct brcmf_flowring_ring *rings[] __counted_by(nrofrings); }; From 4cd243ef30d185e99098d2191302a3dc49bfef58 Mon Sep 17 00:00:00 2001 From: David Laight Date: Sat, 6 Jun 2026 21:26:02 +0100 Subject: [PATCH 278/285] rfkill: Replace strcpy() with memcpy() The length of the string is calculated in order to allocate the correct sized memory block, use the same length to copy the string. Signed-off-by: David Laight Link: https://patch.msgid.link/20260606202633.5018-8-david.laight.linux@gmail.com Signed-off-by: Johannes Berg --- net/rfkill/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 4827e1fb8804..9e143c4bfe6a 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -1000,6 +1000,7 @@ struct rfkill * __must_check rfkill_alloc(const char *name, { struct rfkill *rfkill; struct device *dev; + size_t name_len; if (WARN_ON(!ops)) return NULL; @@ -1013,14 +1014,15 @@ struct rfkill * __must_check rfkill_alloc(const char *name, if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES)) return NULL; - rfkill = kzalloc(sizeof(*rfkill) + strlen(name) + 1, GFP_KERNEL); + name_len = strlen(name); + rfkill = kzalloc(sizeof(*rfkill) + name_len + 1, GFP_KERNEL); if (!rfkill) return NULL; spin_lock_init(&rfkill->lock); INIT_LIST_HEAD(&rfkill->node); rfkill->type = type; - strcpy(rfkill->name, name); + memcpy(rfkill->name, name, name_len); rfkill->ops = ops; rfkill->data = ops_data; From 682edc28b91c2f1bba32e1c6815c33ef799ec538 Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Thu, 28 May 2026 19:31:35 +0200 Subject: [PATCH 279/285] b43: add firmware mappings for rev22 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add the specific firmware mappings for rev22, and drop comments wondering about rev22 initvals Assisted-by: Claude:claude-4.7-opus Signed-off-by: Alessio Ferri Acked-by: Michael Büsch Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260528-b43_complete_n_phy_rev_8_radio_2057_rev_8_support-v4-1-464566194d47@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/b43/main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index b0e6aeb0b872..37c5d9928315 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -2344,6 +2344,10 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) if (phy->type == B43_PHYTYPE_N) filename = "ucode16_mimo"; break; + case 22: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode22_mimo"; + break; case 16 ... 19: if (phy->type == B43_PHYTYPE_N) filename = "ucode16_mimo"; @@ -2405,7 +2409,9 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else if (rev == 24) filename = "n0initvals24"; else if (rev == 23) - filename = "n0initvals16"; /* What about n0initvals22? */ + filename = "n0initvals16"; + else if (rev == 22) + filename = "n0initvals22"; else if (rev >= 16 && rev <= 18) filename = "n0initvals16"; else if (rev >= 11 && rev <= 12) @@ -2465,7 +2471,9 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else if (rev == 24) filename = "n0bsinitvals24"; else if (rev == 23) - filename = "n0bsinitvals16"; /* What about n0bsinitvals22? */ + filename = "n0bsinitvals16"; + else if (rev == 22) + filename = "n0bsinitvals22"; else if (rev >= 16 && rev <= 18) filename = "n0bsinitvals16"; else if (rev >= 11 && rev <= 12) From ee81dc7636fb808a5f480d2767130a7cf3554f9a Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Thu, 28 May 2026 19:31:36 +0200 Subject: [PATCH 280/285] b43: add d11 core revision 0x16 to id table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add d11 core revision 0x16 (= 22) to the b43 bcma device id table. The b43 bcma id table covers d11 revisions 0x11, 0x15, 0x17, 0x18, 0x1C, 0x1D, 0x1E, 0x28 and 0x2A. Revision 0x16 belongs to the same N-PHY family as revisions 0x17 and 0x18 (radio 2057) and needs no new PHY or radio code beyond the radio_rev 8 dispatcher entries added later in this series - only the device id entry is missing. Without it bcma scan enumerates the 802.11 core but no driver binds. The revision is used by the Broadcom BCM6362 single-die integrated 2.4 GHz wireless block found in xDSL SoCs. Assisted-by: Claude:claude-4.7-opus Signed-off-by: Alessio Ferri Acked-by: Michael Büsch Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260528-b43_complete_n_phy_rev_8_radio_2057_rev_8_support-v4-2-464566194d47@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/b43/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 37c5d9928315..85ea8fdd9c0e 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -117,6 +117,7 @@ MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if over static const struct bcma_device_id b43_bcma_tbl[] = { BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x15, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x16, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS), From 2691a1ae6bcc7c8a12ba10af99d1283a0bf69e9d Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Thu, 28 May 2026 19:31:37 +0200 Subject: [PATCH 281/285] b43: route d11 corerev 22 to 24-bit indirect radio access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rev 22 backports the older 802.11 core but pairs it with a radio in the 2057 family, which requires the 24-bit indirect path. With the current dispatch, corerev 22 falls into the legacy 4-wire branch, reads garbage for radio_id, and bails out with -EOPNOTSUPP at the "FOUND UNSUPPORTED RADIO" branch below. brcmsmac handles the same silicon family with the equivalent dispatch in drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/ phy_cmn.c read_radio_reg() and write_radio_reg(): if ((D11REV_GE(pi->sh->corerev, 24)) || (D11REV_IS(pi->sh->corerev, 22) && (pi->pubpi.phy_type != PHY_TYPE_SSN))) { /* radioregaddr / radioregdata (indirect) */ } else { /* phy4waddr / phy4wdatalo (legacy) */ } b43 does not support SSN/SSLPN PHYs - they are rejected earlier in b43_phy_versioning() at the "unsupported PHY type" switch - so just adding the check corerev == 22 will do. Assisted-by: Claude:claude-4.7-opus Signed-off-by: Alessio Ferri Acked-by: Michael Büsch Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260528-b43_complete_n_phy_rev_8_radio_2057_rev_8_support-v4-3-464566194d47@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/b43/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 85ea8fdd9c0e..783af26cb6f0 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -4563,7 +4563,11 @@ static int b43_phy_versioning(struct b43_wldev *dev) radio_id = b43_read16(dev, B43_MMIO_RADIO24_DATA); radio_ver = 0; /* Is there version somewhere? */ - } else if (core_rev >= 24) { + } else if (core_rev >= 24 || core_rev == 22) { + /* + * D11 corerev 22 pairs an older 802.11 core with a 2057 + * radio that requires the 24-bit indirect access path. + */ u16 radio24[3]; for (tmp = 0; tmp < 3; tmp++) { From 454518d95d07ce0ea3d9f16cef33fd17e3d8a1f0 Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Thu, 28 May 2026 19:31:38 +0200 Subject: [PATCH 282/285] b43: support radio 2057 rev 8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for radio 2057 revision 8, paired with N-PHY rev 8 on the Broadcom BCM6362 single-die integrated 2.4 GHz wireless block. Three correlated changes are needed for the same chip: - main.c: the radio_rev allow-list under B43_PHYTYPE_N currently accepts radio 2057 revisions 9 and 14 only; extend to include rev 8. - radio_2057.c: the existing r2057_rev8_init[] is a 54-entry stub declared inside a TODO comment block and never referenced from r2057_upload_inittabs(). Replace it with the full 412-entry register set actually programmed by the proprietary Broadcom wl driver on this radio. I couldn't find the origin of the original 54-entry stub - 8 of its entries do not appear at all in the rev 8 register set and 7 more carry different values. Loading it instead of using the real table leaves the radio hanging producing a "Microcode not responding" timeout. - radio_2057.c: r2057_upload_inittabs() case 8 handles radio_rev 5 and 7 only; add the radio_rev == 8 branch pointing at the new table. The init table is extracted from an MMIO dump of the radio register set programmed during proprietary driver initialisation on BCM6362 silicon (Broadcom wl driver 6.30.102.7). Assisted-by: Claude:claude-4.7-opus Signed-off-by: Alessio Ferri Acked-by: Michael Büsch Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260528-b43_complete_n_phy_rev_8_radio_2057_rev_8_support-v4-4-464566194d47@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/b43/main.c | 3 +- .../net/wireless/broadcom/b43/radio_2057.c | 124 +++++++++++++++--- 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 783af26cb6f0..817448e58e7f 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -4617,7 +4617,8 @@ static int b43_phy_versioning(struct b43_wldev *dev) radio_id != 0x2057) unsupported = 1; if (radio_id == 0x2057 && - !(radio_rev == 9 || radio_rev == 14)) + !(radio_rev == 8 || radio_rev == 9 || + radio_rev == 14)) unsupported = 1; break; case B43_PHYTYPE_LP: diff --git a/drivers/net/wireless/broadcom/b43/radio_2057.c b/drivers/net/wireless/broadcom/b43/radio_2057.c index bd7dafb567ff..9f693d92bb0a 100644 --- a/drivers/net/wireless/broadcom/b43/radio_2057.c +++ b/drivers/net/wireless/broadcom/b43/radio_2057.c @@ -73,24 +73,112 @@ static u16 r2057_rev7_init[][2] = { { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, }; -/* TODO: Which devices should use it? +/* Extracted from MMIO dump of 6.30.102.7 */ static u16 r2057_rev8_init[][2] = { - { 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, - { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, - { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, - { 0x6E, 0x58 }, { 0x75, 0x13 }, { 0x7B, 0x13 }, { 0x7C, 0x0f }, - { 0x7D, 0xee }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, - { 0xA1, 0x20 }, { 0xC9, 0x01 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, - { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0xF3, 0x58 }, - { 0xFA, 0x13 }, { 0x100, 0x13 }, { 0x101, 0x0f }, { 0x102, 0xee }, - { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x126, 0x20 }, - { 0x14E, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, - { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, - { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, - { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, - { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, + { 0x0000, 0x0008 }, { 0x0001, 0x0057 }, { 0x0002, 0x0020 }, { 0x0003, 0x001f }, + { 0x0004, 0x0004 }, { 0x0005, 0x0002 }, { 0x0006, 0x0001 }, { 0x0007, 0x0001 }, + { 0x0008, 0x0001 }, { 0x0009, 0x0069 }, { 0x000a, 0x0066 }, { 0x000b, 0x0006 }, + { 0x000c, 0x0018 }, { 0x000d, 0x0003 }, { 0x000e, 0x0020 }, { 0x000f, 0x0020 }, + { 0x0010, 0x0000 }, { 0x0011, 0x007c }, { 0x0012, 0x0042 }, { 0x0013, 0x00bd }, + { 0x0014, 0x0007 }, { 0x0015, 0x0087 }, { 0x0016, 0x0008 }, { 0x0017, 0x0017 }, + { 0x0018, 0x0007 }, { 0x0019, 0x0000 }, { 0x001a, 0x0002 }, { 0x001b, 0x0013 }, + { 0x001c, 0x003e }, { 0x001d, 0x003e }, { 0x001e, 0x0096 }, { 0x001f, 0x0004 }, + { 0x0020, 0x0000 }, { 0x0021, 0x0000 }, { 0x0022, 0x0017 }, { 0x0023, 0x0006 }, + { 0x0024, 0x0001 }, { 0x0025, 0x0006 }, { 0x0026, 0x0004 }, { 0x0027, 0x000d }, + { 0x0028, 0x000d }, { 0x0029, 0x0030 }, { 0x002a, 0x0032 }, { 0x002b, 0x0008 }, + { 0x002c, 0x001c }, { 0x002d, 0x0002 }, { 0x002e, 0x0004 }, { 0x002f, 0x007f }, + { 0x0030, 0x0027 }, { 0x0031, 0x0000 }, { 0x0032, 0x0000 }, { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, { 0x0035, 0x0020 }, { 0x0036, 0x0018 }, { 0x0037, 0x0007 }, + { 0x0038, 0x0066 }, { 0x0039, 0x0066 }, { 0x003a, 0x0066 }, { 0x003b, 0x0066 }, + { 0x003c, 0x00ff }, { 0x003d, 0x00ff }, { 0x003e, 0x00ff }, { 0x003f, 0x00ff }, + { 0x0040, 0x0016 }, { 0x0041, 0x0007 }, { 0x0042, 0x0029 }, { 0x0043, 0x0007 }, + { 0x0044, 0x0006 }, { 0x0045, 0x0003 }, { 0x0046, 0x0001 }, { 0x0047, 0x0007 }, + { 0x0048, 0x0088 }, { 0x0049, 0x0005 }, { 0x004a, 0x0077 }, { 0x004b, 0x0066 }, + { 0x004c, 0x0066 }, { 0x004d, 0x0000 }, { 0x004e, 0x0004 }, { 0x004f, 0x000c }, + { 0x0050, 0x0000 }, { 0x0051, 0x0070 }, { 0x0056, 0x0007 }, { 0x0057, 0x0000 }, + { 0x0058, 0x0000 }, { 0x0059, 0x0088 }, { 0x005a, 0x0000 }, { 0x005b, 0x001f }, + { 0x005c, 0x0020 }, { 0x005d, 0x0001 }, { 0x005e, 0x0030 }, { 0x005f, 0x0070 }, + { 0x0060, 0x0000 }, { 0x0061, 0x0000 }, { 0x0062, 0x0033 }, { 0x0063, 0x000f }, + { 0x0064, 0x0013 }, { 0x0065, 0x0000 }, { 0x0066, 0x00ee }, { 0x0069, 0x0000 }, + { 0x006a, 0x007e }, { 0x006b, 0x003f }, { 0x006c, 0x007f }, { 0x006d, 0x0078 }, + { 0x006e, 0x0058 }, { 0x006f, 0x0088 }, { 0x0070, 0x0008 }, { 0x0071, 0x000f }, + { 0x0072, 0x00bc }, { 0x0073, 0x0008 }, { 0x0074, 0x0060 }, { 0x0075, 0x001a }, + { 0x0076, 0x0070 }, { 0x0077, 0x0000 }, { 0x0078, 0x0000 }, { 0x0079, 0x0000 }, + { 0x007a, 0x0033 }, { 0x007b, 0x001a }, { 0x007c, 0x0014 }, { 0x007d, 0x00ee }, + { 0x0080, 0x003c }, { 0x0081, 0x0001 }, { 0x0082, 0x000a }, { 0x0083, 0x009d }, + { 0x0084, 0x000a }, { 0x0085, 0x0000 }, { 0x0086, 0x0040 }, { 0x0087, 0x0040 }, + { 0x0088, 0x0088 }, { 0x0089, 0x0010 }, { 0x008a, 0x00f0 }, { 0x008b, 0x0010 }, + { 0x008c, 0x00f0 }, { 0x008d, 0x0000 }, { 0x008e, 0x0000 }, { 0x008f, 0x0010 }, + { 0x0090, 0x0055 }, { 0x0091, 0x003f }, { 0x0092, 0x0036 }, { 0x0093, 0x0000 }, + { 0x0094, 0x0000 }, { 0x0095, 0x0000 }, { 0x0096, 0x0087 }, { 0x0097, 0x0011 }, + { 0x0098, 0x0000 }, { 0x0099, 0x0033 }, { 0x009a, 0x0088 }, { 0x009b, 0x0000 }, + { 0x009c, 0x0087 }, { 0x009d, 0x0011 }, { 0x009e, 0x0000 }, { 0x009f, 0x0033 }, + { 0x00a0, 0x0088 }, { 0x00a1, 0x0020 }, { 0x00a2, 0x003f }, { 0x00a3, 0x0044 }, + { 0x00a4, 0x008c }, { 0x00a5, 0x006c }, { 0x00a6, 0x0022 }, { 0x00a7, 0x00be }, + { 0x00a8, 0x0055 }, { 0x00aa, 0x000c }, { 0x00ab, 0x00aa }, { 0x00ac, 0x0002 }, + { 0x00ad, 0x0000 }, { 0x00ae, 0x0010 }, { 0x00af, 0x0001 }, { 0x00b0, 0x0000 }, + { 0x00b1, 0x0000 }, { 0x00b2, 0x0080 }, { 0x00b3, 0x0060 }, { 0x00b4, 0x0044 }, + { 0x00b5, 0x0055 }, { 0x00b6, 0x0001 }, { 0x00b7, 0x0055 }, { 0x00b8, 0x0001 }, + { 0x00b9, 0x0005 }, { 0x00ba, 0x0055 }, { 0x00bb, 0x0055 }, { 0x00c1, 0x0000 }, + { 0x00c2, 0x0000 }, { 0x00c3, 0x0000 }, { 0x00c4, 0x0000 }, { 0x00c5, 0x0000 }, + { 0x00c6, 0x0000 }, { 0x00c7, 0x0000 }, { 0x00c8, 0x0000 }, { 0x00c9, 0x0001 }, + { 0x00ca, 0x0000 }, { 0x00cb, 0x0000 }, { 0x00cc, 0x0000 }, { 0x00cd, 0x0000 }, + { 0x00ce, 0x005e }, { 0x00cf, 0x000c }, { 0x00d0, 0x000c }, { 0x00d1, 0x000c }, + { 0x00d2, 0x0000 }, { 0x00d3, 0x002b }, { 0x00d4, 0x000c }, { 0x00d5, 0x0000 }, + { 0x00d6, 0x0070 }, { 0x00db, 0x0007 }, { 0x00dc, 0x0000 }, { 0x00dd, 0x0000 }, + { 0x00de, 0x0088 }, { 0x00df, 0x0000 }, { 0x00e0, 0x001f }, { 0x00e1, 0x0020 }, + { 0x00e2, 0x0001 }, { 0x00e3, 0x0030 }, { 0x00e4, 0x0070 }, { 0x00e5, 0x0000 }, + { 0x00e6, 0x0000 }, { 0x00e7, 0x0033 }, { 0x00e8, 0x000f }, { 0x00e9, 0x0013 }, + { 0x00ea, 0x0000 }, { 0x00eb, 0x00ee }, { 0x00ee, 0x0000 }, { 0x00ef, 0x007e }, + { 0x00f0, 0x003f }, { 0x00f1, 0x007f }, { 0x00f2, 0x0078 }, { 0x00f3, 0x0058 }, + { 0x00f4, 0x0088 }, { 0x00f5, 0x0008 }, { 0x00f6, 0x000f }, { 0x00f7, 0x00bc }, + { 0x00f8, 0x0008 }, { 0x00f9, 0x0060 }, { 0x00fa, 0x001a }, { 0x00fb, 0x0070 }, + { 0x00fc, 0x0000 }, { 0x00fd, 0x0000 }, { 0x00fe, 0x0000 }, { 0x00ff, 0x0033 }, + { 0x0100, 0x001a }, { 0x0101, 0x0014 }, { 0x0102, 0x00ee }, { 0x0105, 0x003c }, + { 0x0106, 0x0001 }, { 0x0107, 0x000a }, { 0x0108, 0x009d }, { 0x0109, 0x000a }, + { 0x010a, 0x0000 }, { 0x010b, 0x0040 }, { 0x010c, 0x0040 }, { 0x010d, 0x0088 }, + { 0x010e, 0x0010 }, { 0x010f, 0x00f0 }, { 0x0110, 0x0010 }, { 0x0111, 0x00f0 }, + { 0x0112, 0x0000 }, { 0x0113, 0x0000 }, { 0x0114, 0x0010 }, { 0x0115, 0x0055 }, + { 0x0116, 0x003f }, { 0x0117, 0x0036 }, { 0x0118, 0x0000 }, { 0x0119, 0x0000 }, + { 0x011a, 0x0000 }, { 0x011b, 0x0087 }, { 0x011c, 0x0011 }, { 0x011d, 0x0000 }, + { 0x011e, 0x0033 }, { 0x011f, 0x0088 }, { 0x0120, 0x0000 }, { 0x0121, 0x0087 }, + { 0x0122, 0x0011 }, { 0x0123, 0x0000 }, { 0x0124, 0x0033 }, { 0x0125, 0x0088 }, + { 0x0126, 0x0020 }, { 0x0127, 0x003f }, { 0x0128, 0x0044 }, { 0x0129, 0x008c }, + { 0x012a, 0x006c }, { 0x012b, 0x0022 }, { 0x012c, 0x00be }, { 0x012d, 0x0055 }, + { 0x012f, 0x000c }, { 0x0130, 0x00aa }, { 0x0131, 0x0002 }, { 0x0132, 0x0000 }, + { 0x0133, 0x0010 }, { 0x0134, 0x0001 }, { 0x0135, 0x0000 }, { 0x0136, 0x0000 }, + { 0x0137, 0x0080 }, { 0x0138, 0x0060 }, { 0x0139, 0x0044 }, { 0x013a, 0x0055 }, + { 0x013b, 0x0001 }, { 0x013c, 0x0055 }, { 0x013d, 0x0001 }, { 0x013e, 0x0005 }, + { 0x013f, 0x0055 }, { 0x0140, 0x0055 }, { 0x0146, 0x0000 }, { 0x0147, 0x0000 }, + { 0x0148, 0x0000 }, { 0x0149, 0x0000 }, { 0x014a, 0x0000 }, { 0x014b, 0x0000 }, + { 0x014c, 0x0000 }, { 0x014d, 0x0000 }, { 0x014e, 0x0001 }, { 0x014f, 0x0000 }, + { 0x0150, 0x0000 }, { 0x0151, 0x0000 }, { 0x0154, 0x000c }, { 0x0155, 0x000c }, + { 0x0156, 0x000c }, { 0x0157, 0x0000 }, { 0x0158, 0x002b }, { 0x0159, 0x0084 }, + { 0x015a, 0x0015 }, { 0x015b, 0x000f }, { 0x015c, 0x0000 }, { 0x015d, 0x0000 }, + { 0x015e, 0x0000 }, { 0x015f, 0x0000 }, { 0x0160, 0x0000 }, { 0x0161, 0x0000 }, + { 0x0162, 0x0000 }, { 0x0163, 0x0000 }, { 0x0164, 0x0000 }, { 0x0165, 0x0000 }, + { 0x0166, 0x0000 }, { 0x0167, 0x0000 }, { 0x0168, 0x0000 }, { 0x0169, 0x0000 }, + { 0x016a, 0x0000 }, { 0x016b, 0x0000 }, { 0x016c, 0x0000 }, { 0x016d, 0x0000 }, + { 0x0170, 0x0000 }, { 0x0171, 0x0077 }, { 0x0172, 0x0077 }, { 0x0173, 0x0077 }, + { 0x0174, 0x0077 }, { 0x0175, 0x0000 }, { 0x0176, 0x0003 }, { 0x0177, 0x0037 }, + { 0x0178, 0x0003 }, { 0x0179, 0x0000 }, { 0x017a, 0x0021 }, { 0x017b, 0x0002 }, + { 0x017c, 0x0000 }, { 0x017d, 0x00aa }, { 0x017e, 0x0000 }, { 0x017f, 0x00aa }, + { 0x0180, 0x0000 }, { 0x0190, 0x0000 }, { 0x0191, 0x0077 }, { 0x0192, 0x0077 }, + { 0x0193, 0x0077 }, { 0x0194, 0x0077 }, { 0x0195, 0x0000 }, { 0x0196, 0x0003 }, + { 0x0197, 0x0037 }, { 0x0198, 0x0003 }, { 0x0199, 0x0000 }, { 0x019a, 0x0021 }, + { 0x019b, 0x0002 }, { 0x019c, 0x0000 }, { 0x019d, 0x00aa }, { 0x019e, 0x0000 }, + { 0x019f, 0x00aa }, { 0x01a0, 0x0000 }, { 0x01a1, 0x0002 }, { 0x01a2, 0x000f }, + { 0x01a3, 0x000f }, { 0x01a4, 0x0000 }, { 0x01a5, 0x0000 }, { 0x01a6, 0x0000 }, + { 0x01a7, 0x0002 }, { 0x01a8, 0x000f }, { 0x01a9, 0x000f }, { 0x01aa, 0x0000 }, + { 0x01ab, 0x0000 }, { 0x01ac, 0x0000 }, { 0x01ad, 0x0084 }, { 0x01ae, 0x0060 }, + { 0x01af, 0x0047 }, { 0x01b0, 0x0047 }, { 0x01b1, 0x0000 }, { 0x01b2, 0x0000 }, + { 0x01b3, 0x0000 }, { 0x01b4, 0x0000 }, { 0x01b5, 0x0000 }, { 0x01b6, 0x0000 }, + { 0x01b7, 0x0005 }, { 0x01b8, 0x0000 }, { 0x01b9, 0x0000 }, { 0x01ba, 0x0000 }, + { 0x01bb, 0x0000 }, { 0x01bc, 0x0000 }, { 0x01bd, 0x0000 }, { 0x01be, 0x0000 }, + { 0x01bf, 0x0000 }, { 0x01c0, 0x0000 }, { 0x01c1, 0x0000 }, { 0x01c2, 0x00a0 }, + { 0x01c3, 0x0000 }, { 0x01c4, 0x0000 }, { 0x01c5, 0x0000 }, { 0x01c6, 0x0000 }, + { 0x01c7, 0x0000 }, { 0x01c8, 0x0000 }, { 0x01c9, 0x0000 }, { 0x01ca, 0x0000 }, }; -*/ /* Extracted from MMIO dump of 6.30.223.141 */ static u16 r2057_rev9_init[][2] = { @@ -539,6 +627,10 @@ void r2057_upload_inittabs(struct b43_wldev *dev) } else if (phy->radio_rev == 7) { table = r2057_rev7_init[0]; size = ARRAY_SIZE(r2057_rev7_init); + } else if (phy->radio_rev == 8) { + /* BCM6362 single-die 2.4 GHz. */ + table = r2057_rev8_init[0]; + size = ARRAY_SIZE(r2057_rev8_init); } break; case 9: From 894f1482b2f9476d23b803c284fa06af31ecd018 Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Thu, 28 May 2026 19:31:39 +0200 Subject: [PATCH 283/285] b43: add IPA TX gain table for N-PHY r8 + radio 2057 r8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the 2.4 GHz IPA TX gain table for N-PHY rev 8 paired with radio 2057 rev 8 and wire it to the existing dispatcher. b43_nphy_get_ipa_gain_table() in tables_nphy.c currently handles case 8 only for radio_rev == 5; radio_rev == 8 falls through and the function logs: b43-phyX ERROR: No 2GHz IPA gain table available for this device b43-phyX ERROR: PHY init: Channel switch to default failed leaving b43_phy_init() to return an error and core_init to abort before the MAC is enabled. The high byte of every entry differs from the rev 5 sibling (0x40 vs 0x30): different PAD-gain code prefix for the rev 8 front-end. The low 24 bits coincide with rev 5 across the whole table - the gain step amplitudes are the same, only the PAD-gain selector prefix changes. Values extracted from an MMIO dump of the proprietary Broadcom wl driver running on BCM6362 silicon (wl driver 6.30.102.7). Assisted-by: Claude:claude-4.7-opus Signed-off-by: Alessio Ferri Acked-by: Michael Büsch Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260528-b43_complete_n_phy_rev_8_radio_2057_rev_8_support-v4-5-464566194d47@gmail.com Signed-off-by: Johannes Berg --- .../net/wireless/broadcom/b43/tables_nphy.c | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/net/wireless/broadcom/b43/tables_nphy.c b/drivers/net/wireless/broadcom/b43/tables_nphy.c index 41a25d909d0d..84e8d718d775 100644 --- a/drivers/net/wireless/broadcom/b43/tables_nphy.c +++ b/drivers/net/wireless/broadcom/b43/tables_nphy.c @@ -2715,6 +2715,43 @@ static const u32 b43_ntab_tx_gain_ipa_2057_rev5_2g[] = { 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, }; +/* Extracted from MMIO dump of 6.30.102.7 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev8_2g[] = { + 0x40ff0031, 0x40e70031, 0x40e7002e, 0x40cf002e, + 0x40bf002e, 0x40af002e, 0x409f002f, 0x407f0033, + 0x407f0031, 0x407f002e, 0x4077002e, 0x406f002e, + 0x4067002e, 0x405f002f, 0x40570030, 0x4057002d, + 0x404f002e, 0x40470031, 0x4047002e, 0x4047002c, + 0x40470029, 0x403f002c, 0x403f0029, 0x4037002d, + 0x4037002a, 0x40370028, 0x402f002c, 0x402f002a, + 0x402f0028, 0x402f0026, 0x4027002c, 0x40270029, + 0x40270027, 0x40270025, 0x40270023, 0x401f002c, + 0x401f002a, 0x401f0028, 0x401f0025, 0x401f0024, + 0x401f0022, 0x401f001f, 0x4017002d, 0x4017002b, + 0x40170028, 0x40170026, 0x40170024, 0x40170022, + 0x40170020, 0x4017001e, 0x4017001d, 0x4017001b, + 0x4017001a, 0x40170018, 0x40170017, 0x40170015, + 0x400f002c, 0x400f0029, 0x400f0027, 0x400f0024, + 0x400f0022, 0x400f0021, 0x400f001f, 0x400f001d, + 0x400f001b, 0x400f001a, 0x400f0018, 0x400f0017, + 0x400f0016, 0x400f0015, 0x400f0115, 0x400f0215, + 0x400f0315, 0x400f0415, 0x400f0515, 0x400f0615, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, + 0x400f0715, 0x400f0715, 0x400f0715, 0x400f0715, +}; + + /* Extracted from MMIO dump of 6.30.223.141 */ static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = { 0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029, @@ -3651,6 +3688,8 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) case 8: if (phy->radio_rev == 5) return b43_ntab_tx_gain_ipa_2057_rev5_2g; + if (phy->radio_rev == 8) + return b43_ntab_tx_gain_ipa_2057_rev8_2g; break; case 6: if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) From 631c004e5f4549519f29d1acdb1dd92ac843392d Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Thu, 28 May 2026 19:31:40 +0200 Subject: [PATCH 284/285] b43: add channel info table for N-PHY r8 + radio 2057 r8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the 2.4 GHz channel info table for N-PHY rev 8 paired with radio 2057 rev 8 and wire it to the existing dispatcher in r2057_get_chantabent_rev7(). The dispatcher's case 8 currently handles radio_rev == 5 only. For radio_rev == 8 both output pointers stay NULL, b43_nphy_set_channel() returns an error and channel switch to the default channel fails. The new b43_nphy_chantab_phy_rev8_radio_rev8[] is 14 entries covering the standard 2.4 GHz channel set (2412..2472 in 5 MHz steps, plus 2484 for channel 14). Values extracted from an MMIO dump of the proprietary Broadcom wl driver running on BCM6362 silicon (wl driver 6.30.102.7). Assisted-by: Claude:claude-4.7-opus Signed-off-by: Alessio Ferri Acked-by: Michael Büsch Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260528-b43_complete_n_phy_rev_8_radio_2057_rev_8_support-v4-6-464566194d47@gmail.com Signed-off-by: Johannes Berg --- .../net/wireless/broadcom/b43/radio_2057.c | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/drivers/net/wireless/broadcom/b43/radio_2057.c b/drivers/net/wireless/broadcom/b43/radio_2057.c index 9f693d92bb0a..e761f899bacd 100644 --- a/drivers/net/wireless/broadcom/b43/radio_2057.c +++ b/drivers/net/wireless/broadcom/b43/radio_2057.c @@ -445,6 +445,109 @@ static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev17_radio }, }; +/* Extracted from MMIO dump of 6.30.102.7 */ +static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev8_radio_rev8[] = { + { + .freq = 2412, + RADIOREGS7_2G(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, + 0x09, 0x0f, 0x09, 0x07, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, + 0x09, 0x0f, 0x09, 0x07, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, + 0x09, 0x0f, 0x09, 0x06, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS7_2G(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, + 0x09, 0x0f, 0x09, 0x06, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS7_2G(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, + 0x09, 0x0f, 0x09, 0x06, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS7_2G(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, + 0x09, 0x0f, 0x09, 0x06, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS7_2G(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS7_2G(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS7_2G(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, + 0x09, 0x0f, 0x08, 0x05, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { + .freq = 2467, + RADIOREGS7_2G(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, + 0x09, 0x0f, 0x07, 0x04, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { + .freq = 2472, + RADIOREGS7_2G(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, + 0x09, 0x0f, 0x07, 0x04, 0x61, 0x73, 0xf0, 0x61, + 0x73, 0xf0), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { + .freq = 2484, + RADIOREGS7_2G(0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, + 0x09, 0x0f, 0x07, 0x04, 0x61, 0x73, 0xe0, 0x61, + 0x73, 0xe0), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + } +}; + + /* Extracted from MMIO dump of 6.30.223.141 */ static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = { { @@ -678,6 +781,9 @@ void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, if (phy->radio_rev == 5) { e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev5; len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev5); + } else if (phy->radio_rev == 8) { + e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev8; + len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev8); } break; case 16: From 21352612198c83a8441482abbf3bd45e4f128dd0 Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Thu, 28 May 2026 19:31:41 +0200 Subject: [PATCH 285/285] b43: add RF power offset for N-PHY r8 + radio 2057 r8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the 2.4 GHz RF power offset table for N-PHY rev 8 paired with radio 2057 rev 8 and wire it to the existing dispatcher. b43_ntab_get_rf_pwr_offset_table() currently dispatches on phy->rev == 17 (radio_rev 14) and phy->rev == 16 (radio_rev 9) for 2.4 GHz. phy->rev == 8 falls through and the function logs: b43-phyX ERROR: No 2GHz RF power table available for this device Add a phy->rev == 8 / radio_rev == 8 case returning the new table. The values are sourced from the proprietary Broadcom wl driver's nphy_papd_padgain_dlt_2g_2057rev5 array. Reusing the rev 5 values is structurally appropriate: the IPA TX gain table added by the preceding patch in this series shares the low 24 bits of every entry with rev 5 - same gain step amplitudes, only the PAD-gain selector byte differs. b43's pad_gain extraction in b43_nphy_tx_pwr_ctl_init() reads bits 19..23 of the gain entry, which sit in the shared low-24-bit range; the same gain index therefore maps to the same physical PAD gain code on both revisions and warrants the same per-index dB offset. Note that b43_nphy_tx_gain_table_upload() currently has a "TODO: Enable this once we have gains configured" early-return for phy->rev >= 7. With that early-return in place, this table is fetched (silencing the b43err that would otherwise abort PHY init) but its values are not yet written to MMIO. Resolving the TODO is a future, separate task. Assisted-by: Claude:claude-4.7-opus Signed-off-by: Alessio Ferri Acked-by: Michael Büsch Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260528-b43_complete_n_phy_rev_8_radio_2057_rev_8_support-v4-7-464566194d47@gmail.com Signed-off-by: Johannes Berg --- .../net/wireless/broadcom/b43/tables_nphy.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/net/wireless/broadcom/b43/tables_nphy.c b/drivers/net/wireless/broadcom/b43/tables_nphy.c index 84e8d718d775..ecd660b9c75a 100644 --- a/drivers/net/wireless/broadcom/b43/tables_nphy.c +++ b/drivers/net/wireless/broadcom/b43/tables_nphy.c @@ -2923,6 +2923,21 @@ static const s16 b43_ntab_rf_pwr_offset_2057_rev9_5g[] = { 0, }; +/* Sourced from the rev 5 sibling: the rev 8 IPA TX gain table + * shares the low 24 bits of every entry with rev 5 (only the + * PAD-gain selector byte differs), so the same gain index maps to + * the same physical PAD gain code on both revisions. + */ +static const s16 b43_ntab_rf_pwr_offset_2057_rev8_2g[] = { + -109, -109, -82, -68, -58, + -50, -44, -39, -35, -31, + -28, -26, -23, -21, -19, + -17, -16, -14, -13, -11, + -10, -9, -8, -7, -5, + -5, -4, -3, -2, -1, + -1, 0, +}; + /* Extracted from MMIO dump of 6.30.223.248 * Entries: 0, 26, 28, 29, 30, 31 were guessed */ @@ -3782,6 +3797,10 @@ const s16 *b43_ntab_get_rf_pwr_offset_table(struct b43_wldev *dev) if (phy->radio_rev == 9) return b43_ntab_rf_pwr_offset_2057_rev9_2g; break; + case 8: + if (phy->radio_rev == 8) + return b43_ntab_rf_pwr_offset_2057_rev8_2g; + break; } b43err(dev->wl,