From 490340faddea461319652ce36dbc7c1b4482c35e Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 10 May 2025 15:21:25 +0300 Subject: [PATCH 01/16] wifi: rtw88: usb: Reduce control message timeout to 500 ms RTL8811AU stops responding during the firmware download on some systems: [ 809.256440] rtw_8821au 5-2.1:1.0: Firmware version 42.4.0, H2C version 0 [ 812.759142] rtw_8821au 5-2.1:1.0 wlp48s0f4u2u1: renamed from wlan0 [ 837.315388] rtw_8821au 1-4:1.0: write register 0x1ef4 failed with -110 [ 867.524259] rtw_8821au 1-4:1.0: write register 0x1ef8 failed with -110 [ 868.930976] rtw_8821au 5-2.1:1.0 wlp48s0f4u2u1: entered promiscuous mode [ 897.730952] rtw_8821au 1-4:1.0: write register 0x1efc failed with -110 Each write takes 30 seconds to fail because that's the timeout currently used for control messages in rtw_usb_write(). In this scenario the firmware download takes at least 2000 seconds. Because this is done from the USB probe function, the long delay makes other things in the system hang. Reduce the timeout to 500 ms. This is the value used by the official USB wifi drivers from Realtek. Of course this only makes things hang for ~30 seconds instead of ~30 minutes. It doesn't fix the firmware download. Tested with RTL8822CU, RTL8812BU, RTL8811CU, RTL8814AU, RTL8811AU, RTL8812AU, RTL8821AU, RTL8723DU. Cc: stable@vger.kernel.org Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Link: https://github.com/lwfinger/rtw88/issues/344 Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/1e35dd26-3f10-40b1-b2b4-f72184a26611@gmail.com --- drivers/net/wireless/realtek/rtw88/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 204343ac2558..b16db579fdce 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -139,7 +139,7 @@ static void rtw_usb_write(struct rtw_dev *rtwdev, u32 addr, u32 val, int len) ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE, - addr, 0, data, len, 30000); + addr, 0, data, len, 500); if (ret < 0 && ret != -ENODEV && count++ < 4) rtw_err(rtwdev, "write register 0x%x failed with %d\n", addr, ret); -- 2.51.0 From 80fe0bc1659c0ccc79d082e426fa376be5df9c04 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 10 May 2025 15:22:24 +0300 Subject: [PATCH 02/16] wifi: rtw88: usb: Upload the firmware in bigger chunks RTL8811AU stops responding during the firmware download on some systems: [ 809.256440] rtw_8821au 5-2.1:1.0: Firmware version 42.4.0, H2C version 0 [ 812.759142] rtw_8821au 5-2.1:1.0 wlp48s0f4u2u1: renamed from wlan0 [ 837.315388] rtw_8821au 1-4:1.0: write register 0x1ef4 failed with -110 [ 867.524259] rtw_8821au 1-4:1.0: write register 0x1ef8 failed with -110 [ 868.930976] rtw_8821au 5-2.1:1.0 wlp48s0f4u2u1: entered promiscuous mode [ 897.730952] rtw_8821au 1-4:1.0: write register 0x1efc failed with -110 Maybe it takes too long when writing the firmware 4 bytes at a time. Write 196 bytes at a time for RTL8821AU, RTL8811AU, and RTL8812AU, and 254 bytes at a time for RTL8723DU. These are the sizes used in their official drivers. Tested with all these chips. Cc: stable@vger.kernel.org Link: https://github.com/lwfinger/rtw88/issues/344 Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/43f1daad-3ec0-4a3b-a50c-9cd9eb2c2f52@gmail.com --- drivers/net/wireless/realtek/rtw88/hci.h | 8 ++++ drivers/net/wireless/realtek/rtw88/mac.c | 11 +++-- drivers/net/wireless/realtek/rtw88/mac.h | 2 + drivers/net/wireless/realtek/rtw88/pci.c | 2 + drivers/net/wireless/realtek/rtw88/sdio.c | 2 + drivers/net/wireless/realtek/rtw88/usb.c | 55 +++++++++++++++++++++++ 6 files changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/hci.h b/drivers/net/wireless/realtek/rtw88/hci.h index 96aeda26014e..d4bee9c3ecfe 100644 --- a/drivers/net/wireless/realtek/rtw88/hci.h +++ b/drivers/net/wireless/realtek/rtw88/hci.h @@ -19,6 +19,8 @@ struct rtw_hci_ops { void (*link_ps)(struct rtw_dev *rtwdev, bool enter); void (*interface_cfg)(struct rtw_dev *rtwdev); void (*dynamic_rx_agg)(struct rtw_dev *rtwdev, bool enable); + void (*write_firmware_page)(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size); int (*write_data_rsvd_page)(struct rtw_dev *rtwdev, u8 *buf, u32 size); int (*write_data_h2c)(struct rtw_dev *rtwdev, u8 *buf, u32 size); @@ -79,6 +81,12 @@ static inline void rtw_hci_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable) rtwdev->hci.ops->dynamic_rx_agg(rtwdev, enable); } +static inline void rtw_hci_write_firmware_page(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size) +{ + rtwdev->hci.ops->write_firmware_page(rtwdev, page, data, size); +} + static inline int rtw_hci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size) { diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index 0491f501c138..f66d1b302dc5 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -856,8 +856,8 @@ fwdl_ready: } } -static void -write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size) +void rtw_write_firmware_page(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size) { u32 val32; u32 block_nr; @@ -887,6 +887,7 @@ write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size) rtw_write32(rtwdev, write_addr, le32_to_cpu(remain_data)); } } +EXPORT_SYMBOL(rtw_write_firmware_page); static int download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size) @@ -904,11 +905,13 @@ download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size) rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT); for (page = 0; page < total_page; page++) { - write_firmware_page(rtwdev, page, data, DLFW_PAGE_SIZE_LEGACY); + rtw_hci_write_firmware_page(rtwdev, page, data, + DLFW_PAGE_SIZE_LEGACY); data += DLFW_PAGE_SIZE_LEGACY; } if (last_page_size) - write_firmware_page(rtwdev, page, data, last_page_size); + rtw_hci_write_firmware_page(rtwdev, page, data, + last_page_size); if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT, 1)) { rtw_err(rtwdev, "failed to check download firmware report\n"); diff --git a/drivers/net/wireless/realtek/rtw88/mac.h b/drivers/net/wireless/realtek/rtw88/mac.h index 6905e2747372..e92b1483728d 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.h +++ b/drivers/net/wireless/realtek/rtw88/mac.h @@ -34,6 +34,8 @@ int rtw_pwr_seq_parser(struct rtw_dev *rtwdev, const struct rtw_pwr_seq_cmd * const *cmd_seq); int rtw_mac_power_on(struct rtw_dev *rtwdev); void rtw_mac_power_off(struct rtw_dev *rtwdev); +void rtw_write_firmware_page(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size); int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw); int rtw_mac_init(struct rtw_dev *rtwdev); void rtw_mac_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop); diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index bb4c4ccb31d4..7f2b6dc21f56 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -12,6 +12,7 @@ #include "fw.h" #include "ps.h" #include "debug.h" +#include "mac.h" static bool rtw_disable_msi; static bool rtw_pci_disable_aspm; @@ -1602,6 +1603,7 @@ static const struct rtw_hci_ops rtw_pci_ops = { .link_ps = rtw_pci_link_ps, .interface_cfg = rtw_pci_interface_cfg, .dynamic_rx_agg = NULL, + .write_firmware_page = rtw_write_firmware_page, .read8 = rtw_pci_read8, .read16 = rtw_pci_read16, diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c index 71cbe49b6c59..e733ed846123 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -10,6 +10,7 @@ #include #include #include "main.h" +#include "mac.h" #include "debug.h" #include "fw.h" #include "ps.h" @@ -1164,6 +1165,7 @@ static const struct rtw_hci_ops rtw_sdio_ops = { .link_ps = rtw_sdio_link_ps, .interface_cfg = rtw_sdio_interface_cfg, .dynamic_rx_agg = NULL, + .write_firmware_page = rtw_write_firmware_page, .read8 = rtw_sdio_read8, .read16 = rtw_sdio_read16, diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index b16db579fdce..3b5126ffc81a 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -165,6 +165,60 @@ static void rtw_usb_write32(struct rtw_dev *rtwdev, u32 addr, u32 val) rtw_usb_write(rtwdev, addr, val, 4); } +static void rtw_usb_write_firmware_page(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_device *udev = rtwusb->udev; + u32 addr = FW_START_ADDR_LEGACY; + u8 *data_dup, *buf; + u32 n, block_size; + int ret; + + switch (rtwdev->chip->id) { + case RTW_CHIP_TYPE_8723D: + block_size = 254; + break; + default: + block_size = 196; + break; + } + + data_dup = kmemdup(data, size, GFP_KERNEL); + if (!data_dup) + return; + + buf = data_dup; + + rtw_write32_mask(rtwdev, REG_MCUFW_CTRL, BIT_ROM_PGE, page); + + while (size > 0) { + if (size >= block_size) + n = block_size; + else if (size >= 8) + n = 8; + else + n = 1; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE, + addr, 0, buf, n, 500); + if (ret != n) { + if (ret != -ENODEV) + rtw_err(rtwdev, + "write 0x%x len %d failed: %d\n", + addr, n, ret); + break; + } + + addr += n; + buf += n; + size -= n; + } + + kfree(data_dup); +} + static int dma_mapping_to_ep(enum rtw_dma_mapping dma_mapping) { switch (dma_mapping) { @@ -892,6 +946,7 @@ static const struct rtw_hci_ops rtw_usb_ops = { .link_ps = rtw_usb_link_ps, .interface_cfg = rtw_usb_interface_cfg, .dynamic_rx_agg = rtw_usb_dynamic_rx_agg, + .write_firmware_page = rtw_usb_write_firmware_page, .write8 = rtw_usb_write8, .write16 = rtw_usb_write16, -- 2.51.0 From f24d0d8c3cd7e4237f802c4d2f3bd4ac04572948 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Sat, 10 May 2025 16:12:34 +0300 Subject: [PATCH 03/16] wifi: rtw88: Fix the random "error beacon valid" messages for USB All the USB devices have a problem in AP mode: uploading the updated beacon to the chip's reserved page can randomly fail: [34996.474304] rtw88_8723du 1-2:1.2: error beacon valid [34996.474788] rtw88_8723du 1-2:1.2: failed to download drv rsvd page [34999.956369] rtw88_8723du 1-2:1.2: error beacon valid [34999.956846] rtw88_8723du 1-2:1.2: failed to download drv rsvd page [34999.956855] rtw88_8723du 1-2:1.2: failed to download beacon [35017.978296] rtw88_8723du 1-2:1.2: error beacon valid [35017.978805] rtw88_8723du 1-2:1.2: failed to download drv rsvd page [35017.978823] rtw88_8723du 1-2:1.2: failed to download beacon [35023.200395] rtw88_8723du 1-2:1.2: error beacon valid [35023.200869] rtw88_8723du 1-2:1.2: failed to download drv rsvd page [35023.200875] rtw88_8723du 1-2:1.2: failed to download beacon [35478.680547] rtw88_8723du 1-2:1.2: error beacon valid [35478.681023] rtw88_8723du 1-2:1.2: failed to download drv rsvd page Disable some beacon-related hardware functions before uploading the beacon and enable them again after. Tested with RTL8723DU, RTL8812BU, RTL8822CE. Signed-off-by: Bitterblue Smith Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/c248c40a-d432-47ed-90e0-d81ee6c32464@gmail.com --- drivers/net/wireless/realtek/rtw88/fw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 6b563ac489a7..4fc78b882080 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -1466,7 +1466,7 @@ void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev, int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, u8 *buf, u32 size) { - u8 bckp[2]; + u8 bckp[3]; u8 val; u16 rsvd_pg_head; u32 bcn_valid_addr; @@ -1478,6 +1478,8 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, if (!size) return -EINVAL; + bckp[2] = rtw_read8(rtwdev, REG_BCN_CTRL); + if (rtw_chip_wcpu_11n(rtwdev)) { rtw_write32_set(rtwdev, REG_DWBCN0_CTRL, BIT_BCN_VALID); } else { @@ -1491,6 +1493,9 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, val |= BIT_ENSWBCN >> 8; rtw_write8(rtwdev, REG_CR + 1, val); + rtw_write8(rtwdev, REG_BCN_CTRL, + (bckp[2] & ~BIT_EN_BCN_FUNCTION) | BIT_DIS_TSF_UDT); + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) { val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2); bckp[1] = val; @@ -1521,6 +1526,7 @@ restore: rsvd_pg_head = rtwdev->fifo.rsvd_boundary; rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, rsvd_pg_head | BIT_BCN_VALID_V1); + rtw_write8(rtwdev, REG_BCN_CTRL, bckp[2]); if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]); rtw_write8(rtwdev, REG_CR + 1, bckp[0]); -- 2.51.0 From 46b607974866f59d707d19fc6d954bb3d2c15512 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 11 May 2025 11:52:12 +0800 Subject: [PATCH 04/16] wifi: rtw89: mcc: pass whom to stop at when pausing chanctx When stopping MCC, FW can stop at a given MCC role following H2C command. When pausing chanctx during MCC, in general, the caller expects to process things with its chanctx. So, pass the caller as target and let FW stop MCC at it. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250511035217.10410-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 35 +++++++++++++++++------ drivers/net/wireless/realtek/rtw89/chan.h | 7 ++++- drivers/net/wireless/realtek/rtw89/core.c | 6 +++- drivers/net/wireless/realtek/rtw89/fw.c | 6 +++- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 871befa63889..ff476bde39ab 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -2107,6 +2107,12 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev) } struct rtw89_mcc_stop_sel { + struct { + const struct rtw89_vif_link *target; + } hint; + + /* selection content */ + bool filled; u8 mac_id; u8 slot_idx; }; @@ -2116,6 +2122,7 @@ static void rtw89_mcc_stop_sel_fill(struct rtw89_mcc_stop_sel *sel, { sel->mac_id = mcc_role->rtwvif_link->mac_id; sel->slot_idx = mcc_role->slot_idx; + sel->filled = true; } static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev, @@ -2125,23 +2132,35 @@ static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev, { struct rtw89_mcc_stop_sel *sel = data; + if (mcc_role->rtwvif_link == sel->hint.target) { + rtw89_mcc_stop_sel_fill(sel, mcc_role); + return 1; /* break iteration */ + } + + if (sel->filled) + return 0; + if (!mcc_role->rtwvif_link->chanctx_assigned) return 0; rtw89_mcc_stop_sel_fill(sel, mcc_role); - return 1; /* break iteration */ + return 0; } -static void rtw89_mcc_stop(struct rtw89_dev *rtwdev) +static void rtw89_mcc_stop(struct rtw89_dev *rtwdev, + const struct rtw89_chanctx_pause_parm *pause) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_role *ref = &mcc->role_ref; - struct rtw89_mcc_stop_sel sel; + struct rtw89_mcc_stop_sel sel = { + .hint.target = pause ? pause->trigger : NULL, + }; int ret; /* by default, stop at ref */ - rtw89_mcc_stop_sel_fill(&sel, ref); rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_stop_sel_iterator, &sel); + if (!sel.filled) + rtw89_mcc_stop_sel_fill(&sel, ref); rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC stop at \n", sel.mac_id); @@ -2492,7 +2511,7 @@ void rtw89_chanctx_track(struct rtw89_dev *rtwdev) } void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, - enum rtw89_chanctx_pause_reasons rsn) + const struct rtw89_chanctx_pause_parm *pause_parm) { struct rtw89_hal *hal = &rtwdev->hal; enum rtw89_entity_mode mode; @@ -2502,12 +2521,12 @@ void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, if (hal->entity_pause) return; - rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", rsn); + rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", pause_parm->rsn); mode = rtw89_get_entity_mode(rtwdev); switch (mode) { case RTW89_ENTITY_MODE_MCC: - rtw89_mcc_stop(rtwdev); + rtw89_mcc_stop(rtwdev, pause_parm); break; default: break; @@ -2732,7 +2751,7 @@ out: cur = rtw89_get_entity_mode(rtwdev); switch (cur) { case RTW89_ENTITY_MODE_MCC: - rtw89_mcc_stop(rtwdev); + rtw89_mcc_stop(rtwdev, NULL); break; default: break; diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h index b42e044d7927..2a25563593af 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.h +++ b/drivers/net/wireless/realtek/rtw89/chan.h @@ -46,6 +46,11 @@ enum rtw89_chanctx_pause_reasons { RTW89_CHANCTX_PAUSE_REASON_ROC, }; +struct rtw89_chanctx_pause_parm { + const struct rtw89_vif_link *trigger; + enum rtw89_chanctx_pause_reasons rsn; +}; + struct rtw89_chanctx_cb_parm { int (*cb)(struct rtw89_dev *rtwdev, void *data); void *data; @@ -113,7 +118,7 @@ void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, enum rtw89_chanctx_changes change); void rtw89_chanctx_track(struct rtw89_dev *rtwdev); void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, - enum rtw89_chanctx_pause_reasons rsn); + const struct rtw89_chanctx_pause_parm *parm); void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev, const struct rtw89_chanctx_cb_parm *cb_parm); diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 0b6fb30cbf52..3b2a2c6b9a44 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3389,6 +3389,9 @@ out: void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + struct rtw89_chanctx_pause_parm pause_parm = { + .rsn = RTW89_CHANCTX_PAUSE_REASON_ROC, + }; struct ieee80211_hw *hw = rtwdev->hw; struct rtw89_roc *roc = &rtwvif->roc; struct rtw89_vif_link *rtwvif_link; @@ -3410,7 +3413,8 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) roc->link_id = rtwvif_link->link_id; - rtw89_chanctx_pause(rtwdev, RTW89_CHANCTX_PAUSE_REASON_ROC); + pause_parm.trigger = rtwvif_link; + rtw89_chanctx_pause(rtwdev, &pause_parm); ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, true); if (ret) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 76350351dfb2..1abf69818fc6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -7446,6 +7446,10 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; + struct rtw89_chanctx_pause_parm pause_parm = { + .rsn = RTW89_CHANCTX_PAUSE_REASON_HW_SCAN, + .trigger = rtwvif_link, + }; u32 rx_fltr = rtwdev->hal.rx_fltr; u8 mac_addr[ETH_ALEN]; u32 reg; @@ -7484,7 +7488,7 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rx_fltr); - rtw89_chanctx_pause(rtwdev, RTW89_CHANCTX_PAUSE_REASON_HW_SCAN); + rtw89_chanctx_pause(rtwdev, &pause_parm); if (mode == RTW89_ENTITY_MODE_MCC) rtw89_hw_scan_update_beacon_noa(rtwdev, req); -- 2.51.0 From 8ee99b998fc4a0c1aece047bb2f933903cf35f23 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 11 May 2025 11:52:13 +0800 Subject: [PATCH 05/16] wifi: rtw89: mcc: drop queued chanctx changes when stopping When MCC is about to stop, there may be some chanctx changes which are queued for work but have not yet been run. To avoid these changes from being processed in a wrong state (e.g. next new MCC instance), cancel the queued work and drop queued changes. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250511035217.10410-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index ff476bde39ab..e733564abc7a 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -2150,6 +2150,7 @@ static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev, static void rtw89_mcc_stop(struct rtw89_dev *rtwdev, const struct rtw89_chanctx_pause_parm *pause) { + struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_role *ref = &mcc->role_ref; struct rtw89_mcc_stop_sel sel = { @@ -2157,6 +2158,11 @@ static void rtw89_mcc_stop(struct rtw89_dev *rtwdev, }; int ret; + if (!pause) { + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->chanctx_work); + bitmap_zero(hal->changes, NUM_OF_RTW89_CHANCTX_CHANGES); + } + /* by default, stop at ref */ rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_stop_sel_iterator, &sel); if (!sel.filled) -- 2.51.0 From 7662708c00af2edd73d68aa17f5686163818f646 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 11 May 2025 11:52:14 +0800 Subject: [PATCH 06/16] wifi: rtw89: mcc: add courtesy mechanism conditions to P2P roles In one enablement of courtesy mechanism, there is one provider and one receiver. And, receiver can use the provider's time in a given period. But, to make P2P NoA protocol work as expected as possible, GO should be present at the time it doesn't announce absent, and GC should not use the time when GO announces absent. So, don't enable courtesy mechanism if provider is GO or receiver is GC. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250511035217.10410-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index e733564abc7a..e09541ceb504 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -961,6 +961,15 @@ static int rtw89_mcc_fill_all_roles(struct rtw89_dev *rtwdev) return 0; } +static bool rtw89_mcc_can_courtesy(const struct rtw89_mcc_role *provider, + const struct rtw89_mcc_role *receiver) +{ + if (provider->is_go || receiver->is_gc) + return false; + + return true; +} + static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev, const struct rtw89_mcc_pattern *new) { @@ -980,7 +989,7 @@ static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev, *pattern = *new; memset(&pattern->courtesy, 0, sizeof(pattern->courtesy)); - if (RTW89_MCC_REQ_COURTESY(pattern, aux)) { + if (RTW89_MCC_REQ_COURTESY(pattern, aux) && rtw89_mcc_can_courtesy(ref, aux)) { crtz = &pattern->courtesy.ref; ref->crtz = crtz; @@ -994,7 +1003,7 @@ static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev, ref->crtz = NULL; } - if (RTW89_MCC_REQ_COURTESY(pattern, ref)) { + if (RTW89_MCC_REQ_COURTESY(pattern, ref) && rtw89_mcc_can_courtesy(aux, ref)) { crtz = &pattern->courtesy.aux; aux->crtz = crtz; -- 2.51.0 From eec9dfad1b217fadb82b6e87827a234620ed6868 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 11 May 2025 11:52:15 +0800 Subject: [PATCH 07/16] wifi: rtw89: mcc: introduce calculation of anchor pattern In the cases that two MCC roles' TBTTs are too close or too far, original MCC pattern calculation logic will lead to a result that both roles might not cover its TBTT with sufficient time. Introduce a new calculation logic called anchor pattern for these corner cases. It allows to choose one role as anchor to put its TBTT in the middle of its duration directly. For now, a P2P role has a higher priority to be chosen as an anchor. Then, if able, another role might need to depend on courtesy mechanism to take time from anchor. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250511035217.10410-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 105 ++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index e09541ceb504..67acdcc1f535 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -1185,6 +1185,107 @@ static int __rtw89_mcc_calc_pattern_strict(struct rtw89_dev *rtwdev, return 0; } +static void __rtw89_mcc_fill_ptrn_anchor_ref(struct rtw89_dev *rtwdev, + struct rtw89_mcc_pattern *ptrn, + bool small_bcn_ofst) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_mcc_config *config = &mcc->config; + u16 bcn_ofst = config->beacon_offset; + u16 ref_tob; + u16 ref_toa; + + if (ref->limit.enable) { + ref_tob = ref->limit.max_tob; + ref_toa = ref->limit.max_toa; + } else { + ref_tob = ref->duration / 2; + ref_toa = ref->duration / 2; + } + + if (small_bcn_ofst) { + ptrn->toa_ref = ref_toa; + ptrn->tob_ref = ref->duration - ptrn->toa_ref; + } else { + ptrn->tob_ref = ref_tob; + ptrn->toa_ref = ref->duration - ptrn->tob_ref; + } + + ptrn->tob_aux = bcn_ofst - ptrn->toa_ref; + ptrn->toa_aux = aux->duration - ptrn->tob_aux; +} + +static void __rtw89_mcc_fill_ptrn_anchor_aux(struct rtw89_dev *rtwdev, + struct rtw89_mcc_pattern *ptrn, + bool small_bcn_ofst) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_mcc_config *config = &mcc->config; + u16 bcn_ofst = config->beacon_offset; + u16 aux_tob; + u16 aux_toa; + + if (aux->limit.enable) { + aux_tob = aux->limit.max_tob; + aux_toa = aux->limit.max_toa; + } else { + aux_tob = aux->duration / 2; + aux_toa = aux->duration / 2; + } + + if (small_bcn_ofst) { + ptrn->tob_aux = aux_tob; + ptrn->toa_aux = aux->duration - ptrn->tob_aux; + } else { + ptrn->toa_aux = aux_toa; + ptrn->tob_aux = aux->duration - ptrn->toa_aux; + } + + ptrn->toa_ref = bcn_ofst - ptrn->tob_aux; + ptrn->tob_ref = ref->duration - ptrn->toa_ref; +} + +static int __rtw89_mcc_calc_pattern_anchor(struct rtw89_dev *rtwdev, + struct rtw89_mcc_pattern *ptrn, + bool hdl_bt) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_mcc_config *config = &mcc->config; + u16 mcc_intvl = config->mcc_interval; + u16 bcn_ofst = config->beacon_offset; + bool small_bcn_ofst; + + if (bcn_ofst < RTW89_MCC_MIN_RX_BCN_TIME) + small_bcn_ofst = true; + else if (mcc_intvl - bcn_ofst < RTW89_MCC_MIN_RX_BCN_TIME) + small_bcn_ofst = false; + else + return -EPERM; + + *ptrn = (typeof(*ptrn)){ + .plan = hdl_bt ? RTW89_MCC_PLAN_TAIL_BT : RTW89_MCC_PLAN_NO_BT, + }; + + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MCC calc ptrn_ac: plan %d, bcn_ofst %d\n", + ptrn->plan, bcn_ofst); + + if (ref->is_go || ref->is_gc) + __rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst); + else if (aux->is_go || aux->is_gc) + __rtw89_mcc_fill_ptrn_anchor_aux(rtwdev, ptrn, small_bcn_ofst); + else + __rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst); + + return 0; +} + static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; @@ -1238,6 +1339,10 @@ static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt) goto done; } + ret = __rtw89_mcc_calc_pattern_anchor(rtwdev, &ptrn, hdl_bt); + if (!ret) + goto done; + __rtw89_mcc_calc_pattern_loose(rtwdev, &ptrn, hdl_bt); done: -- 2.51.0 From 122b74ac9b9d19cfbc5f319ca5c0da0efc8be9fb Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 11 May 2025 11:52:16 +0800 Subject: [PATCH 08/16] wifi: rtw89: mcc: deal with non-periodic NoA Originally, MCC just took periodic NoA into account. When the connected GO announces non-periodic NoA and GC side is during MCC, sometimes GC cannot receive beacons well if the MCC scheduling conflicts with the non-periodic NoA planning. After the loss exceeds the tolerable amount, beacon filter will report connection loss. However, in this case, the loss is acceptable. So now, MCC will calculate the range of non-periodic NoA. And then, don't care beacon loss during the range. Besides, rtw89_mcc_fill_role_limit() only makes sense for GC. Remove the redundant check of GO. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250511035217.10410-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 4 +- drivers/net/wireless/realtek/rtw89/core.c | 3 + drivers/net/wireless/realtek/rtw89/core.h | 9 ++ drivers/net/wireless/realtek/rtw89/mac.c | 3 +- drivers/net/wireless/realtek/rtw89/mac80211.c | 4 + drivers/net/wireless/realtek/rtw89/ps.c | 147 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/ps.h | 3 + drivers/net/wireless/realtek/rtw89/ser.c | 2 + 8 files changed, 173 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 67acdcc1f535..b7593c7465b9 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -776,9 +776,11 @@ static void rtw89_mcc_fill_role_limit(struct rtw89_dev *rtwdev, int ret; int i; - if (!mcc_role->is_go && !mcc_role->is_gc) + if (!mcc_role->is_gc) return; + rtw89_p2p_noa_once_recalc(rtwvif_link); + rcu_read_lock(); bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 3b2a2c6b9a44..49447668cbf3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4004,6 +4004,9 @@ int rtw89_core_sta_link_disassoc(struct rtw89_dev *rtwdev, if (vif->type == NL80211_IFTYPE_STATION) rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, false); + if (rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT) + rtw89_p2p_noa_once_deinit(rtwvif_link); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index c0f2b62bc43b..1c8f3b9b7c4c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3485,6 +3485,14 @@ struct rtw89_p2p_noa_setter { u8 noa_index; }; +struct rtw89_ps_noa_once_handler { + bool in_duration; + u64 tsf_begin; + u64 tsf_end; + struct wiphy_delayed_work set_work; + struct wiphy_delayed_work clr_work; +}; + struct rtw89_vif_link { struct rtw89_vif *rtwvif; struct list_head dlink_schd; @@ -3531,6 +3539,7 @@ struct rtw89_vif_link { struct rtw89_phy_rate_pattern rate_pattern; struct list_head general_pkt_list; struct rtw89_p2p_noa_setter p2p_noa; + struct rtw89_ps_noa_once_handler noa_once; }; enum rtw89_lv1_rcvy_step { diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index e3976ba6dda2..9f0e30e75009 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5025,7 +5025,8 @@ rtw89_mac_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l switch (type) { case RTW89_BCN_FLTR_BEACON_LOSS: - if (!rtwdev->scanning && !rtwvif->offchan) + if (!rtwdev->scanning && !rtwvif->offchan && + !rtwvif_link->noa_once.in_duration) ieee80211_connection_loss(vif); else rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 22d13a0d5b8a..a47971003bd4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -114,6 +114,8 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev, wiphy_work_init(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work); INIT_LIST_HEAD(&rtwvif_link->general_pkt_list); + rtw89_p2p_noa_once_init(rtwvif_link); + rtwvif_link->hit_rule = 0; rtwvif_link->bcn_hit_cond = 0; rtwvif_link->chanctx_assigned = false; @@ -143,6 +145,8 @@ static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev, wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work); + rtw89_p2p_noa_once_deinit(rtwvif_link); + rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_role_info(rtwdev, rtwvif_link, NULL, BTC_ROLE_STOP); diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index ac46a7baa00d..8e4fe73e7d77 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -382,3 +382,150 @@ u8 rtw89_p2p_noa_fetch(struct rtw89_vif_link *rtwvif_link, void **data) tail = ie->noa_desc + setter->noa_count; return tail - *data; } + +static void rtw89_ps_noa_once_set_work(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct rtw89_ps_noa_once_handler *noa_once = + container_of(work, struct rtw89_ps_noa_once_handler, set_work.work); + + lockdep_assert_wiphy(wiphy); + + noa_once->in_duration = true; +} + +static void rtw89_ps_noa_once_clr_work(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct rtw89_ps_noa_once_handler *noa_once = + container_of(work, struct rtw89_ps_noa_once_handler, clr_work.work); + struct rtw89_vif_link *rtwvif_link = + container_of(noa_once, struct rtw89_vif_link, noa_once); + struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; + + lockdep_assert_wiphy(wiphy); + + rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); + noa_once->in_duration = false; +} + +void rtw89_p2p_noa_once_init(struct rtw89_vif_link *rtwvif_link) +{ + struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; + + noa_once->in_duration = false; + noa_once->tsf_begin = 0; + noa_once->tsf_end = 0; + + wiphy_delayed_work_init(&noa_once->set_work, rtw89_ps_noa_once_set_work); + wiphy_delayed_work_init(&noa_once->clr_work, rtw89_ps_noa_once_clr_work); +} + +static void rtw89_p2p_noa_once_cancel(struct rtw89_vif_link *rtwvif_link) +{ + struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; + struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; + struct wiphy *wiphy = rtwdev->hw->wiphy; + + wiphy_delayed_work_cancel(wiphy, &noa_once->set_work); + wiphy_delayed_work_cancel(wiphy, &noa_once->clr_work); +} + +void rtw89_p2p_noa_once_deinit(struct rtw89_vif_link *rtwvif_link) +{ + rtw89_p2p_noa_once_cancel(rtwvif_link); + rtw89_p2p_noa_once_init(rtwvif_link); +} + +void rtw89_p2p_noa_once_recalc(struct rtw89_vif_link *rtwvif_link) +{ + struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; + struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; + const struct ieee80211_p2p_noa_desc *noa_desc; + struct wiphy *wiphy = rtwdev->hw->wiphy; + struct ieee80211_bss_conf *bss_conf; + u64 tsf_begin = U64_MAX, tsf_end; + u64 set_delay_us = 0; + u64 clr_delay_us = 0; + u32 start_time; + u32 interval; + u32 duration; + u64 tsf; + int ret; + int i; + + lockdep_assert_wiphy(wiphy); + + ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf); + if (ret) { + rtw89_warn(rtwdev, "%s: failed to get tsf\n", __func__); + return; + } + + rcu_read_lock(); + + bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); + + for (i = 0; i < ARRAY_SIZE(bss_conf->p2p_noa_attr.desc); i++) { + bool first = tsf_begin == U64_MAX; + u64 tmp; + + noa_desc = &bss_conf->p2p_noa_attr.desc[i]; + if (noa_desc->count == 0 || noa_desc->count == 255) + continue; + + start_time = le32_to_cpu(noa_desc->start_time); + interval = le32_to_cpu(noa_desc->interval); + duration = le32_to_cpu(noa_desc->duration); + + if (unlikely(duration == 0 || + (noa_desc->count > 1 && interval == 0))) + continue; + + tmp = start_time + interval * (noa_desc->count - 1) + duration; + tmp = (tsf & GENMASK_ULL(63, 32)) + tmp; + if (unlikely(tmp <= tsf)) + continue; + tsf_end = first ? tmp : max(tsf_end, tmp); + + tmp = (tsf & GENMASK_ULL(63, 32)) | start_time; + tsf_begin = first ? tmp : min(tsf_begin, tmp); + } + + rcu_read_unlock(); + + if (tsf_begin == U64_MAX) + return; + + rtw89_p2p_noa_once_cancel(rtwvif_link); + + if (noa_once->tsf_end > tsf) { + tsf_begin = min(tsf_begin, noa_once->tsf_begin); + tsf_end = max(tsf_end, noa_once->tsf_end); + } + + clr_delay_us = min_t(u64, tsf_end - tsf, UINT_MAX); + + if (tsf_begin <= tsf) { + noa_once->in_duration = true; + goto out; + } + + set_delay_us = tsf_begin - tsf; + if (unlikely(set_delay_us > UINT_MAX)) { + rtw89_warn(rtwdev, "%s: unhandled begin\n", __func__); + set_delay_us = 0; + clr_delay_us = 0; + rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); + noa_once->in_duration = false; + } + +out: + if (set_delay_us) + wiphy_delayed_work_queue(wiphy, &noa_once->set_work, + usecs_to_jiffies(set_delay_us)); + if (clr_delay_us) + wiphy_delayed_work_queue(wiphy, &noa_once->clr_work, + usecs_to_jiffies(clr_delay_us)); + + noa_once->tsf_begin = tsf_begin; + noa_once->tsf_end = tsf_end; +} diff --git a/drivers/net/wireless/realtek/rtw89/ps.h b/drivers/net/wireless/realtek/rtw89/ps.h index 2b88f254a32d..b2c43d44820d 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.h +++ b/drivers/net/wireless/realtek/rtw89/ps.h @@ -22,6 +22,9 @@ void rtw89_p2p_noa_renew(struct rtw89_vif_link *rtwvif_link); void rtw89_p2p_noa_append(struct rtw89_vif_link *rtwvif_link, const struct ieee80211_p2p_noa_desc *desc); u8 rtw89_p2p_noa_fetch(struct rtw89_vif_link *rtwvif_link, void **data); +void rtw89_p2p_noa_once_init(struct rtw89_vif_link *rtwvif_link); +void rtw89_p2p_noa_once_deinit(struct rtw89_vif_link *rtwvif_link); +void rtw89_p2p_noa_once_recalc(struct rtw89_vif_link *rtwvif_link); static inline void rtw89_leave_ips_by_hwflags(struct rtw89_dev *rtwdev) { diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index 6ab25d71b050..811c91481441 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -310,6 +310,8 @@ static void ser_reset_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtwvif_link->net_type = RTW89_NET_TYPE_NO_LINK; rtwvif_link->trigger = false; rtwvif_link->rand_tsf_done = false; + + rtw89_p2p_noa_once_deinit(rtwvif_link); } } -- 2.51.0 From b178c1a23c5f53bdaad4f915e01d674169206c28 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Sun, 11 May 2025 11:52:17 +0800 Subject: [PATCH 09/16] wifi: rtw89: mcc: avoid redundant recalculations if no chance to improve MCC will track the changes of beacon offset, and trigger a recalculation when the difference is larger than the tolerance. It means that a better pattern is expected after recalculating. However, in the cases which get a worse beacon offset, there is no chance to improve the pattern even if recalculating. So, bypass them. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250511035217.10410-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index b7593c7465b9..806f42429a29 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -2367,7 +2367,7 @@ static void rtw89_mcc_track(struct rtw89_dev *rtwdev) struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_config *config = &mcc->config; struct rtw89_mcc_pattern *pattern = &config->pattern; - s16 tolerance; + u16 tolerance; u16 bcn_ofst; u16 diff; @@ -2375,18 +2375,25 @@ static void rtw89_mcc_track(struct rtw89_dev *rtwdev) return; bcn_ofst = rtw89_mcc_get_bcn_ofst(rtwdev); + if (bcn_ofst == config->beacon_offset) + return; + if (bcn_ofst > config->beacon_offset) { diff = bcn_ofst - config->beacon_offset; if (pattern->tob_aux < 0) tolerance = -pattern->tob_aux; - else + else if (pattern->toa_aux > 0) tolerance = pattern->toa_aux; + else + return; /* no chance to improve */ } else { diff = config->beacon_offset - bcn_ofst; if (pattern->toa_aux < 0) tolerance = -pattern->toa_aux; - else + else if (pattern->tob_aux > 0) tolerance = pattern->tob_aux; + else + return; /* no chance to improve */ } if (diff <= tolerance) -- 2.51.0 From 4c2c372de2e108319236203cce6de44d70ae15cd Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Tue, 13 May 2025 12:13:04 +0000 Subject: [PATCH 10/16] wifi: rtw88: fix the 'para' buffer size to avoid reading out of bounds Set the size to 6 instead of 2, since 'para' array is passed to 'rtw_fw_bt_wifi_control(rtwdev, para[0], ¶[1])', which reads 5 bytes: void rtw_fw_bt_wifi_control(struct rtw_dev *rtwdev, u8 op_code, u8 *data) { ... SET_BT_WIFI_CONTROL_DATA1(h2c_pkt, *data); SET_BT_WIFI_CONTROL_DATA2(h2c_pkt, *(data + 1)); ... SET_BT_WIFI_CONTROL_DATA5(h2c_pkt, *(data + 4)); Detected using the static analysis tool - Svace. Fixes: 4136214f7c46 ("rtw88: add BT co-existence support") Signed-off-by: Alexey Kodanev Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250513121304.124141-1-aleksei.kodanev@bell-sw.com --- drivers/net/wireless/realtek/rtw88/coex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c index c929db1e53ca..64904278ddad 100644 --- a/drivers/net/wireless/realtek/rtw88/coex.c +++ b/drivers/net/wireless/realtek/rtw88/coex.c @@ -309,7 +309,7 @@ static void rtw_coex_tdma_timer_base(struct rtw_dev *rtwdev, u8 type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; - u8 para[2] = {0}; + u8 para[6] = {}; u8 times; u16 tbtt_interval = coex_stat->wl_beacon_interval; -- 2.51.0 From 3cc35394fac15d533639c9c9e42f28d28936a4a0 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Tue, 13 May 2025 20:52:03 +0800 Subject: [PATCH 11/16] wifi: rtw89: fix firmware scan delay unit for WiFi 6 chips The scan delay unit of firmware command for WiFi 6 chips is microsecond, but is wrong set now and lead to abnormal work for net-detect. Correct the unit to avoid the error. Fixes: e99dd80c8a18 ("wifi: rtw89: wow: add delay option for net-detect") Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250513125203.6858-1-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 1abf69818fc6..00b65b2995cf 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -5537,7 +5537,7 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, return 0; } -#define RTW89_SCAN_DELAY_TSF_UNIT 104800 +#define RTW89_SCAN_DELAY_TSF_UNIT 1000000 int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev, struct rtw89_scan_option *option, struct rtw89_vif_link *rtwvif_link, -- 2.51.0 From 68b44b05f4c880c42109a91d2e0e7faa94f40529 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Thu, 15 May 2025 23:31:21 +0530 Subject: [PATCH 12/16] wifi: mac80211: handle non-MLO mode as well in ieee80211_num_beaconing_links() Currently, ieee80211_num_beaconing_links() returns 0 when the interface operates in non-ML mode. However, non-MLO mode is equivalent to having a single link. Therefore, the function can handle the non-MLO case as well. This adjustment will also eliminate the need for deflink usage in certain scenarios. Hence, implement changes to handle the non-MLO case as well. There is no change in functionality, and no existing user-visible bug is getting fixed. This update simply makes the function generic to handle all cases. Suggested-by: Johannes Berg Link: https://lore.kernel.org/linux-wireless/16499ad8e4b060ee04c8a8b3615fe8952aa7b07b.camel@sipsolutions.net/ Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250515-fix_num_beaconing_links-v1-1-4a39e2704314@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 2cd8731d8275..05b7296b152c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1290,9 +1290,9 @@ static u8 ieee80211_num_beaconing_links(struct ieee80211_sub_if_data *sdata) sdata->vif.type != NL80211_IFTYPE_P2P_GO) return num; - if (!sdata->vif.valid_links) - return num; - + /* non-MLO mode of operation also uses link_id 0 in sdata so it is + * safe to directly proceed with the below loop + */ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { link = sdata_dereference(sdata->link[link_id], sdata); if (!link) -- 2.51.0 From d7500fbfb12067ee7313f13f4c58f771be3018ab Mon Sep 17 00:00:00 2001 From: Bert Karwatzki Date: Wed, 21 May 2025 00:34:29 +0200 Subject: [PATCH 13/16] wifi: check if socket flags are valid Checking the SOCK_WIFI_STATUS flag bit in sk_flags may give wrong results since sk_flags are part of a union and the union is used otherwise. Add sk_requests_wifi_status() which checks if sk is non-NULL, sk is a full socket (so flags are valid) and checks the flag bit. Fixes: 76a853f86c97 ("wifi: free SKBTX_WIFI_STATUS skb tx_flags flag") Suggested-by: Johannes Berg Signed-off-by: Bert Karwatzki Reviewed-by: Jason Xing Link: https://patch.msgid.link/20250520223430.6875-1-spasswolf@web.de [edit commit message, fix indentation] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/txrx.h | 3 +-- drivers/net/wireless/marvell/mwifiex/main.c | 3 +-- include/net/sock.h | 6 ++++++ net/mac80211/mesh.c | 2 +- net/mac80211/tx.c | 6 +++--- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 33ccd0b248d4..fff8b7f8abc5 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -617,8 +617,7 @@ static inline bool wil_need_txstat(struct sk_buff *skb) { const u8 *da = wil_skb_get_da(skb); - return is_unicast_ether_addr(da) && skb->sk && - sock_flag(skb->sk, SOCK_WIFI_STATUS); + return is_unicast_ether_addr(da) && sk_requests_wifi_status(skb->sk); } static inline void wil_consume_skb(struct sk_buff *skb, bool acked) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 1485f949ad4e..7b50a88a18e5 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -913,8 +913,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) multicast = is_multicast_ether_addr(skb->data); - if (unlikely(!multicast && skb->sk && - sock_flag(skb->sk, SOCK_WIFI_STATUS) && + if (unlikely(!multicast && sk_requests_wifi_status(skb->sk) && priv->adapter->fw_api_ver == MWIFIEX_FW_V15)) skb = mwifiex_clone_skb_for_tx_status(priv, skb, diff --git a/include/net/sock.h b/include/net/sock.h index f0fabb9fd28a..75c12e14fc47 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2821,6 +2821,12 @@ sk_is_refcounted(struct sock *sk) return !sk_fullsock(sk) || !sock_flag(sk, SOCK_RCU_FREE); } +static inline bool +sk_requests_wifi_status(struct sock *sk) +{ + return sk && sk_fullsock(sk) && sock_flag(sk, SOCK_WIFI_STATUS); +} + /* Checks if this SKB belongs to an HW offloaded socket * and whether any SW fallbacks are required based on dev. * Check decrypted mark in case skb_orphan() cleared socket. diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a381b4b756ea..5cc56d578048 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -777,7 +777,7 @@ bool ieee80211_mesh_xmit_fast(struct ieee80211_sub_if_data *sdata, if (ethertype < ETH_P_802_3_MIN) return false; - if (skb->sk && sock_flag(skb->sk, SOCK_WIFI_STATUS)) + if (sk_requests_wifi_status(skb->sk)) return false; if (skb->ip_summed == CHECKSUM_PARTIAL) { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3b9392a6ddb2..d8d4f3d7d7f2 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2859,7 +2859,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, } if (unlikely(!multicast && - ((skb->sk && sock_flag(skb->sk, SOCK_WIFI_STATUS)) || + (sk_requests_wifi_status(skb->sk) || ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS))) info_id = ieee80211_store_ack_skb(local, skb, &info_flags, cookie); @@ -3756,7 +3756,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, return false; /* don't handle TX status request here either */ - if (skb->sk && sock_flag(skb->sk, SOCK_WIFI_STATUS)) + if (sk_requests_wifi_status(skb->sk)) return false; if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { @@ -4648,7 +4648,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info)); } - if (unlikely(skb->sk && sock_flag(skb->sk, SOCK_WIFI_STATUS))) { + if (unlikely(sk_requests_wifi_status(skb->sk))) { info->status_data = ieee80211_store_ack_skb(local, skb, &info->flags, NULL); if (info->status_data) -- 2.51.0 From da1b9a55ff116cb040528ef664c70a4eec03ae99 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 16 May 2025 20:41:06 +0200 Subject: [PATCH 14/16] wifi: p54: prevent buffer-overflow in p54_rx_eeprom_readback() Robert Morris reported: |If a malicious USB device pretends to be an Intersil p54 wifi |interface and generates an eeprom_readback message with a large |eeprom->v1.len, p54_rx_eeprom_readback() will copy data from the |message beyond the end of priv->eeprom. | |static void p54_rx_eeprom_readback(struct p54_common *priv, | struct sk_buff *skb) |{ | struct p54_hdr *hdr = (struct p54_hdr *) skb->data; | struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data; | | if (priv->fw_var >= 0x509) { | memcpy(priv->eeprom, eeprom->v2.data, | le16_to_cpu(eeprom->v2.len)); | } else { | memcpy(priv->eeprom, eeprom->v1.data, | le16_to_cpu(eeprom->v1.len)); | } | [...] The eeprom->v{1,2}.len is set by the driver in p54_download_eeprom(). The device is supposed to provide the same length back to the driver. But yes, it's possible (like shown in the report) to alter the value to something that causes a crash/panic due to overrun. This patch addresses the issue by adding the size to the common device context, so p54_rx_eeprom_readback no longer relies on possibly tampered values... That said, it also checks if the "firmware" altered the value and no longer copies them. The one, small saving grace is: Before the driver tries to read the eeprom, it needs to upload >a< firmware. the vendor firmware has a proprietary license and as a reason, it is not present on most distributions by default. Cc: Reported-by: Robert Morris Closes: https://lore.kernel.org/linux-wireless/28782.1747258414@localhost/ Fixes: 7cb770729ba8 ("p54: move eeprom code into common library") Signed-off-by: Christian Lamparter Link: https://patch.msgid.link/20250516184107.47794-1-chunkeey@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/intersil/p54/fwio.c | 2 ++ drivers/net/wireless/intersil/p54/p54.h | 1 + drivers/net/wireless/intersil/p54/txrx.c | 13 +++++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c index 772084a9bd8d..3baf8ab01e22 100644 --- a/drivers/net/wireless/intersil/p54/fwio.c +++ b/drivers/net/wireless/intersil/p54/fwio.c @@ -231,6 +231,7 @@ int p54_download_eeprom(struct p54_common *priv, void *buf, mutex_lock(&priv->eeprom_mutex); priv->eeprom = buf; + priv->eeprom_slice_size = len; eeprom_hdr = skb_put(skb, eeprom_hdr_size + len); if (priv->fw_var < 0x509) { @@ -253,6 +254,7 @@ int p54_download_eeprom(struct p54_common *priv, void *buf, ret = -EBUSY; } priv->eeprom = NULL; + priv->eeprom_slice_size = 0; mutex_unlock(&priv->eeprom_mutex); return ret; } diff --git a/drivers/net/wireless/intersil/p54/p54.h b/drivers/net/wireless/intersil/p54/p54.h index 522656de4159..aeb5e40cc5ef 100644 --- a/drivers/net/wireless/intersil/p54/p54.h +++ b/drivers/net/wireless/intersil/p54/p54.h @@ -258,6 +258,7 @@ struct p54_common { /* eeprom handling */ void *eeprom; + size_t eeprom_slice_size; struct completion eeprom_comp; struct mutex eeprom_mutex; }; diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c index 8414aa208655..2deb1bb54f24 100644 --- a/drivers/net/wireless/intersil/p54/txrx.c +++ b/drivers/net/wireless/intersil/p54/txrx.c @@ -496,14 +496,19 @@ static void p54_rx_eeprom_readback(struct p54_common *priv, return ; if (priv->fw_var >= 0x509) { - memcpy(priv->eeprom, eeprom->v2.data, - le16_to_cpu(eeprom->v2.len)); + if (le16_to_cpu(eeprom->v2.len) != priv->eeprom_slice_size) + return; + + memcpy(priv->eeprom, eeprom->v2.data, priv->eeprom_slice_size); } else { - memcpy(priv->eeprom, eeprom->v1.data, - le16_to_cpu(eeprom->v1.len)); + if (le16_to_cpu(eeprom->v1.len) != priv->eeprom_slice_size) + return; + + memcpy(priv->eeprom, eeprom->v1.data, priv->eeprom_slice_size); } priv->eeprom = NULL; + priv->eeprom_slice_size = 0; tmp = p54_find_and_unlink_skb(priv, hdr->req_id); dev_kfree_skb_any(tmp); complete(&priv->eeprom_comp); -- 2.51.0 From 78a7a126dc5b8e3c5a3d4da9f513e0236d2dc1a3 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Fri, 16 May 2025 16:02:07 +0530 Subject: [PATCH 15/16] wifi: mac80211: validate SCAN_FLAG_AP in scan request during MLO When an AP interface is already beaconing, a subsequent scan is not allowed unless the user space explicitly sets the flag NL80211_SCAN_FLAG_AP in the scan request. If this flag is not set, the scan request will be returned with the error code -EOPNOTSUPP. However, this restriction currently applies only to non-ML interfaces. For ML interfaces, scans are allowed without this flag being explicitly set by the user space which is wrong. This is because the beaconing check currently uses only the deflink, which does not get set during MLO. Hence to fix this, during MLO, use the existing helper ieee80211_num_beaconing_links() to know if any of the link is beaconing. Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250516-bug_fix_mlo_scan-v2-1-12e59d9110ac@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 05b7296b152c..d9d88f2f2831 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2924,7 +2924,7 @@ static int ieee80211_scan(struct wiphy *wiphy, * the frames sent while scanning on other channel will be * lost) */ - if (sdata->deflink.u.ap.beacon && + if (ieee80211_num_beaconing_links(sdata) && (!(wiphy->features & NL80211_FEATURE_AP_SCAN) || !(req->flags & NL80211_SCAN_FLAG_AP))) return -EOPNOTSUPP; -- 2.51.0 From 0b0ff976af94fc2437b62e3798f11aacc3798613 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Fri, 16 May 2025 16:02:08 +0530 Subject: [PATCH 16/16] wifi: mac80211: accept probe response on link address as well If a random MAC address is not requested during scan request, unicast probe response frames are only accepted if the destination address matches the interface address. This works fine for non-ML interfaces. However, with MLO, the same interface can have multiple links, and a scan on a link would be requested with the link address. In such cases, the probe response frame gets dropped which is incorrect. Therefore, add logic to check if any of the link addresses match the destination address if the interface address does not match. Signed-off-by: Aditya Kumar Singh Link: https://patch.msgid.link/20250516-bug_fix_mlo_scan-v2-2-12e59d9110ac@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/scan.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index cb7079071885..7b8da40a912d 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -240,6 +240,9 @@ static bool ieee80211_scan_accept_presp(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel, u32 scan_flags, const u8 *da) { + struct ieee80211_link_data *link_sdata; + u8 link_id; + if (!sdata) return false; @@ -251,7 +254,20 @@ static bool ieee80211_scan_accept_presp(struct ieee80211_sub_if_data *sdata, if (scan_flags & NL80211_SCAN_FLAG_RANDOM_ADDR) return true; - return ether_addr_equal(da, sdata->vif.addr); + + if (ether_addr_equal(da, sdata->vif.addr)) + return true; + + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + link_sdata = rcu_dereference(sdata->link[link_id]); + if (!link_sdata) + continue; + + if (ether_addr_equal(da, link_sdata->conf->addr)) + return true; + } + + return false; } void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) -- 2.51.0