| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/26 17:32 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch series adds support for the RTL8723BS SDIO WiFi/Bluetooth coexistence and modifies SDIO-specific TX/RX handling. All functional changes are either confined to the SDIO interface (sdio.c) or explicitly guarded by `rtw_is_8723bs(rtwdev)`, which checks for `RTW_HCI_TYPE_SDIO`. Since standard virtualized environments (like QEMU) used by syzkaller do not emulate physical SDIO WiFi cards, this driver will not probe and the modified code paths are impossible to reach.",
"WorthFuzzing": false
} |
| 1/1 | 2026/07/26 17:32 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit e089b7d59ab9d93ea51a7142f2e491e0c0cf516a\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sun Jul 26 17:32:44 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c\nindex 37c336def41915..314b2a368a8f17 100644\n--- a/drivers/net/wireless/realtek/rtw88/coex.c\n+++ b/drivers/net/wireless/realtek/rtw88/coex.c\n@@ -1443,6 +1443,191 @@ static void rtw_coex_set_ant_path(struct rtw_dev *rtwdev, bool force, u8 phase)\n #define case_ALGO(src) \\\n \tcase COEX_ALGO_##src: return #src\n \n+/* 8723BS SDIO WiFi/BT coexistence antenna handling. On BT-disabled boards the\n+ * scan/auth window still routes through the PTA mux; these helpers force the\n+ * vendor-shaped WiFi-owned antenna path so directed management TX reaches air.\n+ */\n+#define REG_8723BS_BT_COEX_CTRL\t\t0x0039\n+#define REG_8723BS_BB_ANT_CFG\t\t0x0930\n+#define REG_8723BS_BB_ANT_CFG1\t\t0x0944\n+#define REG_8723BS_BB_ANT_BUF\t\t0x0974\n+#define RTW8723BS_COEX_H_WLAN_ACTIVE\t0x1800101b\n+\n+static bool rtw_coex_8723bs_ant_is_aux(struct rtw_dev *rtwdev)\n+{\n+\treturn !!(rtwdev-\u003eefuse.bt_setting \u0026 BIT(6));\n+}\n+\n+static bool rtw_coex_8723bs_bt_disabled(struct rtw_dev *rtwdev)\n+{\n+\treturn rtw_is_8723bs(rtwdev) \u0026\u0026 rtwdev-\u003ecoex.stat.bt_disabled;\n+}\n+\n+static u32 rtw_coex_8723bs_pta_ant_path(struct rtw_dev *rtwdev)\n+{\n+\treturn rtw_coex_8723bs_ant_is_aux(rtwdev) ? 0x80 : 0x200;\n+}\n+\n+/* Write BB_SEL_BTG, retrying once with SYS_FUNC BB reset if the first\n+ * write does not stick (RF/BB clock may have been gated).\n+ */\n+static u32 rtw_coex_8723bs_write_bb_sel_btg(struct rtw_dev *rtwdev, u32 value)\n+{\n+\tu8 sys_func_before;\n+\tu32 readback;\n+\n+\tsys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);\n+\tif ((sys_func_before \u0026 (BIT(0) | BIT(1))) != (BIT(0) | BIT(1))) {\n+\t\trtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT(0) | BIT(1));\n+\t\tusleep_range(10, 11);\n+\t}\n+\n+\trtw_write32(rtwdev, REG_BB_SEL_BTG_8723B, value);\n+\treadback = rtw_read32(rtwdev, REG_BB_SEL_BTG_8723B);\n+\tif (readback == value)\n+\t\treturn readback;\n+\n+\tusleep_range(10, 11);\n+\trtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT(0) | BIT(1));\n+\trtw_write32(rtwdev, REG_BB_SEL_BTG_8723B, value);\n+\n+\treturn rtw_read32(rtwdev, REG_BB_SEL_BTG_8723B);\n+}\n+\n+static u32 rtw_coex_8723bs_reassert_pta_ant(struct rtw_dev *rtwdev)\n+{\n+\treturn rtw_coex_8723bs_write_bb_sel_btg(rtwdev,\n+\t\t\t\t\t\trtw_coex_8723bs_pta_ant_path(rtwdev));\n+}\n+\n+static void rtw_coex_8723bs_set_cck_pri(struct rtw_dev *rtwdev, bool high)\n+{\n+\tif (!rtw_coex_8723bs_bt_disabled(rtwdev))\n+\t\treturn;\n+\n+\tif (high) {\n+\t\trtw_write32(rtwdev, REG_BT_COEX_TABLE_H,\n+\t\t\t RTW8723BS_COEX_H_WLAN_ACTIVE);\n+\t} else {\n+\t\trtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_TX_CCK, false);\n+\t\trtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_RX_CCK, false);\n+\t}\n+}\n+\n+static void rtw_coex_8723bs_restore_pad_ctrl(struct rtw_dev *rtwdev,\n+\t\t\t\t\t bool keep_pta_owner)\n+{\n+\tu32 before, after;\n+\n+\tbefore = rtw_read32(rtwdev, REG_PAD_CTRL1);\n+\tafter = before \u0026 ~(BIT_LNAON_WLBT_SEL | BIT_SW_DPDT_SEL_DATA);\n+\tif (keep_pta_owner)\n+\t\tafter |= BIT_PAPE_WLBT_SEL;\n+\telse\n+\t\tafter \u0026= ~BIT_PAPE_WLBT_SEL;\n+\tif (after != before)\n+\t\trtw_write32(rtwdev, REG_PAD_CTRL1, after);\n+}\n+\n+static void rtw_coex_8723bs_fw_gnt_bt_low(struct rtw_dev *rtwdev)\n+{\n+\tif (!rtw_coex_8723bs_bt_disabled(rtwdev))\n+\t\treturn;\n+\n+\tif (rtw_read8(rtwdev, REG_GNT_BT) == 0x00 \u0026\u0026\n+\t rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL) == 0x0c)\n+\t\treturn;\n+\n+\trtw_fw_set_gnt_bt(rtwdev, 0);\n+}\n+\n+static void rtw_coex_8723bs_force_assoc_pta_ant(struct rtw_dev *rtwdev)\n+{\n+\tu32 ant_target;\n+\n+\tif (!rtw_coex_8723bs_bt_disabled(rtwdev))\n+\t\treturn;\n+\n+\tant_target = rtw_coex_8723bs_pta_ant_path(rtwdev);\n+\n+\trtw_coex_8723bs_fw_gnt_bt_low(rtwdev);\n+\trtw_coex_set_ant_switch(rtwdev, COEX_SWITCH_CTRL_BY_PTA,\n+\t\t\t\tCOEX_SWITCH_TO_NOCARE);\n+\trtw_coex_8723bs_set_cck_pri(rtwdev, true);\n+\trtw_coex_8723bs_write_bb_sel_btg(rtwdev, ant_target);\n+\trtw_coex_8723bs_restore_pad_ctrl(rtwdev, true);\n+}\n+\n+static void rtw_coex_8723bs_reassert_ant_buffer(struct rtw_dev *rtwdev)\n+{\n+\tu8 sys_func_before;\n+\n+\tsys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);\n+\tif ((sys_func_before \u0026 (BIT(0) | BIT(1))) != (BIT(0) | BIT(1))) {\n+\t\trtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT(0) | BIT(1));\n+\t\tusleep_range(10, 11);\n+\t}\n+\n+\trtw_write8_mask(rtwdev, REG_8723BS_BT_COEX_CTRL, BIT(3), 0x1);\n+\trtw_write8(rtwdev, REG_8723BS_BB_ANT_BUF, 0xff);\n+\trtw_write8_mask(rtwdev, REG_8723BS_BB_ANT_CFG1, 0x3, 0x3);\n+\trtw_write8(rtwdev, REG_8723BS_BB_ANT_CFG, 0x77);\n+}\n+\n+static void rtw_coex_8723bs_apply_scan_table(struct rtw_dev *rtwdev)\n+{\n+\trtwdev-\u003ecoex.dm.cur_table = 2;\n+\trtw_coex_set_table(rtwdev, true, 0x5a5a5a5a, 0x5a5a5a5a);\n+}\n+\n+/* Non-connected scan/auth workaround: PS-TDMA type 8 off, PTA antenna path,\n+ * coex table type 2 (matches the vendor non-connected arbitration).\n+ */\n+void rtw_coex_8723bs_scan_workaround(struct rtw_dev *rtwdev)\n+{\n+\tstruct rtw_coex_dm *coex_dm = \u0026rtwdev-\u003ecoex.dm;\n+\tstruct rtw_coex_stat *coex_stat = \u0026rtwdev-\u003ecoex.stat;\n+\n+\tif (!rtw_is_8723bs(rtwdev))\n+\t\treturn;\n+\n+\tcoex_dm-\u003ecur_ps_tdma_on = false;\n+\tcoex_dm-\u003ecur_ps_tdma = 8;\n+\tcoex_dm-\u003eps_tdma_para[0] = 0x08;\n+\tcoex_dm-\u003eps_tdma_para[1] = 0x00;\n+\tcoex_dm-\u003eps_tdma_para[2] = 0x00;\n+\tcoex_dm-\u003eps_tdma_para[3] = 0x00;\n+\tcoex_dm-\u003eps_tdma_para[4] = 0x00;\n+\n+\trtw_fw_coex_tdma_type(rtwdev, 0x08, 0x00, 0x00, 0x00, 0x00);\n+\trtw_coex_8723bs_fw_gnt_bt_low(rtwdev);\n+\trtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_2G);\n+\trtw_coex_8723bs_reassert_ant_buffer(rtwdev);\n+\trtw_coex_8723bs_apply_scan_table(rtwdev);\n+\tif (coex_stat-\u003ebt_disabled)\n+\t\trtw_coex_8723bs_set_cck_pri(rtwdev, true);\n+\trtw_coex_8723bs_reassert_pta_ant(rtwdev);\n+\trtw_coex_8723bs_restore_pad_ctrl(rtwdev, true);\n+}\n+\n+/* Replayed immediately before start_clnt_join()/auth: BT_INFO + PS-TDMA type 8\n+ * then the forced WiFi PTA antenna path, so the auth window is clean.\n+ */\n+void rtw_coex_8723bs_pre_auth_h2c(struct rtw_dev *rtwdev)\n+{\n+\tlockdep_assert_held(\u0026rtwdev-\u003emutex);\n+\n+\tif (!rtw_coex_8723bs_bt_disabled(rtwdev))\n+\t\treturn;\n+\n+\trtw_fw_query_bt_info(rtwdev);\n+\trtw_fw_coex_tdma_type(rtwdev, 0x08, 0x00, 0x00, 0x00, 0x00);\n+\trtw_fw_coex_tdma_type(rtwdev, 0x08, 0x00, 0x00, 0x00, 0x00);\n+\trtw_fw_coex_tdma_type(rtwdev, 0x08, 0x00, 0x00, 0x00, 0x00);\n+\trtw_coex_8723bs_fw_gnt_bt_low(rtwdev);\n+\trtw_coex_8723bs_force_assoc_pta_ant(rtwdev);\n+}\n+\n static const char *rtw_coex_get_algo_string(u8 algo)\n {\n \tswitch (algo) {\n@@ -2770,7 +2955,7 @@ void rtw_coex_power_on_setting(struct rtw_dev *rtwdev)\n \tcoex-\u003estop_dm = true;\n \tcoex-\u003ewl_rf_off = false;\n \n-\t/* enable BB, we can write 0x948 */\n+\t/* enable BB, so BB_SEL_BTG is writable */\n \trtw_write8_set(rtwdev, REG_SYS_FUNC_EN,\n \t\t BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);\n \n@@ -2877,6 +3062,33 @@ void rtw_coex_scan_notify(struct rtw_dev *rtwdev, u8 type)\n \tcoex-\u003efreeze = false;\n \trtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true);\n \n+\t/* 8723BS SDIO BT-disabled: keep the scan/auth PTA antenna state the\n+\t * vendor uses and skip the generic coex run. At scan start (firmware\n+\t * has been up long enough for stable RX DMA) replay the vendor BT_MP /\n+\t * BT_INFO queries, then re-establish the scan-path PTA setup.\n+\t */\n+\tif (rtw_coex_8723bs_bt_disabled(rtwdev)) {\n+\t\tif (type == COEX_SCAN_START_2G || type == COEX_SCAN_START) {\n+\t\t\tstruct rtw_coex_info_req req = {};\n+\n+\t\t\tcoex_stat-\u003ecnt_wl[COEX_CNT_WL_SCANAP] = 0;\n+\t\t\tcoex_stat-\u003ewl_hi_pri_task2 = true;\n+\n+\t\t\treq.seq = 0x0e;\n+\t\t\treq.op_code = BT_MP_INFO_OP_SUPP_VER;\n+\t\t\trtw_fw_query_bt_mp_info(rtwdev, \u0026req);\n+\t\t\treq.seq = 0x0f;\n+\t\t\treq.op_code = BT_MP_INFO_OP_PATCH_VER;\n+\t\t\trtw_fw_query_bt_mp_info(rtwdev, \u0026req);\n+\t\t\trtw_fw_query_bt_info(rtwdev);\n+\n+\t\t\trtw_coex_8723bs_scan_workaround(rtwdev);\n+\t\t} else {\n+\t\t\tcoex_stat-\u003ewl_hi_pri_task2 = false;\n+\t\t}\n+\t\treturn;\n+\t}\n+\n \tif (type == COEX_SCAN_START_5G) {\n \t\trtw_dbg(rtwdev, RTW_DBG_COEX,\n \t\t\t\"[BTCoex], SCAN START notify (5G)\\n\");\n@@ -2938,6 +3150,17 @@ void rtw_coex_connect_notify(struct rtw_dev *rtwdev, u8 type)\n \tif (coex-\u003emanual_control || coex-\u003estop_dm)\n \t\treturn;\n \n+\t/* 8723BS SDIO BT-disabled: the vendor ConnectNotify() early-returns\n+\t * without sending H2Cs; scan_workaround already established the PTA\n+\t * path / coex table / PS-TDMA. Keep only the register-level PTA\n+\t * reassertion at associate-start.\n+\t */\n+\tif (rtw_coex_8723bs_bt_disabled(rtwdev)) {\n+\t\tif (type == COEX_ASSOCIATE_START)\n+\t\t\trtw_coex_8723bs_force_assoc_pta_ant(rtwdev);\n+\t\treturn;\n+\t}\n+\n \trtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true);\n \n \tif (type == COEX_ASSOCIATE_5G_START) {\ndiff --git a/drivers/net/wireless/realtek/rtw88/coex.h b/drivers/net/wireless/realtek/rtw88/coex.h\nindex c398be8391f7b0..c58ae0a50b30b1 100644\n--- a/drivers/net/wireless/realtek/rtw88/coex.h\n+++ b/drivers/net/wireless/realtek/rtw88/coex.h\n@@ -430,4 +430,7 @@ static inline void rtw_coex_active_query_bt_info(struct rtw_dev *rtwdev)\n \t\trtw_coex_query_bt_info(rtwdev);\n }\n \n+void rtw_coex_8723bs_scan_workaround(struct rtw_dev *rtwdev);\n+void rtw_coex_8723bs_pre_auth_h2c(struct rtw_dev *rtwdev);\n+\n #endif\ndiff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c\nindex 945fedcd375b62..7c17086cac32c4 100644\n--- a/drivers/net/wireless/realtek/rtw88/fw.c\n+++ b/drivers/net/wireless/realtek/rtw88/fw.c\n@@ -786,6 +786,17 @@ void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect)\n \trtw_fw_send_h2c_command(rtwdev, h2c_pkt);\n }\n \n+/* 8723BS SDIO: set the firmware GNT_BT state (0 = WiFi owns the antenna). */\n+void rtw_fw_set_gnt_bt(struct rtw_dev *rtwdev, u8 state)\n+{\n+\tu8 h2c_pkt[H2C_PKT_SIZE] = {0};\n+\n+\tSET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_GNT_BT);\n+\tSET_GNT_BT_STATE(h2c_pkt, state);\n+\n+\trtw_fw_send_h2c_command(rtwdev, h2c_pkt);\n+}\n+\n void rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev)\n {\n \tstruct rtw_traffic_stats *stats = \u0026rtwdev-\u003estats;\n@@ -1466,10 +1477,14 @@ void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev,\n \trtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_INFO, true);\n }\n \n+/* REG_DWBCN1_CTRL bit 20 (SW_BCN_SEL for port 0), relative to byte +2 */\n+#define BIT_OFFSET_DWBCN1_SW_BCN_SEL_PORT0\t(20 - 16)\n+\n int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,\n \t\t\t\tu8 *buf, u32 size)\n {\n-\tu8 bckp[3];\n+\tconst bool is_8723bs_sdio = rtw_is_8723bs(rtwdev);\n+\tu8 bckp[4];\n \tu8 val;\n \tu16 rsvd_pg_head;\n \tu32 bcn_valid_addr;\n@@ -1499,13 +1514,28 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,\n \trtw_write8(rtwdev, REG_BCN_CTRL,\n \t\t (bckp[2] \u0026 ~BIT_EN_BCN_FUNCTION) | BIT_DIS_TSF_UDT);\n \n-\tif (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) {\n+\t/* Clear BIT_EN_BCNQ_DL so the chip does not treat the reserved-page\n+\t * upload as a real beacon; otherwise BIT_BCN_VALID never asserts. The\n+\t * vendor rtl8723bs driver does this unconditionally; rtw88 only did it\n+\t * for PCIe, which left 8723BS SDIO's BCN_VALID handshake failing.\n+\t */\n+\tif (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE || is_8723bs_sdio) {\n \t\tval = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2);\n \t\tbckp[1] = val;\n \t\tval \u0026= ~(BIT_EN_BCNQ_DL \u003e\u003e 16);\n \t\trtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, val);\n \t}\n \n+\t/* 8723BS SDIO: point the SW beacon download path at port 0, else\n+\t * BIT_BCN_VALID is never asserted after the SDIO upload completes.\n+\t */\n+\tif (is_8723bs_sdio) {\n+\t\tval = rtw_read8(rtwdev, REG_DWBCN1_CTRL + 2);\n+\t\tbckp[3] = val;\n+\t\tval \u0026= ~BIT(BIT_OFFSET_DWBCN1_SW_BCN_SEL_PORT0);\n+\t\trtw_write8(rtwdev, REG_DWBCN1_CTRL + 2, val);\n+\t}\n+\n \tret = rtw_hci_write_data_rsvd_page(rtwdev, buf, size);\n \tif (ret) {\n \t\trtw_err(rtwdev, \"failed to write data to rsvd page\\n\");\n@@ -1526,11 +1556,13 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,\n \t}\n \n restore:\n+\tif (is_8723bs_sdio)\n+\t\trtw_write8(rtwdev, REG_DWBCN1_CTRL + 2, bckp[3]);\n \trsvd_pg_head = rtwdev-\u003efifo.rsvd_boundary;\n \trtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2,\n \t\t rsvd_pg_head | BIT_BCN_VALID_V1);\n \trtw_write8(rtwdev, REG_BCN_CTRL, bckp[2]);\n-\tif (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE)\n+\tif (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE || is_8723bs_sdio)\n \t\trtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]);\n \trtw_write8(rtwdev, REG_CR + 1, bckp[0]);\n \ndiff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h\nindex 48ad9ceab6ea12..f5ed65c46350f4 100644\n--- a/drivers/net/wireless/realtek/rtw88/fw.h\n+++ b/drivers/net/wireless/realtek/rtw88/fw.h\n@@ -572,6 +572,7 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)\n #define H2C_CMD_QUERY_BT_MP_INFO\t0x67\n #define H2C_CMD_BT_WIFI_CONTROL\t\t0x69\n #define H2C_CMD_WIFI_CALIBRATION\t0x6d\n+#define H2C_CMD_GNT_BT\t\t\t0x6e\n #define H2C_CMD_QUERY_BT_HID_INFO\t0x73\n \n #define H2C_CMD_KEEP_ALIVE\t\t0x03\n@@ -684,6 +685,8 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)\n \tle32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(31, 24))\n #define SET_QUERY_BT_INFO(h2c_pkt, value) \\\n \tle32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))\n+#define SET_GNT_BT_STATE(h2c_pkt, value) \\\n+\tle32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))\n #define SET_WL_CH_INFO_LINK(h2c_pkt, value) \\\n \tle32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8))\n #define SET_WL_CH_INFO_CHNL(h2c_pkt, value) \\\n@@ -849,6 +852,7 @@ void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si);\n void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,\n \t\t\t bool reset_ra_mask);\n void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool conn);\n+void rtw_fw_set_gnt_bt(struct rtw_dev *rtwdev, u8 state);\n void rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev);\n void rtw_fw_beacon_filter_config(struct rtw_dev *rtwdev, bool connect,\n \t\t\t\t struct ieee80211_vif *vif);\ndiff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c\nindex 766f22d31079ee..91014a8e242b32 100644\n--- a/drivers/net/wireless/realtek/rtw88/mac80211.c\n+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c\n@@ -15,6 +15,463 @@\n #include \"wow.h\"\n #include \"sar.h\"\n \n+/* 8723BS SDIO: record a beacon/probe-resp seen from the target BSSID during\n+ * the pre-auth window so the join sequence (mgd_prepare_tx) can wait for it,\n+ * mirroring the vendor start_clnt_join(). Called from the SDIO RX path.\n+ */\n+void rtw8723bs_auth_sync_rx(struct rtw_dev *rtwdev,\n+\t\t\t const struct ieee80211_hdr *hdr, u32 len,\n+\t\t\t const struct rtw_rx_pkt_stat *pkt_stat,\n+\t\t\t const struct ieee80211_rx_status *rx_status)\n+{\n+\tstruct rtw_auth_sync *sync = \u0026rtwdev-\u003eauth_sync;\n+\tunsigned long flags;\n+\t__le16 fc = hdr-\u003eframe_control;\n+\n+\tif (!rtw_is_8723bs(rtwdev) ||\n+\t test_bit(RTW_FLAG_SCANNING, rtwdev-\u003eflags) ||\n+\t pkt_stat-\u003ecrc_err || pkt_stat-\u003eicv_err)\n+\t\treturn;\n+\n+\tif (!ieee80211_is_beacon(fc) \u0026\u0026 !ieee80211_is_probe_resp(fc))\n+\t\treturn;\n+\n+\tspin_lock_irqsave(\u0026sync-\u003elock, flags);\n+\tif (sync-\u003eactive \u0026\u0026 ether_addr_equal(hdr-\u003eaddr3, sync-\u003ebssid)) {\n+\t\tsync-\u003eseen = true;\n+\t\tsync-\u003eseen_count++;\n+\t\twake_up(\u0026sync-\u003ewait);\n+\t}\n+\tspin_unlock_irqrestore(\u0026sync-\u003elock, flags);\n+}\n+EXPORT_SYMBOL(rtw8723bs_auth_sync_rx);\n+\n+/* ---- 8723BS SDIO association sequence (vendor start_clnt_join) ---- */\n+\n+#define RTW8723BS_JOIN_RETRY_LIMIT\t\t0x30\n+#define RTW8723BS_AUTH_SYNC_WAIT_FALLBACK_MS\t120\n+#define RTW8723BS_AUTH_SYNC_WAIT_MIN_MS\t\t80\n+#define RTW8723BS_AUTH_SYNC_WAIT_MAX_MS\t\t160\n+#define RTW8723BS_ACK_PREAMBLE_SHORT\t\tBIT(7)\n+#define RTW8723BS_SHORT_SLOT_TIME\t\t9\n+#define RTW8723BS_LONG_SLOT_TIME\t\t20\n+#define RTW8723BS_RRSR_1M\t\t\tBIT(0)\n+#define RTW8723BS_RRSR_2M\t\t\tBIT(1)\n+#define RTW8723BS_RRSR_5_5M\t\t\tBIT(2)\n+#define RTW8723BS_RRSR_11M\t\t\tBIT(3)\n+#define RTW8723BS_RRSR_6M\t\t\tBIT(4)\n+#define RTW8723BS_RRSR_9M\t\t\tBIT(5)\n+#define RTW8723BS_RRSR_12M\t\t\tBIT(6)\n+#define RTW8723BS_RRSR_18M\t\t\tBIT(7)\n+#define RTW8723BS_RRSR_24M\t\t\tBIT(8)\n+#define RTW8723BS_RRSR_36M\t\t\tBIT(9)\n+#define RTW8723BS_RRSR_48M\t\t\tBIT(10)\n+#define RTW8723BS_RRSR_54M\t\t\tBIT(11)\n+#define RTW8723BS_RRSR_CCK_RATES \\\n+\t(RTW8723BS_RRSR_1M | RTW8723BS_RRSR_2M | \\\n+\t RTW8723BS_RRSR_5_5M | RTW8723BS_RRSR_11M)\n+#define RTW8723BS_RRSR_2G_FORCE\t\t\tRTW8723BS_RRSR_CCK_RATES\n+#define RTW8723BS_RRSR_2G_ALLOW \\\n+\t(RTW8723BS_RRSR_CCK_RATES | RTW8723BS_RRSR_6M | \\\n+\t RTW8723BS_RRSR_12M | RTW8723BS_RRSR_24M)\n+\n+/* Keep the RCR at the vendor target-only state (AMF + CBSSID) across the whole\n+ * connect window, matching the vendor STA path. accept_all is kept only for the\n+ * caller's intent; both paths converge the filter to target-only.\n+ */\n+static void rtw8723bs_auth_rx_filter(struct rtw_dev *rtwdev, bool accept_all)\n+{\n+\trtwdev-\u003ehal.rcr |= BIT_AMF | BIT_CBSSID_DATA | BIT_CBSSID_BCN;\n+\trtwdev-\u003ehal.rcr \u0026= ~BIT_AAP;\n+\trtw_write32(rtwdev, REG_RCR, rtwdev-\u003ehal.rcr);\n+}\n+\n+static void rtw8723bs_config_sec_cfg(struct rtw_dev *rtwdev)\n+{\n+\tu16 sec = rtw_read16(rtwdev, RTW_SEC_CONFIG);\n+\n+\tsec |= RTW_SEC_CHK_KEYID | RTW_SEC_TX_DEC_EN | RTW_SEC_RX_DEC_EN;\n+\trtw_write16(rtwdev, RTW_SEC_CONFIG, sec);\n+}\n+\n+static void rtw8723bs_config_default_key_search(struct rtw_dev *rtwdev,\n+\t\t\t\t\t\tbool enable)\n+{\n+\tu16 sec = rtw_read16(rtwdev, RTW_SEC_CONFIG);\n+\n+\tif (enable)\n+\t\tsec |= RTW_SEC_TX_BC_USE_DK | RTW_SEC_TX_UNI_USE_DK |\n+\t\t RTW_SEC_RX_UNI_USE_DK;\n+\telse\n+\t\tsec \u0026= ~(RTW_SEC_TX_UNI_USE_DK | RTW_SEC_RX_UNI_USE_DK |\n+\t\t\t RTW_SEC_TX_BC_USE_DK | RTW_SEC_RX_BC_USE_DK);\n+\trtw_write16(rtwdev, RTW_SEC_CONFIG, sec);\n+}\n+\n+static void rtw8723bs_enable_tsf_update(struct rtw_dev *rtwdev)\n+{\n+\trtw_write8_clr(rtwdev, REG_BCN_CTRL, BIT_DIS_TSF_UDT);\n+}\n+\n+static void rtw8723bs_set_ack_preamble(struct rtw_dev *rtwdev,\n+\t\t\t\t bool short_preamble)\n+{\n+\tu8 val = rtw_read8(rtwdev, REG_RRSR + 2) \u0026 ~RTW8723BS_ACK_PREAMBLE_SHORT;\n+\n+\tif (short_preamble)\n+\t\tval |= RTW8723BS_ACK_PREAMBLE_SHORT;\n+\trtw_write8(rtwdev, REG_RRSR + 2, val);\n+}\n+\n+static void rtw8723bs_set_slot_time(struct rtw_dev *rtwdev, bool short_slot)\n+{\n+\trtw_write8(rtwdev, REG_SLOT,\n+\t\t short_slot ? RTW8723BS_SHORT_SLOT_TIME :\n+\t\t\t\tRTW8723BS_LONG_SLOT_TIME);\n+}\n+\n+static u16 rtw8723bs_rrsr_from_ie_rate(u8 rate)\n+{\n+\tswitch (rate \u0026 0x7f) {\n+\tcase 2: return RTW8723BS_RRSR_1M;\n+\tcase 4: return RTW8723BS_RRSR_2M;\n+\tcase 11: return RTW8723BS_RRSR_5_5M;\n+\tcase 22: return RTW8723BS_RRSR_11M;\n+\tcase 12: return RTW8723BS_RRSR_6M;\n+\tcase 18: return RTW8723BS_RRSR_9M;\n+\tcase 24: return RTW8723BS_RRSR_12M;\n+\tcase 36: return RTW8723BS_RRSR_18M;\n+\tcase 48: return RTW8723BS_RRSR_24M;\n+\tcase 72: return RTW8723BS_RRSR_36M;\n+\tcase 96: return RTW8723BS_RRSR_48M;\n+\tcase 108: return RTW8723BS_RRSR_54M;\n+\tdefault: return 0;\n+\t}\n+}\n+\n+static void rtw8723bs_collect_basic_rates(const u8 *ie, u16 *basic_rates,\n+\t\t\t\t\t bool *valid)\n+{\n+\tint i;\n+\n+\tif (!ie)\n+\t\treturn;\n+\n+\tfor (i = 0; i \u003c ie[1]; i++) {\n+\t\tu16 r;\n+\n+\t\tif (!(ie[i + 2] \u0026 0x80))\n+\t\t\tcontinue;\n+\t\tr = rtw8723bs_rrsr_from_ie_rate(ie[i + 2]);\n+\t\tif (!r)\n+\t\t\tcontinue;\n+\t\t*basic_rates |= r;\n+\t\t*valid = true;\n+\t}\n+}\n+\n+static void rtw8723bs_reset_response_rates(struct rtw_dev *rtwdev)\n+{\n+\trtw_write32(rtwdev, REG_RRSR, 0xffff1);\n+\trtwdev-\u003edm_info.rrsr_val_init = 0xffff1;\n+}\n+\n+static void rtw8723bs_apply_basic_rates(struct rtw_dev *rtwdev,\n+\t\t\t\t\tstruct ieee80211_vif *vif,\n+\t\t\t\t\tconst u8 *bssid)\n+{\n+\tstruct ieee80211_bss_conf *conf = \u0026vif-\u003ebss_conf;\n+\tstruct cfg80211_bss *lookup_bss = NULL;\n+\tstruct cfg80211_bss *bss = NULL;\n+\tbool valid = false;\n+\tu16 basic_rates = 0;\n+\n+\tif (!rtw_is_8723bs(rtwdev) || vif-\u003etype != NL80211_IFTYPE_STATION)\n+\t\treturn;\n+\n+\tif (conf-\u003ebss) {\n+\t\tbss = conf-\u003ebss;\n+\t} else if (bssid \u0026\u0026 is_valid_ether_addr(bssid)) {\n+\t\tlookup_bss = cfg80211_get_bss(rtwdev-\u003ehw-\u003ewiphy, NULL,\n+\t\t\t\t\t bssid, NULL, 0,\n+\t\t\t\t\t IEEE80211_BSS_TYPE_ESS,\n+\t\t\t\t\t IEEE80211_PRIVACY_ANY);\n+\t\tbss = lookup_bss;\n+\t}\n+\n+\tif (bss) {\n+\t\tconst u8 *rates, *ext;\n+\n+\t\trcu_read_lock();\n+\t\trates = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);\n+\t\text = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);\n+\t\trtw8723bs_collect_basic_rates(rates, \u0026basic_rates, \u0026valid);\n+\t\trtw8723bs_collect_basic_rates(ext, \u0026basic_rates, \u0026valid);\n+\t\trcu_read_unlock();\n+\t}\n+\n+\tif (valid) {\n+\t\tbasic_rates |= RTW8723BS_RRSR_2G_FORCE;\n+\t\tbasic_rates \u0026= RTW8723BS_RRSR_2G_ALLOW;\n+\t\trtw_write16(rtwdev, REG_RRSR, basic_rates);\n+\t\trtw_write8(rtwdev, REG_RRSR + 2,\n+\t\t\t rtw_read8(rtwdev, REG_RRSR + 2) \u0026 0xf0);\n+\t\trtwdev-\u003edm_info.rrsr_val_init = basic_rates;\n+\t}\n+\n+\tif (lookup_bss)\n+\t\tcfg80211_put_bss(rtwdev-\u003ehw-\u003ewiphy, lookup_bss);\n+}\n+\n+/* Program response slot time (and, when set_preamble, the ACK preamble) from\n+ * the selected scan BSS capabilities; the AP capabilities are not yet in\n+ * bss_conf at mgd_prepare_tx() time.\n+ */\n+static void rtw8723bs_apply_bss_cap(struct rtw_dev *rtwdev,\n+\t\t\t\t struct ieee80211_vif *vif,\n+\t\t\t\t const u8 *bssid, bool set_preamble)\n+{\n+\tstruct ieee80211_bss_conf *conf = \u0026vif-\u003ebss_conf;\n+\tstruct cfg80211_bss *lookup_bss = NULL;\n+\tstruct cfg80211_bss *bss = NULL;\n+\tbool short_preamble, short_slot;\n+\tu16 cap = 0;\n+\n+\tif (!rtw_is_8723bs(rtwdev) || vif-\u003etype != NL80211_IFTYPE_STATION)\n+\t\treturn;\n+\n+\tif (conf-\u003ebss) {\n+\t\tbss = conf-\u003ebss;\n+\t} else if (bssid \u0026\u0026 is_valid_ether_addr(bssid)) {\n+\t\tlookup_bss = cfg80211_get_bss(rtwdev-\u003ehw-\u003ewiphy, NULL,\n+\t\t\t\t\t bssid, NULL, 0,\n+\t\t\t\t\t IEEE80211_BSS_TYPE_ESS,\n+\t\t\t\t\t IEEE80211_PRIVACY_ANY);\n+\t\tbss = lookup_bss;\n+\t}\n+\n+\tif (bss) {\n+\t\tcap = bss-\u003ecapability;\n+\t} else if (conf-\u003eassoc_capability) {\n+\t\tcap = conf-\u003eassoc_capability;\n+\t} else {\n+\t\tshort_preamble = conf-\u003euse_short_preamble;\n+\t\tshort_slot = conf-\u003euse_short_slot;\n+\t\tgoto program;\n+\t}\n+\tshort_preamble = !!(cap \u0026 WLAN_CAPABILITY_SHORT_PREAMBLE);\n+\tshort_slot = !!(cap \u0026 WLAN_CAPABILITY_SHORT_SLOT_TIME);\n+\n+program:\n+\tif (set_preamble)\n+\t\trtw8723bs_set_ack_preamble(rtwdev, short_preamble);\n+\trtw8723bs_set_slot_time(rtwdev, short_slot);\n+\n+\tif (lookup_bss)\n+\t\tcfg80211_put_bss(rtwdev-\u003ehw-\u003ewiphy, lookup_bss);\n+}\n+\n+static unsigned int rtw8723bs_auth_sync_wait_ms(struct ieee80211_vif *vif)\n+{\n+\tu16 beacon_int = vif-\u003ebss_conf.beacon_int;\n+\tunsigned int wait_ms;\n+\n+\tif (!beacon_int)\n+\t\treturn RTW8723BS_AUTH_SYNC_WAIT_FALLBACK_MS;\n+\n+\twait_ms = DIV_ROUND_UP(beacon_int * 1024, 1000) + 20;\n+\treturn clamp_t(unsigned int, wait_ms, RTW8723BS_AUTH_SYNC_WAIT_MIN_MS,\n+\t\t RTW8723BS_AUTH_SYNC_WAIT_MAX_MS);\n+}\n+\n+static void rtw8723bs_auth_sync_start(struct rtw_dev *rtwdev, const u8 *bssid)\n+{\n+\tstruct rtw_auth_sync *sync = \u0026rtwdev-\u003eauth_sync;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026sync-\u003elock, flags);\n+\tether_addr_copy(sync-\u003ebssid, bssid);\n+\tsync-\u003eseen = false;\n+\tsync-\u003eseen_count = 0;\n+\tsync-\u003eactive = true;\n+\tspin_unlock_irqrestore(\u0026sync-\u003elock, flags);\n+}\n+\n+static void rtw8723bs_auth_sync_stop(struct rtw_dev *rtwdev)\n+{\n+\tstruct rtw_auth_sync *sync = \u0026rtwdev-\u003eauth_sync;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(\u0026sync-\u003elock, flags);\n+\tsync-\u003eactive = false;\n+\tspin_unlock_irqrestore(\u0026sync-\u003elock, flags);\n+}\n+\n+static bool rtw8723bs_auth_sync_seen(struct rtw_dev *rtwdev)\n+{\n+\tstruct rtw_auth_sync *sync = \u0026rtwdev-\u003eauth_sync;\n+\tunsigned long flags;\n+\tbool seen;\n+\n+\tspin_lock_irqsave(\u0026sync-\u003elock, flags);\n+\tseen = sync-\u003eseen;\n+\tspin_unlock_irqrestore(\u0026sync-\u003elock, flags);\n+\n+\treturn seen;\n+}\n+\n+static bool rtw8723bs_auth_sync_wait(struct rtw_dev *rtwdev,\n+\t\t\t\t unsigned int wait_ms)\n+{\n+\tstruct rtw_auth_sync *sync = \u0026rtwdev-\u003eauth_sync;\n+\n+\treturn wait_event_timeout(sync-\u003ewait, rtw8723bs_auth_sync_seen(rtwdev),\n+\t\t\t\t msecs_to_jiffies(wait_ms)) \u003e 0;\n+}\n+\n+static bool rtw8723bs_mgd_prepare_is_auth(struct rtw_dev *rtwdev,\n+\t\t\t\t\t struct ieee80211_prep_tx_info *info)\n+{\n+\treturn rtw_is_8723bs(rtwdev) \u0026\u0026 info \u0026\u0026\n+\t info-\u003esubtype == IEEE80211_STYPE_AUTH;\n+}\n+\n+/* Replicate the vendor start_clnt_join() register programming right before\n+ * auth. Returns true for a fresh join (BSSID changed).\n+ */\n+static bool rtw8723bs_mgd_prepare_join(struct rtw_dev *rtwdev,\n+\t\t\t\t struct ieee80211_vif *vif,\n+\t\t\t\t const u8 *bssid)\n+{\n+\tstruct rtw_vif *rtwvif = (struct rtw_vif *)vif-\u003edrv_priv;\n+\tbool fresh_join;\n+\tu16 retry_limit;\n+\n+\tif (!is_valid_ether_addr(bssid))\n+\t\treturn false;\n+\n+\tfresh_join = !ether_addr_equal(rtwvif-\u003ebssid, bssid);\n+\n+\tether_addr_copy(rtwvif-\u003ebssid, bssid);\n+\trtwvif-\u003eaid = 0;\n+\trtwvif-\u003enet_type = RTW_NET_MGD_LINKED;\n+\trtw_vif_port_config(rtwdev, rtwvif,\n+\t\t\t PORT_SET_BSSID | PORT_SET_AID | PORT_SET_NET_TYPE);\n+\n+\t/* Do not narrow RRSR or switch to short-preamble responses before the\n+\t * exchange: the whole auth/assoc runs on the init response set\n+\t * (0xffff1, long preamble). Only slot time is programmed early.\n+\t */\n+\trtw8723bs_apply_bss_cap(rtwdev, vif, bssid, false);\n+\n+\trtw_fw_beacon_filter_config(rtwdev, false, vif);\n+\n+\t/* Match the vendor start_clnt_join() TX state (set_msr directly, keep\n+\t * BCN_CTRL / BCNQ_DL set, reassert TBTT/RESP_SIFS every join).\n+\t */\n+\trtw_write8(rtwdev, REG_BCN_CTRL,\n+\t\t BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION);\n+\trtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT_EN_BCNQ_DL);\n+\trtw_write8(rtwdev, REG_TBTT_PROHIBIT + 1, 0x64 \u0026 0xff);\n+\trtw_write8(rtwdev, REG_TBTT_PROHIBIT + 2,\n+\t\t (rtw_read8(rtwdev, REG_TBTT_PROHIBIT + 2) \u0026 0xf0) | (0x64 \u003e\u003e 8));\n+\trtw_write16(rtwdev, REG_RESP_SIFS_CCK, 0x0808);\n+\trtw_write16(rtwdev, REG_RESP_SIFS_OFDM, 0x0a0a);\n+\n+\trtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff);\n+\trtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff);\n+\trtw8723bs_auth_rx_filter(rtwdev, true);\n+\n+\tretry_limit = (RTW8723BS_JOIN_RETRY_LIMIT \u003c\u003c 8) |\n+\t\t RTW8723BS_JOIN_RETRY_LIMIT;\n+\trtw_write16(rtwdev, REG_RETRY_LIMIT, retry_limit);\n+\n+\trtw8723bs_config_sec_cfg(rtwdev);\n+\n+\treturn fresh_join;\n+}\n+\n+/* The vendor sends a deauth to the target before auth to clear stale AP-side\n+ * state; synthesize and TX one, then let the AP settle.\n+ */\n+static void rtw8723bs_tx_pre_auth_deauth(struct rtw_dev *rtwdev,\n+\t\t\t\t\t struct ieee80211_vif *vif,\n+\t\t\t\t\t const u8 *bssid)\n+{\n+\tstruct ieee80211_tx_control control = {};\n+\tstruct ieee80211_tx_info *info;\n+\tstruct ieee80211_mgmt *mgmt;\n+\tstruct sk_buff *skb;\n+\tunsigned int frame_len, headroom;\n+\n+\tframe_len = sizeof(struct ieee80211_hdr_3addr) + sizeof(mgmt-\u003eu.deauth);\n+\theadroom = rtwdev-\u003echip-\u003etx_pkt_desc_sz + 8;\n+\n+\tskb = dev_alloc_skb(headroom + frame_len);\n+\tif (!skb)\n+\t\treturn;\n+\n+\tskb_reserve(skb, headroom);\n+\tmgmt = skb_put_zero(skb, frame_len);\n+\tmgmt-\u003eframe_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |\n+\t\t\t\t\t IEEE80211_STYPE_DEAUTH);\n+\tmemcpy(mgmt-\u003eda, bssid, ETH_ALEN);\n+\tmemcpy(mgmt-\u003esa, vif-\u003eaddr, ETH_ALEN);\n+\tmemcpy(mgmt-\u003ebssid, bssid, ETH_ALEN);\n+\tmgmt-\u003eu.deauth.reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);\n+\n+\tinfo = IEEE80211_SKB_CB(skb);\n+\tmemset(info, 0, sizeof(*info));\n+\tinfo-\u003econtrol.vif = vif;\n+\n+\trtw_tx(rtwdev, \u0026control, skb);\n+\tmsleep(100);\n+}\n+\n+/* Orchestrate the pre-auth join: program the vendor join state, send the\n+ * pre-auth deauth, wait for a beacon from the target, then replay the\n+ * pre-auth coex H2Cs - once per fresh BSSID.\n+ */\n+static void rtw8723bs_mgd_prepare_auth_join(struct rtw_dev *rtwdev,\n+\t\t\t\t\t struct ieee80211_vif *vif,\n+\t\t\t\t\t struct ieee80211_prep_tx_info *info)\n+{\n+\tstruct rtw_vif *rtwvif;\n+\tconst u8 *bssid = NULL;\n+\tbool fresh_join;\n+\n+\tif (!rtw8723bs_mgd_prepare_is_auth(rtwdev, info) || !vif ||\n+\t test_bit(RTW_FLAG_SCANNING, rtwdev-\u003eflags))\n+\t\treturn;\n+\n+\trtwvif = (struct rtw_vif *)vif-\u003edrv_priv;\n+\n+\tif (!is_zero_ether_addr(vif-\u003ecfg.ap_addr))\n+\t\tbssid = vif-\u003ecfg.ap_addr;\n+\telse if (vif-\u003ebss_conf.bssid \u0026\u0026 !is_zero_ether_addr(vif-\u003ebss_conf.bssid))\n+\t\tbssid = vif-\u003ebss_conf.bssid;\n+\n+\tif (!bssid)\n+\t\treturn;\n+\n+\tfresh_join = rtw8723bs_mgd_prepare_join(rtwdev, vif, bssid);\n+\n+\tif (fresh_join || !rtwvif-\u003epre_auth_join_done) {\n+\t\tunsigned int wait_ms = rtw8723bs_auth_sync_wait_ms(vif);\n+\n+\t\trtw8723bs_auth_sync_start(rtwdev, bssid);\n+\t\trtw8723bs_tx_pre_auth_deauth(rtwdev, vif, bssid);\n+\t\trtw8723bs_auth_sync_wait(rtwdev, wait_ms);\n+\t\trtw8723bs_auth_sync_stop(rtwdev);\n+\t\trtwvif-\u003epre_auth_join_done = true;\n+\t}\n+\n+\tif (!rtwvif-\u003epre_auth_h2c_sent) {\n+\t\trtw_coex_8723bs_pre_auth_h2c(rtwdev);\n+\t\trtwvif-\u003epre_auth_h2c_sent = true;\n+\t}\n+}\n+\n static void rtw_ops_tx(struct ieee80211_hw *hw,\n \t\t struct ieee80211_tx_control *control,\n \t\t struct sk_buff *skb)\n@@ -387,6 +844,13 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,\n \tif (changed \u0026 BSS_CHANGED_ASSOC) {\n \t\trtw_vif_assoc_changed(rtwvif, conf);\n \t\tif (vif-\u003ecfg.assoc) {\n+\t\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026\n+\t\t\t vif-\u003etype == NL80211_IFTYPE_STATION) {\n+\t\t\t\trtw8723bs_auth_rx_filter(rtwdev, false);\n+\t\t\t\trtw8723bs_apply_basic_rates(rtwdev, vif, NULL);\n+\t\t\t\trtw8723bs_enable_tsf_update(rtwdev);\n+\t\t\t}\n+\n \t\t\trtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_FINISH);\n \n \t\t\trtw_fw_download_rsvd_page(rtwdev);\n@@ -408,6 +872,13 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,\n \t\t\tif (test_bit(RTW_FLAG_SCANNING, rtwdev-\u003eflags))\n \t\t\t\trtw_hw_scan_abort(rtwdev);\n \n+\t\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026\n+\t\t\t vif-\u003etype == NL80211_IFTYPE_STATION) {\n+\t\t\t\trtw8723bs_auth_rx_filter(rtwdev, false);\n+\t\t\t\trtw8723bs_reset_response_rates(rtwdev);\n+\t\t\t\trtwvif-\u003epre_auth_h2c_sent = false;\n+\t\t\t\trtwvif-\u003epre_auth_join_done = false;\n+\t\t\t}\n \t\t}\n \n \t\tconfig |= PORT_SET_NET_TYPE;\n@@ -415,8 +886,26 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,\n \t}\n \n \tif (changed \u0026 BSS_CHANGED_BSSID) {\n+\t\tbool bssid_cleared = is_zero_ether_addr(conf-\u003ebssid);\n+\t\tbool bssid_changed = !ether_addr_equal(rtwvif-\u003ebssid,\n+\t\t\t\t\t\t conf-\u003ebssid);\n+\n+\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026\n+\t\t vif-\u003etype == NL80211_IFTYPE_STATION \u0026\u0026 bssid_changed) {\n+\t\t\trtwvif-\u003epre_auth_h2c_sent = false;\n+\t\t\trtwvif-\u003epre_auth_join_done = false;\n+\t\t}\n \t\tether_addr_copy(rtwvif-\u003ebssid, conf-\u003ebssid);\n \t\tconfig |= PORT_SET_BSSID;\n+\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026\n+\t\t vif-\u003etype == NL80211_IFTYPE_STATION \u0026\u0026 bssid_cleared) {\n+\t\t\trtwvif-\u003eaid = 0;\n+\t\t\trtwvif-\u003enet_type = RTW_NET_NO_LINK;\n+\t\t\tconfig |= PORT_SET_NET_TYPE | PORT_SET_AID;\n+\t\t\trtw_write8(rtwdev, REG_BCN_CTRL,\n+\t\t\t\t BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION |\n+\t\t\t\t BIT_DIS_ATIM);\n+\t\t}\n \t\tif (!rtw_core_check_sta_active(rtwdev))\n \t\t\trtw_clear_op_chan(rtwdev);\n \t\telse\n@@ -448,8 +937,17 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,\n \tif (changed \u0026 BSS_CHANGED_MU_GROUPS)\n \t\trtw_chip_set_gid_table(rtwdev, vif, conf);\n \n-\tif (changed \u0026 BSS_CHANGED_ERP_SLOT)\n+\tif (changed \u0026 BSS_CHANGED_ERP_PREAMBLE \u0026\u0026\n+\t rtw_is_8723bs(rtwdev) \u0026\u0026\n+\t vif-\u003etype == NL80211_IFTYPE_STATION)\n+\t\trtw8723bs_set_ack_preamble(rtwdev, conf-\u003euse_short_preamble);\n+\n+\tif (changed \u0026 BSS_CHANGED_ERP_SLOT) {\n+\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026\n+\t\t vif-\u003etype == NL80211_IFTYPE_STATION)\n+\t\t\trtw8723bs_set_slot_time(rtwdev, conf-\u003euse_short_slot);\n \t\trtw_conf_tx(rtwdev, rtwvif);\n+\t}\n \n \tif (changed \u0026 BSS_CHANGED_PS)\n \t\trtw_recalc_lps(rtwdev, NULL);\n@@ -609,11 +1107,19 @@ static int rtw_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,\n \t\tkey-\u003ehw_key_idx = hw_key_idx;\n \t\trtw_sec_write_cam(rtwdev, sec, sta, key,\n \t\t\t\t hw_key_type, hw_key_idx);\n+\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026 vif \u0026\u0026\n+\t\t vif-\u003etype == NL80211_IFTYPE_STATION \u0026\u0026\n+\t\t !(key-\u003eflags \u0026 IEEE80211_KEY_FLAG_PAIRWISE))\n+\t\t\trtw8723bs_config_default_key_search(rtwdev, true);\n \t\tbreak;\n \tcase DISABLE_KEY:\n \t\trtw_hci_flush_all_queues(rtwdev, false);\n \t\trtw_mac_flush_all_queues(rtwdev, false);\n \t\trtw_sec_clear_cam(rtwdev, sec, key-\u003ehw_key_idx);\n+\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026 vif \u0026\u0026\n+\t\t vif-\u003etype == NL80211_IFTYPE_STATION \u0026\u0026\n+\t\t !(key-\u003eflags \u0026 IEEE80211_KEY_FLAG_PAIRWISE))\n+\t\t\trtw8723bs_config_default_key_search(rtwdev, false);\n \t\tbreak;\n \t}\n \n@@ -703,8 +1209,23 @@ static void rtw_ops_mgd_prepare_tx(struct ieee80211_hw *hw,\n \n \tmutex_lock(\u0026rtwdev-\u003emutex);\n \trtw_leave_lps_deep(rtwdev);\n-\trtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_START);\n-\trtw_chip_prepare_tx(rtwdev);\n+\n+\tif (rtw_is_8723bs(rtwdev)) {\n+\t\t/* Wake from soft IPS and run the vendor join sequence. The RFK\n+\t\t * is handled by the once-only power-on IQK plus the ps.c\n+\t\t * post-IPS RF-bus recovery, not a fresh calibration here.\n+\t\t */\n+\t\tif (rtw_leave_ips(rtwdev)) {\n+\t\t\trtw_err(rtwdev, \"failed to leave idle state for mgd tx\\n\");\n+\t\t\tgoto out;\n+\t\t}\n+\t\trtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_START);\n+\t\trtw8723bs_mgd_prepare_auth_join(rtwdev, vif, info);\n+\t} else {\n+\t\trtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_START);\n+\t\trtw_chip_prepare_tx(rtwdev);\n+\t}\n+out:\n \tmutex_unlock(\u0026rtwdev-\u003emutex);\n }\n \ndiff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c\nindex cd9254370fcc72..16228cae252ebd 100644\n--- a/drivers/net/wireless/realtek/rtw88/main.c\n+++ b/drivers/net/wireless/realtek/rtw88/main.c\n@@ -2174,11 +2174,13 @@ int rtw_core_init(struct rtw_dev *rtwdev)\n \n \tspin_lock_init(\u0026rtwdev-\u003etxq_lock);\n \tspin_lock_init(\u0026rtwdev-\u003etx_report.q_lock);\n+\tspin_lock_init(\u0026rtwdev-\u003eauth_sync.lock);\n \n \tmutex_init(\u0026rtwdev-\u003emutex);\n \tmutex_init(\u0026rtwdev-\u003ehal.tx_power_mutex);\n \n \tinit_waitqueue_head(\u0026rtwdev-\u003ecoex.wait);\n+\tinit_waitqueue_head(\u0026rtwdev-\u003eauth_sync.wait);\n \tinit_completion(\u0026rtwdev-\u003elps_leave_check);\n \tinit_completion(\u0026rtwdev-\u003efw_scan_density);\n \ndiff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h\nindex 9c0b746540b0ee..99b99aa3d600f8 100644\n--- a/drivers/net/wireless/realtek/rtw88/main.h\n+++ b/drivers/net/wireless/realtek/rtw88/main.h\n@@ -194,6 +194,7 @@ enum rtw_chip_type {\n \tRTW_CHIP_TYPE_8723D,\n \tRTW_CHIP_TYPE_8821C,\n \tRTW_CHIP_TYPE_8703B,\n+\tRTW_CHIP_TYPE_8723B,\n \tRTW_CHIP_TYPE_8821A,\n \tRTW_CHIP_TYPE_8812A,\n \tRTW_CHIP_TYPE_8814A,\n@@ -828,6 +829,8 @@ struct rtw_vif {\n \tu8 bssid[ETH_ALEN];\n \tu8 port;\n \tu8 bcn_ctrl;\n+\tbool pre_auth_h2c_sent;\n+\tbool pre_auth_join_done;\n \tstruct list_head rsvd_page_list;\n \tstruct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS];\n \tconst struct rtw_vif_port *conf;\n@@ -2056,6 +2059,20 @@ struct rtw_hw_scan_info {\n \tu8 op_bw;\n };\n \n+/*\n+ * Synchronises the pre-auth wait on a beacon or probe response from the\n+ * target BSSID before the join sequence continues.\n+ */\n+struct rtw_auth_sync {\n+\twait_queue_head_t wait;\n+\t/* Protects the fields below. */\n+\tspinlock_t lock;\n+\tu8 bssid[ETH_ALEN];\n+\tbool active;\n+\tbool seen;\n+\tu32 seen_count;\n+};\n+\n struct rtw_dev {\n \tstruct ieee80211_hw *hw;\n \tstruct device *dev;\n@@ -2132,6 +2149,8 @@ struct rtw_dev {\n \tstruct completion fw_scan_density;\n \tbool ap_active;\n \n+\tstruct rtw_auth_sync auth_sync;\n+\n \tbool led_registered;\n \tchar led_name[32];\n \tstruct led_classdev led_cdev;\n@@ -2193,6 +2212,12 @@ static inline bool rtw_chip_has_tx_stbc(struct rtw_dev *rtwdev)\n \treturn rtwdev-\u003echip-\u003etx_stbc;\n }\n \n+static inline bool rtw_is_8723bs(struct rtw_dev *rtwdev)\n+{\n+\treturn rtwdev-\u003echip-\u003eid == RTW_CHIP_TYPE_8723B \u0026\u0026\n+\t rtwdev-\u003ehci.type == RTW_HCI_TYPE_SDIO;\n+}\n+\n static inline u8 rtw_acquire_macid(struct rtw_dev *rtwdev)\n {\n \tunsigned long mac_id;\n@@ -2283,4 +2308,8 @@ bool rtw_core_check_sta_active(struct rtw_dev *rtwdev);\n void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable);\n void rtw_set_ampdu_factor(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,\n \t\t\t struct ieee80211_bss_conf *bss_conf);\n+void rtw8723bs_auth_sync_rx(struct rtw_dev *rtwdev,\n+\t\t\t const struct ieee80211_hdr *hdr, u32 len,\n+\t\t\t const struct rtw_rx_pkt_stat *pkt_stat,\n+\t\t\t const struct ieee80211_rx_status *rx_status);\n #endif\ndiff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h\nindex 08e9494977e06a..b6d4e8ed98c755 100644\n--- a/drivers/net/wireless/realtek/rtw88/reg.h\n+++ b/drivers/net/wireless/realtek/rtw88/reg.h\n@@ -479,6 +479,7 @@\n #define BIT_DIS_TSF_UDT\t\tBIT(4)\n #define BIT_EN_BCN_FUNCTION\tBIT(3)\n #define BIT_EN_TXBCN_RPT\tBIT(2)\n+#define BIT_DIS_ATIM\t\tBIT(0)\n #define REG_BCN_CTRL_CLINT0\t0x0551\n #define REG_DRVERLYINT\t\t0x0558\n #define REG_BCNDMATIM\t\t0x0559\n@@ -518,6 +519,7 @@\n #define BIT_UC_MD_EN\t\tBIT(16)\n #define BIT_RXSK_PERPKT\t\tBIT(15)\n #define BIT_HTC_LOC_CTRL\tBIT(14)\n+#define BIT_AMF\t\t\tBIT(13)\n #define BIT_RPFM_CAM_ENABLE\tBIT(12)\n #define BIT_TA_BCN\t\tBIT(11)\n #define BIT_RCR_ADF\t\tBIT(11)\n@@ -628,6 +630,7 @@\n #define REG_PSD\t\t\t0x0910\n #define BIT_PSD_INI\t\tGENMASK(23, 22)\n #define REG_SINGLE_TONE_CONT_TX\t0x0914\n+#define REG_BB_SEL_BTG_8723B\t0x0948\n #define REG_AGC_TABLE\t\t0x0958\n #define REG_RFE_CTRL_E\t\t0x0974\n #define REG_2ND_CCA_CTRL\t0x0976\ndiff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c\nindex 01fd299abb7fe9..5303975d00a2a7 100644\n--- a/drivers/net/wireless/realtek/rtw88/rx.c\n+++ b/drivers/net/wireless/realtek/rtw88/rx.c\n@@ -253,10 +253,12 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,\n \n \trtw_rx_addr_match(rtwdev, pkt_stat, hdr);\n \n-\t/* Rtl8723cs driver checks for size \u003c 14 or size \u003e 8192 and\n-\t * simply drops the packet.\n+\t/* Rtl8723cs and rtl8723bs drivers check for size \u003c 14 or size \u003e 8192\n+\t * and simply drop the packet.\n \t */\n-\tif (rtwdev-\u003echip-\u003eid == RTW_CHIP_TYPE_8703B \u0026\u0026 pkt_stat-\u003epkt_len == 0) {\n+\tif ((rtwdev-\u003echip-\u003eid == RTW_CHIP_TYPE_8703B ||\n+\t rtw_is_8723bs(rtwdev)) \u0026\u0026\n+\t pkt_stat-\u003epkt_len == 0) {\n \t\trx_status-\u003eflag |= RX_FLAG_NO_PSDU;\n \t\trtw_dbg(rtwdev, RTW_DBG_RX, \"zero length packet\");\n \t}\ndiff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c\nindex 5b40d74b16ee08..b294bcc4d82807 100644\n--- a/drivers/net/wireless/realtek/rtw88/sdio.c\n+++ b/drivers/net/wireless/realtek/rtw88/sdio.c\n@@ -20,6 +20,16 @@\n #include \"tx.h\"\n \n #define RTW_SDIO_INDIRECT_RW_RETRIES\t\t\t50\n+#define RTW_SDIO_OQT_TIMEOUT_MS\t\t\t\t1000\n+\n+/* 8723BS SDIO TX FIFO back-pressure watermarks: stop the mac80211 queue once\n+ * the per-AC software FIFO fills past the high watermark, and wake it from the\n+ * TX drain path once it falls back to the low one. Bounds the queueing latency\n+ * that otherwise causes uplink bufferbloat / congestion collapse.\n+ */\n+#define RTW_SDIO_TX_FIFO_HIWATER\t\t\t16\n+#define RTW_SDIO_TX_FIFO_LOWATER\t\t\t8\n+#define RTW_SDIO_TX_RETRY_DELAY\t\t\tmsecs_to_jiffies(1)\n \n static bool rtw_sdio_is_bus_addr(u32 addr)\n {\n@@ -548,12 +558,91 @@ static int rtw_sdio_read_port(struct rtw_dev *rtwdev, u8 *buf, size_t count)\n \treturn ret;\n }\n \n+static void rtw_sdio_init_free_txpg(struct rtw_dev *rtwdev)\n+{\n+\tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n+\tconst struct rtw_page_table *pg_tbl;\n+\tu32 free_txpg;\n+\tu16 pubq_num;\n+\n+\tif (!rtw_is_8723bs(rtwdev))\n+\t\treturn;\n+\n+\tpg_tbl = \u0026rtwdev-\u003echip-\u003epage_table[0];\n+\tpubq_num = rtwdev-\u003efifo.acq_pg_num - pg_tbl-\u003ehq_num - pg_tbl-\u003elq_num -\n+\t\t pg_tbl-\u003enq_num - pg_tbl-\u003eexq_num - pg_tbl-\u003egapq_num;\n+\tfree_txpg = rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);\n+\tif (free_txpg) {\n+\t\tatomic_set(\u0026rtwsdio-\u003efree_pg_high, free_txpg \u0026 0xff);\n+\t\tatomic_set(\u0026rtwsdio-\u003efree_pg_normal, (free_txpg \u003e\u003e 8) \u0026 0xff);\n+\t\tatomic_set(\u0026rtwsdio-\u003efree_pg_low, (free_txpg \u003e\u003e 16) \u0026 0xff);\n+\t\tatomic_set(\u0026rtwsdio-\u003efree_pg_pub, (free_txpg \u003e\u003e 24) \u0026 0xff);\n+\t} else {\n+\t\tatomic_set(\u0026rtwsdio-\u003efree_pg_high, pg_tbl-\u003ehq_num);\n+\t\tatomic_set(\u0026rtwsdio-\u003efree_pg_normal, pg_tbl-\u003enq_num);\n+\t\tatomic_set(\u0026rtwsdio-\u003efree_pg_low, pg_tbl-\u003elq_num);\n+\t\tatomic_set(\u0026rtwsdio-\u003efree_pg_pub, pubq_num);\n+\t}\n+\n+\tatomic_set(\u0026rtwsdio-\u003etx_oqt_free,\n+\t\t rtw_read8(rtwdev, REG_SDIO_OQT_FREE_PG));\n+}\n+\n+static void rtw_sdio_sync_free_txpg(struct rtw_dev *rtwdev)\n+{\n+\tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n+\tu32 free_txpg = rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);\n+\n+\tif (!free_txpg)\n+\t\treturn;\n+\n+\tatomic_set(\u0026rtwsdio-\u003efree_pg_high, free_txpg \u0026 0xff);\n+\tatomic_set(\u0026rtwsdio-\u003efree_pg_normal, (free_txpg \u003e\u003e 8) \u0026 0xff);\n+\tatomic_set(\u0026rtwsdio-\u003efree_pg_low, (free_txpg \u003e\u003e 16) \u0026 0xff);\n+\tatomic_set(\u0026rtwsdio-\u003efree_pg_pub, (free_txpg \u003e\u003e 24) \u0026 0xff);\n+}\n+\n+static int rtw_sdio_8723bs_free_txpg(struct rtw_dev *rtwdev, u8 queue)\n+{\n+\tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n+\n+\tswitch (queue) {\n+\tcase RTW_TX_QUEUE_BCN:\n+\tcase RTW_TX_QUEUE_H2C:\n+\tcase RTW_TX_QUEUE_HI0:\n+\tcase RTW_TX_QUEUE_MGMT:\n+\tcase RTW_TX_QUEUE_VO:\n+\t\treturn atomic_read(\u0026rtwsdio-\u003efree_pg_high);\n+\tcase RTW_TX_QUEUE_VI:\n+\t\treturn atomic_read(\u0026rtwsdio-\u003efree_pg_normal);\n+\tcase RTW_TX_QUEUE_BE:\n+\tcase RTW_TX_QUEUE_BK:\n+\t\treturn atomic_read(\u0026rtwsdio-\u003efree_pg_low);\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+}\n+\n static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue,\n \t\t\t\t size_t count)\n {\n \tunsigned int pages_free, pages_needed;\n \n-\tif (rtw_chip_wcpu_8051(rtwdev)) {\n+\tif (rtw_is_8723bs(rtwdev)) {\n+\t\tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n+\t\tint dedicated = rtw_sdio_8723bs_free_txpg(rtwdev, queue);\n+\n+\t\tif (dedicated \u003c 0)\n+\t\t\treturn dedicated;\n+\t\tpages_free = dedicated + atomic_read(\u0026rtwsdio-\u003efree_pg_pub);\n+\t\tpages_needed = DIV_ROUND_UP(count, rtwdev-\u003echip-\u003epage_size);\n+\t\tif (pages_needed \u003c= pages_free)\n+\t\t\treturn 0;\n+\n+\t\trtw_sdio_sync_free_txpg(rtwdev);\n+\t\tdedicated = rtw_sdio_8723bs_free_txpg(rtwdev, queue);\n+\t\tpages_free = dedicated + atomic_read(\u0026rtwsdio-\u003efree_pg_pub);\n+\t} else if (rtw_chip_wcpu_8051(rtwdev)) {\n \t\tu32 free_txpg;\n \n \t\tfree_txpg = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);\n@@ -632,44 +721,131 @@ static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue,\n \treturn 0;\n }\n \n+static int rtw_sdio_wait_tx_oqt(struct rtw_dev *rtwdev)\n+{\n+\tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n+\tint i;\n+\tu8 free;\n+\n+\tif (!rtw_is_8723bs(rtwdev))\n+\t\treturn 0;\n+\n+\tif (atomic_add_unless(\u0026rtwsdio-\u003etx_oqt_free, -1, 0))\n+\t\treturn 0;\n+\n+\tfor (i = 0; i \u003c RTW_SDIO_OQT_TIMEOUT_MS; i++) {\n+\t\tfree = rtw_read8(rtwdev, REG_SDIO_OQT_FREE_PG);\n+\t\tif (free) {\n+\t\t\tatomic_set(\u0026rtwsdio-\u003etx_oqt_free, free - 1);\n+\t\t\treturn 0;\n+\t\t}\n+\t\tusleep_range(1000, 2000);\n+\t}\n+\n+\treturn -EBUSY;\n+}\n+\n+static void rtw_sdio_8723bs_consume_txpg(struct rtw_dev *rtwdev, u8 queue,\n+\t\t\t\t\t unsigned int pages)\n+{\n+\tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n+\tatomic_t *dedicated;\n+\tint free;\n+\n+\tswitch (queue) {\n+\tcase RTW_TX_QUEUE_VI:\n+\t\tdedicated = \u0026rtwsdio-\u003efree_pg_normal;\n+\t\tbreak;\n+\tcase RTW_TX_QUEUE_BE:\n+\tcase RTW_TX_QUEUE_BK:\n+\t\tdedicated = \u0026rtwsdio-\u003efree_pg_low;\n+\t\tbreak;\n+\tdefault:\n+\t\tdedicated = \u0026rtwsdio-\u003efree_pg_high;\n+\t\tbreak;\n+\t}\n+\n+\tfree = atomic_read(dedicated);\n+\tif (pages \u003c= free) {\n+\t\tatomic_sub(pages, dedicated);\n+\t} else {\n+\t\tatomic_set(dedicated, 0);\n+\t\tatomic_sub(pages - free, \u0026rtwsdio-\u003efree_pg_pub);\n+\t}\n+}\n+\n static int rtw_sdio_write_port(struct rtw_dev *rtwdev, struct sk_buff *skb,\n \t\t\t enum rtw_tx_queue_type queue)\n {\n \tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n+\tunsigned int orig_len = skb-\u003elen;\n+\tbool rtl8723bs = rtw_is_8723bs(rtwdev);\n+\tunsigned int pages;\n \tbool bus_claim;\n \tsize_t txsize;\n+\tsize_t write_size;\n \tu32 txaddr;\n \tint ret;\n \n-\ttxaddr = rtw_sdio_get_tx_addr(rtwdev, skb-\u003elen, queue);\n-\tif (!txaddr)\n-\t\treturn -EINVAL;\n+\tif (rtl8723bs) {\n+\t\ttxsize = round_up(orig_len, 4);\n+\t\twrite_size = txsize \u003e RTW_SDIO_BLOCK_SIZE ?\n+\t\t\t round_up(txsize, RTW_SDIO_BLOCK_SIZE) : txsize;\n+\t} else {\n+\t\ttxsize = sdio_align_size(rtwsdio-\u003esdio_func, orig_len);\n+\t\twrite_size = txsize;\n+\t}\n+\n+\tif (write_size \u003e orig_len) {\n+\t\tunsigned int padding = write_size - orig_len;\n \n-\ttxsize = sdio_align_size(rtwsdio-\u003esdio_func, skb-\u003elen);\n+\t\tif (skb_tailroom(skb) \u003c padding) {\n+\t\t\tret = pskb_expand_head(skb, 0,\n+\t\t\t\t\t padding - skb_tailroom(skb),\n+\t\t\t\t\t GFP_KERNEL);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t\tskb_put_zero(skb, padding);\n+\t}\n+\n+\ttxaddr = rtw_sdio_get_tx_addr(rtwdev, txsize, queue);\n+\tif (!txaddr) {\n+\t\tret = -EINVAL;\n+\t\tgoto out_trim;\n+\t}\n \n \tret = rtw_sdio_check_free_txpg(rtwdev, queue, txsize);\n \tif (ret)\n-\t\treturn ret;\n+\t\tgoto out_trim;\n+\tret = rtw_sdio_wait_tx_oqt(rtwdev);\n+\tif (ret)\n+\t\tgoto out_trim;\n \n \tif (!IS_ALIGNED((unsigned long)skb-\u003edata, RTW_SDIO_DATA_PTR_ALIGN))\n \t\trtw_warn(rtwdev, \"Got unaligned SKB in %s() for queue %u\\n\",\n \t\t\t __func__, queue);\n \n \tbus_claim = rtw_sdio_bus_claim_needed(rtwsdio);\n-\n \tif (bus_claim)\n \t\tsdio_claim_host(rtwsdio-\u003esdio_func);\n-\n-\tret = sdio_memcpy_toio(rtwsdio-\u003esdio_func, txaddr, skb-\u003edata, txsize);\n-\n+\tret = sdio_memcpy_toio(rtwsdio-\u003esdio_func, txaddr, skb-\u003edata,\n+\t\t\t write_size);\n \tif (bus_claim)\n \t\tsdio_release_host(rtwsdio-\u003esdio_func);\n \n+\tif (!ret \u0026\u0026 rtl8723bs) {\n+\t\tpages = DIV_ROUND_UP(txsize, rtwdev-\u003echip-\u003epage_size);\n+\t\trtw_sdio_8723bs_consume_txpg(rtwdev, queue, pages);\n+\t}\n \tif (ret)\n \t\trtw_warn(rtwdev,\n \t\t\t \"Failed to write %zu byte(s) to SDIO port 0x%08x\",\n-\t\t\t txsize, txaddr);\n+\t\t\t write_size, txaddr);\n \n+out_trim:\n+\tif (write_size \u003e orig_len)\n+\t\tskb_trim(skb, orig_len);\n \treturn ret;\n }\n \n@@ -677,7 +853,11 @@ static void rtw_sdio_init(struct rtw_dev *rtwdev)\n {\n \tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n \n-\trtwsdio-\u003eirq_mask = REG_SDIO_HIMR_RX_REQUEST | REG_SDIO_HIMR_CPWM1;\n+\tif (rtw_is_8723bs(rtwdev))\n+\t\trtwsdio-\u003eirq_mask = REG_SDIO_HIMR_RX_REQUEST;\n+\telse\n+\t\trtwsdio-\u003eirq_mask = REG_SDIO_HIMR_RX_REQUEST |\n+\t\t\t\t REG_SDIO_HIMR_CPWM1;\n }\n \n static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)\n@@ -685,6 +865,7 @@ static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)\n \tu8 size, timeout;\n \n \tswitch (rtwdev-\u003echip-\u003eid) {\n+\tcase RTW_CHIP_TYPE_8723B:\n \tcase RTW_CHIP_TYPE_8703B:\n \tcase RTW_CHIP_TYPE_8821A:\n \tcase RTW_CHIP_TYPE_8812A:\n@@ -712,6 +893,8 @@ static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)\n \t\t FIELD_PREP(BIT_DMA_AGG_TO_V1, timeout));\n \n \trtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);\n+\tif (rtw_is_8723bs(rtwdev))\n+\t\trtw_write8_set(rtwdev, REG_RXDMA_MODE, 3 \u003c\u003c 2);\n }\n \n static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev)\n@@ -749,9 +932,50 @@ static int rtw_sdio_setup(struct rtw_dev *rtwdev)\n \treturn 0;\n }\n \n+static void rtw_sdio_8723bs_check_rqpn(struct rtw_dev *rtwdev)\n+{\n+\tconst struct rtw_chip_info *chip = rtwdev-\u003echip;\n+\tstruct rtw_fifo_conf *fifo = \u0026rtwdev-\u003efifo;\n+\tconst struct rtw_page_table *pg_tbl;\n+\tu32 free_txpg;\n+\tu16 pubq_num;\n+\n+\tif (!rtw_is_8723bs(rtwdev))\n+\t\treturn;\n+\n+\tfree_txpg = rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);\n+\tif (free_txpg || !fifo-\u003eacq_pg_num)\n+\t\treturn;\n+\n+\tpg_tbl = \u0026chip-\u003epage_table[0];\n+\tif (fifo-\u003eacq_pg_num \u003c= pg_tbl-\u003ehq_num + pg_tbl-\u003elq_num +\n+\t\t\t\t pg_tbl-\u003enq_num + pg_tbl-\u003eexq_num +\n+\t\t\t\t pg_tbl-\u003egapq_num)\n+\t\treturn;\n+\n+\tpubq_num = fifo-\u003eacq_pg_num - pg_tbl-\u003ehq_num - pg_tbl-\u003elq_num -\n+\t\t pg_tbl-\u003enq_num - pg_tbl-\u003eexq_num - pg_tbl-\u003egapq_num;\n+\trtw_write32(rtwdev, REG_RQPN_NPQ,\n+\t\t BIT_RQPN_NE(pg_tbl-\u003enq_num, pg_tbl-\u003eexq_num));\n+\trtw_write32(rtwdev, REG_RQPN,\n+\t\t BIT_RQPN_HLP(pg_tbl-\u003ehq_num, pg_tbl-\u003elq_num, pubq_num));\n+}\n+\n static int rtw_sdio_start(struct rtw_dev *rtwdev)\n {\n+\tu32 clear;\n+\n+\trtw_sdio_8723bs_check_rqpn(rtwdev);\n+\trtw_sdio_init_free_txpg(rtwdev);\n \trtw_sdio_enable_rx_aggregation(rtwdev);\n+\n+\tif (rtw_is_8723bs(rtwdev)) {\n+\t\tclear = rtw_read32(rtwdev, REG_SDIO_HISR) \u0026\n+\t\t\tRTW_SDIO_HISR_CLEAR_MASK;\n+\t\tif (clear)\n+\t\t\trtw_write32(rtwdev, REG_SDIO_HISR, clear);\n+\t}\n+\n \trtw_sdio_enable_interrupt(rtwdev);\n \n \treturn 0;\n@@ -815,7 +1039,7 @@ static void rtw_sdio_tx_kick_off(struct rtw_dev *rtwdev)\n {\n \tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n \n-\tqueue_work(rtwsdio-\u003etxwq, \u0026rtwsdio-\u003etx_handler_data-\u003ework);\n+\tmod_delayed_work(rtwsdio-\u003etxwq, \u0026rtwsdio-\u003etx_handler_data-\u003ework, 0);\n }\n \n static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter)\n@@ -831,6 +1055,8 @@ static void rtw_sdio_interface_cfg(struct rtw_dev *rtwdev)\n \n \tval = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);\n \tval \u0026= 0xfff8;\n+\tif (rtw_is_8723bs(rtwdev))\n+\t\tval |= BIT_SDIO_TX_CTRL_ALWAYS_RECOGNIZE;\n \trtw_write32(rtwdev, REG_SDIO_TX_CTRL, val);\n }\n \n@@ -937,6 +1163,18 @@ static int rtw_sdio_tx_write(struct rtw_dev *rtwdev,\n \n \tskb_queue_tail(\u0026rtwsdio-\u003etx_queue[queue], skb);\n \n+\t/* Back-pressure on the data ACs (BK/BE/VI/VO): once the FIFO fills past\n+\t * the high watermark, stop the corresponding mac80211 queue so it stops\n+\t * handing us frames, bounding the queueing latency. Resumed from the TX\n+\t * drain path once the FIFO drains below the low watermark.\n+\t */\n+\tif (rtw_is_8723bs(rtwdev) \u0026\u0026 queue \u003c RTW_TX_QUEUE_BCN \u0026\u0026\n+\t !rtwsdio-\u003equeue_stopped[queue] \u0026\u0026\n+\t skb_queue_len(\u0026rtwsdio-\u003etx_queue[queue]) \u003e= RTW_SDIO_TX_FIFO_HIWATER) {\n+\t\trtwsdio-\u003equeue_stopped[queue] = true;\n+\t\tieee80211_stop_queue(rtwdev-\u003ehw, skb_get_queue_mapping(skb));\n+\t}\n+\n \treturn 0;\n }\n \n@@ -965,6 +1203,11 @@ static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb,\n \trtw_update_rx_freq_for_invalid(rtwdev, skb, rx_status, pkt_stat);\n \trtw_rx_stats(rtwdev, pkt_stat-\u003evif, skb);\n \n+\tif (skb-\u003elen \u003e= sizeof(struct ieee80211_hdr_3addr))\n+\t\trtw8723bs_auth_sync_rx(rtwdev,\n+\t\t\t\t (struct ieee80211_hdr *)skb-\u003edata,\n+\t\t\t\t skb-\u003elen, pkt_stat, rx_status);\n+\n \tieee80211_rx_irqsafe(rtwdev-\u003ehw, skb);\n }\n \n@@ -1095,6 +1338,13 @@ static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)\n \t\trtw_sdio_rx_isr(rtwdev);\n \t}\n \n+\t/* RTL8723BS keeps raising the interrupt after resume if undefined\n+\t * status bits are written back, so acknowledge only the bits that are\n+\t * both defined and unmasked. Other chips keep the existing behaviour.\n+\t */\n+\tif (rtw_is_8723bs(rtwdev))\n+\t\thisr \u0026= rtwsdio-\u003eirq_mask \u0026 RTW_SDIO_HISR_CLEAR_MASK;\n+\n \trtw_write32(rtwdev, REG_SDIO_HISR, hisr);\n \n \trtwsdio-\u003eirq_thread = NULL;\n@@ -1231,32 +1481,48 @@ static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev,\n \tieee80211_tx_status_irqsafe(hw, skb);\n }\n \n-static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,\n-\t\t\t\t enum rtw_tx_queue_type queue)\n+static int rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,\n+\t\t\t\t enum rtw_tx_queue_type queue,\n+\t\t\t\t bool *processed)\n {\n \tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n \tstruct sk_buff *skb;\n+\tu16 q_map;\n \tint ret;\n \n+\t*processed = false;\n \tskb = skb_dequeue(\u0026rtwsdio-\u003etx_queue[queue]);\n \tif (!skb)\n-\t\treturn;\n+\t\treturn 0;\n \n+\t*processed = true;\n+\tq_map = skb_get_queue_mapping(skb);\n \tret = rtw_sdio_write_port(rtwdev, skb, queue);\n \tif (ret) {\n \t\tskb_queue_head(\u0026rtwsdio-\u003etx_queue[queue], skb);\n-\t\treturn;\n+\t\treturn ret;\n \t}\n \n \trtw_sdio_indicate_tx_status(rtwdev, skb);\n+\n+\tif (rtw_is_8723bs(rtwdev) \u0026\u0026 queue \u003c RTW_TX_QUEUE_BCN \u0026\u0026\n+\t rtwsdio-\u003equeue_stopped[queue] \u0026\u0026\n+\t skb_queue_len(\u0026rtwsdio-\u003etx_queue[queue]) \u003c= RTW_SDIO_TX_FIFO_LOWATER) {\n+\t\trtwsdio-\u003equeue_stopped[queue] = false;\n+\t\tieee80211_wake_queue(rtwdev-\u003ehw, q_map);\n+\t}\n+\n+\treturn 0;\n }\n \n static void rtw_sdio_tx_handler(struct work_struct *work)\n {\n \tstruct rtw_sdio_work_data *work_data =\n-\t\tcontainer_of(work, struct rtw_sdio_work_data, work);\n+\t\tcontainer_of(to_delayed_work(work), struct rtw_sdio_work_data,\n+\t\t\t work);\n \tstruct rtw_sdio *rtwsdio;\n \tstruct rtw_dev *rtwdev;\n+\tbool processed;\n \tint limit, queue;\n \n \trtwdev = work_data-\u003ertwdev;\n@@ -1267,7 +1533,24 @@ static void rtw_sdio_tx_handler(struct work_struct *work)\n \n \tfor (queue = RTK_MAX_TX_QUEUE_NUM - 1; queue \u003e= 0; queue--) {\n \t\tfor (limit = 0; limit \u003c 1000; limit++) {\n-\t\t\trtw_sdio_process_tx_queue(rtwdev, queue);\n+\t\t\tint ret;\n+\n+\t\t\tret = rtw_sdio_process_tx_queue(rtwdev, queue, \u0026processed);\n+\t\t\tif (ret) {\n+\t\t\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026 ret == -EBUSY) {\n+\t\t\t\t\tmod_delayed_work(rtwsdio-\u003etxwq,\n+\t\t\t\t\t\t\t \u0026work_data-\u003ework,\n+\t\t\t\t\t\t\t RTW_SDIO_TX_RETRY_DELAY);\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tif (rtw_is_8723bs(rtwdev) \u0026\u0026\n+\t\t\t queue == RTW_TX_QUEUE_MGMT \u0026\u0026 processed) {\n+\t\t\t\tmod_delayed_work(rtwsdio-\u003etxwq, \u0026work_data-\u003ework, 0);\n+\t\t\t\treturn;\n+\t\t\t}\n \n \t\t\tif (skb_queue_empty(\u0026rtwsdio-\u003etx_queue[queue]))\n \t\t\t\tbreak;\n@@ -1294,14 +1577,16 @@ static int rtw_sdio_init_tx(struct rtw_dev *rtwdev)\n \t\treturn -ENOMEM;\n \t}\n \n-\tfor (i = 0; i \u003c RTK_MAX_TX_QUEUE_NUM; i++)\n+\tfor (i = 0; i \u003c RTK_MAX_TX_QUEUE_NUM; i++) {\n \t\tskb_queue_head_init(\u0026rtwsdio-\u003etx_queue[i]);\n+\t\trtwsdio-\u003equeue_stopped[i] = false;\n+\t}\n \trtwsdio-\u003etx_handler_data = kmalloc_obj(*rtwsdio-\u003etx_handler_data);\n \tif (!rtwsdio-\u003etx_handler_data)\n \t\tgoto err_destroy_wq;\n \n \trtwsdio-\u003etx_handler_data-\u003ertwdev = rtwdev;\n-\tINIT_WORK(\u0026rtwsdio-\u003etx_handler_data-\u003ework, rtw_sdio_tx_handler);\n+\tINIT_DELAYED_WORK(\u0026rtwsdio-\u003etx_handler_data-\u003ework, rtw_sdio_tx_handler);\n \n \treturn 0;\n \n@@ -1315,6 +1600,7 @@ static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev)\n \tstruct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev-\u003epriv;\n \tint i;\n \n+\tcancel_delayed_work_sync(\u0026rtwsdio-\u003etx_handler_data-\u003ework);\n \tdestroy_workqueue(rtwsdio-\u003etxwq);\n \tkfree(rtwsdio-\u003etx_handler_data);\n \ndiff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wireless/realtek/rtw88/sdio.h\nindex 457e8b02380e5b..f1f59f8301b8f9 100644\n--- a/drivers/net/wireless/realtek/rtw88/sdio.h\n+++ b/drivers/net/wireless/realtek/rtw88/sdio.h\n@@ -22,6 +22,7 @@\n \n /* SDIO Tx Control */\n #define REG_SDIO_TX_CTRL\t\t\t(SDIO_LOCAL_OFFSET + 0x0000)\n+#define BIT_SDIO_TX_CTRL_ALWAYS_RECOGNIZE\tBIT(4)\n \n /*SDIO status timeout*/\n #define REG_SDIO_TIMEOUT\t\t\t(SDIO_LOCAL_OFFSET + 0x0002)\n@@ -77,6 +78,14 @@\n /* the following two are RTL8188 SDIO Specific */\n #define REG_SDIO_HISR_MCU_ERR\t\t\tBIT(28)\n #define REG_SDIO_HISR_TSF_BIT32_TOGGLE\t\tBIT(29)\n+#define RTW_SDIO_HISR_CLEAR_MASK\t\t\\\n+\t(REG_SDIO_HISR_TXERR | REG_SDIO_HISR_RXERR | \\\n+\t REG_SDIO_HISR_TXFOVW | REG_SDIO_HISR_RXFOVW | \\\n+\t REG_SDIO_HISR_TXBCNOK | REG_SDIO_HISR_TXBCNERR | \\\n+\t REG_SDIO_HISR_C2HCMD | REG_SDIO_HISR_CPWM1 | \\\n+\t REG_SDIO_HISR_CPWM2 | REG_SDIO_HISR_HSISR_IND | \\\n+\t REG_SDIO_HISR_GTINT3_IND | REG_SDIO_HISR_GTINT4_IND | \\\n+\t REG_SDIO_HISR_PSTIMEOUT | REG_SDIO_HISR_OCPINT)\n \n /* HCI Current Power Mode */\n #define REG_SDIO_HCPWM\t\t\t\t(SDIO_LOCAL_OFFSET + 0x0019)\n@@ -143,7 +152,7 @@ struct rtw_sdio_tx_data {\n };\n \n struct rtw_sdio_work_data {\n-\tstruct work_struct work;\n+\tstruct delayed_work work;\n \tstruct rtw_dev *rtwdev;\n };\n \n@@ -159,6 +168,13 @@ struct rtw_sdio {\n \tstruct workqueue_struct *txwq;\n \tstruct rtw_sdio_work_data *tx_handler_data;\n \tstruct sk_buff_head tx_queue[RTK_MAX_TX_QUEUE_NUM];\n+\tbool queue_stopped[RTK_MAX_TX_QUEUE_NUM];\n+\n+\tatomic_t free_pg_high;\n+\tatomic_t free_pg_normal;\n+\tatomic_t free_pg_low;\n+\tatomic_t free_pg_pub;\n+\tatomic_t tx_oqt_free;\n };\n \n extern const struct dev_pm_ops rtw_sdio_pm_ops;\ndiff --git a/drivers/net/wireless/realtek/rtw88/sec.h b/drivers/net/wireless/realtek/rtw88/sec.h\nindex efcf45433999af..73f2af66effe85 100644\n--- a/drivers/net/wireless/realtek/rtw88/sec.h\n+++ b/drivers/net/wireless/realtek/rtw88/sec.h\n@@ -22,6 +22,7 @@\n #define RTW_SEC_RX_DEC_EN\t\tBIT(3)\n #define RTW_SEC_TX_BC_USE_DK\t\tBIT(6)\n #define RTW_SEC_RX_BC_USE_DK\t\tBIT(7)\n+#define RTW_SEC_CHK_KEYID\t\tBIT(8)\n \n #define RTW_SEC_ENGINE_EN\t\tBIT(9)\n \n"
} |
| 2/1 | 2026/07/26 17:32 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch series adds support for the RTL8723BS SDIO WiFi/Bluetooth coexistence and modifies SDIO-specific TX/RX handling. All functional changes are either confined to the SDIO interface (sdio.c) or explicitly guarded by `rtw_is_8723bs(rtwdev)`, which checks for `RTW_HCI_TYPE_SDIO`. Since standard virtualized environments (like QEMU) used by syzkaller do not emulate physical SDIO WiFi cards, this driver will not probe and the modified code paths are impossible to reach.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit e089b7d59ab9d93ea51a7142f2e491e0c0cf516a
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Jul 26 17:32:44 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c
index 37c336def41915..314b2a368a8f17 100644
--- a/drivers/net/wireless/realtek/rtw88/coex.c
+++ b/drivers/net/wireless/realtek/rtw88/coex.c
@@ -1443,6 +1443,191 @@ static void rtw_coex_set_ant_path(struct rtw_dev *rtwdev, bool force, u8 phase)
#define case_ALGO(src) \
case COEX_ALGO_##src: return #src
+/* 8723BS SDIO WiFi/BT coexistence antenna handling. On BT-disabled boards the
+ * scan/auth window still routes through the PTA mux; these helpers force the
+ * vendor-shaped WiFi-owned antenna path so directed management TX reaches air.
+ */
+#define REG_8723BS_BT_COEX_CTRL 0x0039
+#define REG_8723BS_BB_ANT_CFG 0x0930
+#define REG_8723BS_BB_ANT_CFG1 0x0944
+#define REG_8723BS_BB_ANT_BUF 0x0974
+#define RTW8723BS_COEX_H_WLAN_ACTIVE 0x1800101b
+
+static bool rtw_coex_8723bs_ant_is_aux(struct rtw_dev *rtwdev)
+{
+ return !!(rtwdev->efuse.bt_setting & BIT(6));
+}
+
+static bool rtw_coex_8723bs_bt_disabled(struct rtw_dev *rtwdev)
+{
+ return rtw_is_8723bs(rtwdev) && rtwdev->coex.stat.bt_disabled;
+}
+
+static u32 rtw_coex_8723bs_pta_ant_path(struct rtw_dev *rtwdev)
+{
+ return rtw_coex_8723bs_ant_is_aux(rtwdev) ? 0x80 : 0x200;
+}
+
+/* Write BB_SEL_BTG, retrying once with SYS_FUNC BB reset if the first
+ * write does not stick (RF/BB clock may have been gated).
+ */
+static u32 rtw_coex_8723bs_write_bb_sel_btg(struct rtw_dev *rtwdev, u32 value)
+{
+ u8 sys_func_before;
+ u32 readback;
+
+ sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+ if ((sys_func_before & (BIT(0) | BIT(1))) != (BIT(0) | BIT(1))) {
+ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT(0) | BIT(1));
+ usleep_range(10, 11);
+ }
+
+ rtw_write32(rtwdev, REG_BB_SEL_BTG_8723B, value);
+ readback = rtw_read32(rtwdev, REG_BB_SEL_BTG_8723B);
+ if (readback == value)
+ return readback;
+
+ usleep_range(10, 11);
+ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT(0) | BIT(1));
+ rtw_write32(rtwdev, REG_BB_SEL_BTG_8723B, value);
+
+ return rtw_read32(rtwdev, REG_BB_SEL_BTG_8723B);
+}
+
+static u32 rtw_coex_8723bs_reassert_pta_ant(struct rtw_dev *rtwdev)
+{
+ return rtw_coex_8723bs_write_bb_sel_btg(rtwdev,
+ rtw_coex_8723bs_pta_ant_path(rtwdev));
+}
+
+static void rtw_coex_8723bs_set_cck_pri(struct rtw_dev *rtwdev, bool high)
+{
+ if (!rtw_coex_8723bs_bt_disabled(rtwdev))
+ return;
+
+ if (high) {
+ rtw_write32(rtwdev, REG_BT_COEX_TABLE_H,
+ RTW8723BS_COEX_H_WLAN_ACTIVE);
+ } else {
+ rtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_TX_CCK, false);
+ rtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_RX_CCK, false);
+ }
+}
+
+static void rtw_coex_8723bs_restore_pad_ctrl(struct rtw_dev *rtwdev,
+ bool keep_pta_owner)
+{
+ u32 before, after;
+
+ before = rtw_read32(rtwdev, REG_PAD_CTRL1);
+ after = before & ~(BIT_LNAON_WLBT_SEL | BIT_SW_DPDT_SEL_DATA);
+ if (keep_pta_owner)
+ after |= BIT_PAPE_WLBT_SEL;
+ else
+ after &= ~BIT_PAPE_WLBT_SEL;
+ if (after != before)
+ rtw_write32(rtwdev, REG_PAD_CTRL1, after);
+}
+
+static void rtw_coex_8723bs_fw_gnt_bt_low(struct rtw_dev *rtwdev)
+{
+ if (!rtw_coex_8723bs_bt_disabled(rtwdev))
+ return;
+
+ if (rtw_read8(rtwdev, REG_GNT_BT) == 0x00 &&
+ rtw_read8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL) == 0x0c)
+ return;
+
+ rtw_fw_set_gnt_bt(rtwdev, 0);
+}
+
+static void rtw_coex_8723bs_force_assoc_pta_ant(struct rtw_dev *rtwdev)
+{
+ u32 ant_target;
+
+ if (!rtw_coex_8723bs_bt_disabled(rtwdev))
+ return;
+
+ ant_target = rtw_coex_8723bs_pta_ant_path(rtwdev);
+
+ rtw_coex_8723bs_fw_gnt_bt_low(rtwdev);
+ rtw_coex_set_ant_switch(rtwdev, COEX_SWITCH_CTRL_BY_PTA,
+ COEX_SWITCH_TO_NOCARE);
+ rtw_coex_8723bs_set_cck_pri(rtwdev, true);
+ rtw_coex_8723bs_write_bb_sel_btg(rtwdev, ant_target);
+ rtw_coex_8723bs_restore_pad_ctrl(rtwdev, true);
+}
+
+static void rtw_coex_8723bs_reassert_ant_buffer(struct rtw_dev *rtwdev)
+{
+ u8 sys_func_before;
+
+ sys_func_before = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+ if ((sys_func_before & (BIT(0) | BIT(1))) != (BIT(0) | BIT(1))) {
+ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT(0) | BIT(1));
+ usleep_range(10, 11);
+ }
+
+ rtw_write8_mask(rtwdev, REG_8723BS_BT_COEX_CTRL, BIT(3), 0x1);
+ rtw_write8(rtwdev, REG_8723BS_BB_ANT_BUF, 0xff);
+ rtw_write8_mask(rtwdev, REG_8723BS_BB_ANT_CFG1, 0x3, 0x3);
+ rtw_write8(rtwdev, REG_8723BS_BB_ANT_CFG, 0x77);
+}
+
+static void rtw_coex_8723bs_apply_scan_table(struct rtw_dev *rtwdev)
+{
+ rtwdev->coex.dm.cur_table = 2;
+ rtw_coex_set_table(rtwdev, true, 0x5a5a5a5a, 0x5a5a5a5a);
+}
+
+/* Non-connected scan/auth workaround: PS-TDMA type 8 off, PTA antenna path,
+ * coex table type 2 (matches the vendor non-connected arbitration).
+ */
+void rtw_coex_8723bs_scan_workaround(struct rtw_dev *rtwdev)
+{
+ struct rtw_coex_dm *coex_dm = &rtwdev->coex.dm;
+ struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat;
+
+ if (!rtw_is_8723bs(rtwdev))
+ return;
+
+ coex_dm->cur_ps_tdma_on = false;
+ coex_dm->cur_ps_tdma = 8;
+ coex_dm->ps_tdma_para[0] = 0x08;
+ coex_dm->ps_tdma_para[1] = 0x00;
+ coex_dm->ps_tdma_para[2] = 0x00;
+ coex_dm->ps_tdma_para[3] = 0x00;
+ coex_dm->ps_tdma_para[4] = 0x00;
+
+ rtw_fw_coex_tdma_type(rtwdev, 0x08, 0x00, 0x00, 0x00, 0x00);
+ rtw_coex_8723bs_fw_gnt_bt_low(rtwdev);
+ rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_2G);
+ rtw_coex_8723bs_reassert_ant_buffer(rtwdev);
+ rtw_coex_8723bs_apply_scan_table(rtwdev);
+ if (coex_stat->bt_disabled)
+ rtw_coex_8723bs_set_cck_pri(rtwdev, true);
+ rtw_coex_8723bs_reassert_pta_ant(rtwdev);
+ rtw_coex_8723bs_restore_pad_ctrl(rtwdev, true);
+}
+
+/* Replayed immediately before start_clnt_join()/auth: BT_INFO + PS-TDMA type 8
+ * then the forced WiFi PTA antenna path, so the auth window is clean.
+ */
+void rtw_coex_8723bs_pre_auth_h2c(struct rtw_dev *rtwdev)
+{
+ lockdep_assert_held(&rtwdev->mutex);
+
+ if (!rtw_coex_8723bs_bt_disabled(rtwdev))
+ return;
+
+ rtw_fw_query_bt_info(rtwdev);
+ rtw_fw_coex_tdma_type(rtwdev, 0x08, 0x00, 0x00, 0x00, 0x00);
+ rtw_fw_coex_tdma_type(rtwdev, 0x08, 0x00, 0x00, 0x00, 0x00);
+ rtw_fw_coex_tdma_type(rtwdev, 0x08, 0x00, 0x00, 0x00, 0x00);
+ rtw_coex_8723bs_fw_gnt_bt_low(rtwdev);
+ rtw_coex_8723bs_force_assoc_pta_ant(rtwdev);
+}
+
static const char *rtw_coex_get_algo_string(u8 algo)
{
switch (algo) {
@@ -2770,7 +2955,7 @@ void rtw_coex_power_on_setting(struct rtw_dev *rtwdev)
coex->stop_dm = true;
coex->wl_rf_off = false;
- /* enable BB, we can write 0x948 */
+ /* enable BB, so BB_SEL_BTG is writable */
rtw_write8_set(rtwdev, REG_SYS_FUNC_EN,
BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB);
@@ -2877,6 +3062,33 @@ void rtw_coex_scan_notify(struct rtw_dev *rtwdev, u8 type)
coex->freeze = false;
rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true);
+ /* 8723BS SDIO BT-disabled: keep the scan/auth PTA antenna state the
+ * vendor uses and skip the generic coex run. At scan start (firmware
+ * has been up long enough for stable RX DMA) replay the vendor BT_MP /
+ * BT_INFO queries, then re-establish the scan-path PTA setup.
+ */
+ if (rtw_coex_8723bs_bt_disabled(rtwdev)) {
+ if (type == COEX_SCAN_START_2G || type == COEX_SCAN_START) {
+ struct rtw_coex_info_req req = {};
+
+ coex_stat->cnt_wl[COEX_CNT_WL_SCANAP] = 0;
+ coex_stat->wl_hi_pri_task2 = true;
+
+ req.seq = 0x0e;
+ req.op_code = BT_MP_INFO_OP_SUPP_VER;
+ rtw_fw_query_bt_mp_info(rtwdev, &req);
+ req.seq = 0x0f;
+ req.op_code = BT_MP_INFO_OP_PATCH_VER;
+ rtw_fw_query_bt_mp_info(rtwdev, &req);
+ rtw_fw_query_bt_info(rtwdev);
+
+ rtw_coex_8723bs_scan_workaround(rtwdev);
+ } else {
+ coex_stat->wl_hi_pri_task2 = false;
+ }
+ return;
+ }
+
if (type == COEX_SCAN_START_5G) {
rtw_dbg(rtwdev, RTW_DBG_COEX,
"[BTCoex], SCAN START notify (5G)\n");
@@ -2938,6 +3150,17 @@ void rtw_coex_connect_notify(struct rtw_dev *rtwdev, u8 type)
if (coex->manual_control || coex->stop_dm)
return;
+ /* 8723BS SDIO BT-disabled: the vendor ConnectNotify() early-returns
+ * without sending H2Cs; scan_workaround already established the PTA
+ * path / coex table / PS-TDMA. Keep only the register-level PTA
+ * reassertion at associate-start.
+ */
+ if (rtw_coex_8723bs_bt_disabled(rtwdev)) {
+ if (type == COEX_ASSOCIATE_START)
+ rtw_coex_8723bs_force_assoc_pta_ant(rtwdev);
+ return;
+ }
+
rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true);
if (type == COEX_ASSOCIATE_5G_START) {
diff --git a/drivers/net/wireless/realtek/rtw88/coex.h b/drivers/net/wireless/realtek/rtw88/coex.h
index c398be8391f7b0..c58ae0a50b30b1 100644
--- a/drivers/net/wireless/realtek/rtw88/coex.h
+++ b/drivers/net/wireless/realtek/rtw88/coex.h
@@ -430,4 +430,7 @@ static inline void rtw_coex_active_query_bt_info(struct rtw_dev *rtwdev)
rtw_coex_query_bt_info(rtwdev);
}
+void rtw_coex_8723bs_scan_workaround(struct rtw_dev *rtwdev);
+void rtw_coex_8723bs_pre_auth_h2c(struct rtw_dev *rtwdev);
+
#endif
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
index 945fedcd375b62..7c17086cac32c4 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.c
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -786,6 +786,17 @@ void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect)
rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}
+/* 8723BS SDIO: set the firmware GNT_BT state (0 = WiFi owns the antenna). */
+void rtw_fw_set_gnt_bt(struct rtw_dev *rtwdev, u8 state)
+{
+ u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+
+ SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_GNT_BT);
+ SET_GNT_BT_STATE(h2c_pkt, state);
+
+ rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
+}
+
void rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev)
{
struct rtw_traffic_stats *stats = &rtwdev->stats;
@@ -1466,10 +1477,14 @@ void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev,
rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_INFO, true);
}
+/* REG_DWBCN1_CTRL bit 20 (SW_BCN_SEL for port 0), relative to byte +2 */
+#define BIT_OFFSET_DWBCN1_SW_BCN_SEL_PORT0 (20 - 16)
+
int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
u8 *buf, u32 size)
{
- u8 bckp[3];
+ const bool is_8723bs_sdio = rtw_is_8723bs(rtwdev);
+ u8 bckp[4];
u8 val;
u16 rsvd_pg_head;
u32 bcn_valid_addr;
@@ -1499,13 +1514,28 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
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) {
+ /* Clear BIT_EN_BCNQ_DL so the chip does not treat the reserved-page
+ * upload as a real beacon; otherwise BIT_BCN_VALID never asserts. The
+ * vendor rtl8723bs driver does this unconditionally; rtw88 only did it
+ * for PCIe, which left 8723BS SDIO's BCN_VALID handshake failing.
+ */
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE || is_8723bs_sdio) {
val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2);
bckp[1] = val;
val &= ~(BIT_EN_BCNQ_DL >> 16);
rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, val);
}
+ /* 8723BS SDIO: point the SW beacon download path at port 0, else
+ * BIT_BCN_VALID is never asserted after the SDIO upload completes.
+ */
+ if (is_8723bs_sdio) {
+ val = rtw_read8(rtwdev, REG_DWBCN1_CTRL + 2);
+ bckp[3] = val;
+ val &= ~BIT(BIT_OFFSET_DWBCN1_SW_BCN_SEL_PORT0);
+ rtw_write8(rtwdev, REG_DWBCN1_CTRL + 2, val);
+ }
+
ret = rtw_hci_write_data_rsvd_page(rtwdev, buf, size);
if (ret) {
rtw_err(rtwdev, "failed to write data to rsvd page\n");
@@ -1526,11 +1556,13 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
}
restore:
+ if (is_8723bs_sdio)
+ rtw_write8(rtwdev, REG_DWBCN1_CTRL + 2, bckp[3]);
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)
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE || is_8723bs_sdio)
rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]);
rtw_write8(rtwdev, REG_CR + 1, bckp[0]);
diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h
index 48ad9ceab6ea12..f5ed65c46350f4 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.h
+++ b/drivers/net/wireless/realtek/rtw88/fw.h
@@ -572,6 +572,7 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
#define H2C_CMD_QUERY_BT_MP_INFO 0x67
#define H2C_CMD_BT_WIFI_CONTROL 0x69
#define H2C_CMD_WIFI_CALIBRATION 0x6d
+#define H2C_CMD_GNT_BT 0x6e
#define H2C_CMD_QUERY_BT_HID_INFO 0x73
#define H2C_CMD_KEEP_ALIVE 0x03
@@ -684,6 +685,8 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(31, 24))
#define SET_QUERY_BT_INFO(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))
+#define SET_GNT_BT_STATE(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))
#define SET_WL_CH_INFO_LINK(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8))
#define SET_WL_CH_INFO_CHNL(h2c_pkt, value) \
@@ -849,6 +852,7 @@ void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si);
void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
bool reset_ra_mask);
void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool conn);
+void rtw_fw_set_gnt_bt(struct rtw_dev *rtwdev, u8 state);
void rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev);
void rtw_fw_beacon_filter_config(struct rtw_dev *rtwdev, bool connect,
struct ieee80211_vif *vif);
diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c
index 766f22d31079ee..91014a8e242b32 100644
--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
@@ -15,6 +15,463 @@
#include "wow.h"
#include "sar.h"
+/* 8723BS SDIO: record a beacon/probe-resp seen from the target BSSID during
+ * the pre-auth window so the join sequence (mgd_prepare_tx) can wait for it,
+ * mirroring the vendor start_clnt_join(). Called from the SDIO RX path.
+ */
+void rtw8723bs_auth_sync_rx(struct rtw_dev *rtwdev,
+ const struct ieee80211_hdr *hdr, u32 len,
+ const struct rtw_rx_pkt_stat *pkt_stat,
+ const struct ieee80211_rx_status *rx_status)
+{
+ struct rtw_auth_sync *sync = &rtwdev->auth_sync;
+ unsigned long flags;
+ __le16 fc = hdr->frame_control;
+
+ if (!rtw_is_8723bs(rtwdev) ||
+ test_bit(RTW_FLAG_SCANNING, rtwdev->flags) ||
+ pkt_stat->crc_err || pkt_stat->icv_err)
+ return;
+
+ if (!ieee80211_is_beacon(fc) && !ieee80211_is_probe_resp(fc))
+ return;
+
+ spin_lock_irqsave(&sync->lock, flags);
+ if (sync->active && ether_addr_equal(hdr->addr3, sync->bssid)) {
+ sync->seen = true;
+ sync->seen_count++;
+ wake_up(&sync->wait);
+ }
+ spin_unlock_irqrestore(&sync->lock, flags);
+}
+EXPORT_SYMBOL(rtw8723bs_auth_sync_rx);
+
+/* ---- 8723BS SDIO association sequence (vendor start_clnt_join) ---- */
+
+#define RTW8723BS_JOIN_RETRY_LIMIT 0x30
+#define RTW8723BS_AUTH_SYNC_WAIT_FALLBACK_MS 120
+#define RTW8723BS_AUTH_SYNC_WAIT_MIN_MS 80
+#define RTW8723BS_AUTH_SYNC_WAIT_MAX_MS 160
+#define RTW8723BS_ACK_PREAMBLE_SHORT BIT(7)
+#define RTW8723BS_SHORT_SLOT_TIME 9
+#define RTW8723BS_LONG_SLOT_TIME 20
+#define RTW8723BS_RRSR_1M BIT(0)
+#define RTW8723BS_RRSR_2M BIT(1)
+#define RTW8723BS_RRSR_5_5M BIT(2)
+#define RTW8723BS_RRSR_11M BIT(3)
+#define RTW8723BS_RRSR_6M BIT(4)
+#define RTW8723BS_RRSR_9M BIT(5)
+#define RTW8723BS_RRSR_12M BIT(6)
+#define RTW8723BS_RRSR_18M BIT(7)
+#define RTW8723BS_RRSR_24M BIT(8)
+#define RTW8723BS_RRSR_36M BIT(9)
+#define RTW8723BS_RRSR_48M BIT(10)
+#define RTW8723BS_RRSR_54M BIT(11)
+#define RTW8723BS_RRSR_CCK_RATES \
+ (RTW8723BS_RRSR_1M | RTW8723BS_RRSR_2M | \
+ RTW8723BS_RRSR_5_5M | RTW8723BS_RRSR_11M)
+#define RTW8723BS_RRSR_2G_FORCE RTW8723BS_RRSR_CCK_RATES
+#define RTW8723BS_RRSR_2G_ALLOW \
+ (RTW8723BS_RRSR_CCK_RATES | RTW8723BS_RRSR_6M | \
+ RTW8723BS_RRSR_12M | RTW8723BS_RRSR_24M)
+
+/* Keep the RCR at the vendor target-only state (AMF + CBSSID) across the whole
+ * connect window, matching the vendor STA path. accept_all is kept only for the
+ * caller's intent; both paths converge the filter to target-only.
+ */
+static void rtw8723bs_auth_rx_filter(struct rtw_dev *rtwdev, bool accept_all)
+{
+ rtwdev->hal.rcr |= BIT_AMF | BIT_CBSSID_DATA | BIT_CBSSID_BCN;
+ rtwdev->hal.rcr &= ~BIT_AAP;
+ rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
+}
+
+static void rtw8723bs_config_sec_cfg(struct rtw_dev *rtwdev)
+{
+ u16 sec = rtw_read16(rtwdev, RTW_SEC_CONFIG);
+
+ sec |= RTW_SEC_CHK_KEYID | RTW_SEC_TX_DEC_EN | RTW_SEC_RX_DEC_EN;
+ rtw_write16(rtwdev, RTW_SEC_CONFIG, sec);
+}
+
+static void rtw8723bs_config_default_key_search(struct rtw_dev *rtwdev,
+ bool enable)
+{
+ u16 sec = rtw_read16(rtwdev, RTW_SEC_CONFIG);
+
+ if (enable)
+ sec |= RTW_SEC_TX_BC_USE_DK | RTW_SEC_TX_UNI_USE_DK |
+ RTW_SEC_RX_UNI_USE_DK;
+ else
+ sec &= ~(RTW_SEC_TX_UNI_USE_DK | RTW_SEC_RX_UNI_USE_DK |
+ RTW_SEC_TX_BC_USE_DK | RTW_SEC_RX_BC_USE_DK);
+ rtw_write16(rtwdev, RTW_SEC_CONFIG, sec);
+}
+
+static void rtw8723bs_enable_tsf_update(struct rtw_dev *rtwdev)
+{
+ rtw_write8_clr(rtwdev, REG_BCN_CTRL, BIT_DIS_TSF_UDT);
+}
+
+static void rtw8723bs_set_ack_preamble(struct rtw_dev *rtwdev,
+ bool short_preamble)
+{
+ u8 val = rtw_read8(rtwdev, REG_RRSR + 2) & ~RTW8723BS_ACK_PREAMBLE_SHORT;
+
+ if (short_preamble)
+ val |= RTW8723BS_ACK_PREAMBLE_SHORT;
+ rtw_write8(rtwdev, REG_RRSR + 2, val);
+}
+
+static void rtw8723bs_set_slot_time(struct rtw_dev *rtwdev, bool short_slot)
+{
+ rtw_write8(rtwdev, REG_SLOT,
+ short_slot ? RTW8723BS_SHORT_SLOT_TIME :
+ RTW8723BS_LONG_SLOT_TIME);
+}
+
+static u16 rtw8723bs_rrsr_from_ie_rate(u8 rate)
+{
+ switch (rate & 0x7f) {
+ case 2: return RTW8723BS_RRSR_1M;
+ case 4: return RTW8723BS_RRSR_2M;
+ case 11: return RTW8723BS_RRSR_5_5M;
+ case 22: return RTW8723BS_RRSR_11M;
+ case 12: return RTW8723BS_RRSR_6M;
+ case 18: return RTW8723BS_RRSR_9M;
+ case 24: return RTW8723BS_RRSR_12M;
+ case 36: return RTW8723BS_RRSR_18M;
+ case 48: return RTW8723BS_RRSR_24M;
+ case 72: return RTW8723BS_RRSR_36M;
+ case 96: return RTW8723BS_RRSR_48M;
+ case 108: return RTW8723BS_RRSR_54M;
+ default: return 0;
+ }
+}
+
+static void rtw8723bs_collect_basic_rates(const u8 *ie, u16 *basic_rates,
+ bool *valid)
+{
+ int i;
+
+ if (!ie)
+ return;
+
+ for (i = 0; i < ie[1]; i++) {
+ u16 r;
+
+ if (!(ie[i + 2] & 0x80))
+ continue;
+ r = rtw8723bs_rrsr_from_ie_rate(ie[i + 2]);
+ if (!r)
+ continue;
+ *basic_rates |= r;
+ *valid = true;
+ }
+}
+
+static void rtw8723bs_reset_response_rates(struct rtw_dev *rtwdev)
+{
+ rtw_write32(rtwdev, REG_RRSR, 0xffff1);
+ rtwdev->dm_info.rrsr_val_init = 0xffff1;
+}
+
+static void rtw8723bs_apply_basic_rates(struct rtw_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ const u8 *bssid)
+{
+ struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ struct cfg80211_bss *lookup_bss = NULL;
+ struct cfg80211_bss *bss = NULL;
+ bool valid = false;
+ u16 basic_rates = 0;
+
+ if (!rtw_is_8723bs(rtwdev) || vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (conf->bss) {
+ bss = conf->bss;
+ } else if (bssid && is_valid_ether_addr(bssid)) {
+ lookup_bss = cfg80211_get_bss(rtwdev->hw->wiphy, NULL,
+ bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY);
+ bss = lookup_bss;
+ }
+
+ if (bss) {
+ const u8 *rates, *ext;
+
+ rcu_read_lock();
+ rates = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ ext = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+ rtw8723bs_collect_basic_rates(rates, &basic_rates, &valid);
+ rtw8723bs_collect_basic_rates(ext, &basic_rates, &valid);
+ rcu_read_unlock();
+ }
+
+ if (valid) {
+ basic_rates |= RTW8723BS_RRSR_2G_FORCE;
+ basic_rates &= RTW8723BS_RRSR_2G_ALLOW;
+ rtw_write16(rtwdev, REG_RRSR, basic_rates);
+ rtw_write8(rtwdev, REG_RRSR + 2,
+ rtw_read8(rtwdev, REG_RRSR + 2) & 0xf0);
+ rtwdev->dm_info.rrsr_val_init = basic_rates;
+ }
+
+ if (lookup_bss)
+ cfg80211_put_bss(rtwdev->hw->wiphy, lookup_bss);
+}
+
+/* Program response slot time (and, when set_preamble, the ACK preamble) from
+ * the selected scan BSS capabilities; the AP capabilities are not yet in
+ * bss_conf at mgd_prepare_tx() time.
+ */
+static void rtw8723bs_apply_bss_cap(struct rtw_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ const u8 *bssid, bool set_preamble)
+{
+ struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ struct cfg80211_bss *lookup_bss = NULL;
+ struct cfg80211_bss *bss = NULL;
+ bool short_preamble, short_slot;
+ u16 cap = 0;
+
+ if (!rtw_is_8723bs(rtwdev) || vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (conf->bss) {
+ bss = conf->bss;
+ } else if (bssid && is_valid_ether_addr(bssid)) {
+ lookup_bss = cfg80211_get_bss(rtwdev->hw->wiphy, NULL,
+ bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY);
+ bss = lookup_bss;
+ }
+
+ if (bss) {
+ cap = bss->capability;
+ } else if (conf->assoc_capability) {
+ cap = conf->assoc_capability;
+ } else {
+ short_preamble = conf->use_short_preamble;
+ short_slot = conf->use_short_slot;
+ goto program;
+ }
+ short_preamble = !!(cap & WLAN_CAPABILITY_SHORT_PREAMBLE);
+ short_slot = !!(cap & WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+program:
+ if (set_preamble)
+ rtw8723bs_set_ack_preamble(rtwdev, short_preamble);
+ rtw8723bs_set_slot_time(rtwdev, short_slot);
+
+ if (lookup_bss)
+ cfg80211_put_bss(rtwdev->hw->wiphy, lookup_bss);
+}
+
+static unsigned int rtw8723bs_auth_sync_wait_ms(struct ieee80211_vif *vif)
+{
+ u16 beacon_int = vif->bss_conf.beacon_int;
+ unsigned int wait_ms;
+
+ if (!beacon_int)
+ return RTW8723BS_AUTH_SYNC_WAIT_FALLBACK_MS;
+
+ wait_ms = DIV_ROUND_UP(beacon_int * 1024, 1000) + 20;
+ return clamp_t(unsigned int, wait_ms, RTW8723BS_AUTH_SYNC_WAIT_MIN_MS,
+ RTW8723BS_AUTH_SYNC_WAIT_MAX_MS);
+}
+
+static void rtw8723bs_auth_sync_start(struct rtw_dev *rtwdev, const u8 *bssid)
+{
+ struct rtw_auth_sync *sync = &rtwdev->auth_sync;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sync->lock, flags);
+ ether_addr_copy(sync->bssid, bssid);
+ sync->seen = false;
+ sync->seen_count = 0;
+ sync->active = true;
+ spin_unlock_irqrestore(&sync->lock, flags);
+}
+
+static void rtw8723bs_auth_sync_stop(struct rtw_dev *rtwdev)
+{
+ struct rtw_auth_sync *sync = &rtwdev->auth_sync;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sync->lock, flags);
+ sync->active = false;
+ spin_unlock_irqrestore(&sync->lock, flags);
+}
+
+static bool rtw8723bs_auth_sync_seen(struct rtw_dev *rtwdev)
+{
+ struct rtw_auth_sync *sync = &rtwdev->auth_sync;
+ unsigned long flags;
+ bool seen;
+
+ spin_lock_irqsave(&sync->lock, flags);
+ seen = sync->seen;
+ spin_unlock_irqrestore(&sync->lock, flags);
+
+ return seen;
+}
+
+static bool rtw8723bs_auth_sync_wait(struct rtw_dev *rtwdev,
+ unsigned int wait_ms)
+{
+ struct rtw_auth_sync *sync = &rtwdev->auth_sync;
+
+ return wait_event_timeout(sync->wait, rtw8723bs_auth_sync_seen(rtwdev),
+ msecs_to_jiffies(wait_ms)) > 0;
+}
+
+static bool rtw8723bs_mgd_prepare_is_auth(struct rtw_dev *rtwdev,
+ struct ieee80211_prep_tx_info *info)
+{
+ return rtw_is_8723bs(rtwdev) && info &&
+ info->subtype == IEEE80211_STYPE_AUTH;
+}
+
+/* Replicate the vendor start_clnt_join() register programming right before
+ * auth. Returns true for a fresh join (BSSID changed).
+ */
+static bool rtw8723bs_mgd_prepare_join(struct rtw_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ const u8 *bssid)
+{
+ struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
+ bool fresh_join;
+ u16 retry_limit;
+
+ if (!is_valid_ether_addr(bssid))
+ return false;
+
+ fresh_join = !ether_addr_equal(rtwvif->bssid, bssid);
+
+ ether_addr_copy(rtwvif->bssid, bssid);
+ rtwvif->aid = 0;
+ rtwvif->net_type = RTW_NET_MGD_LINKED;
+ rtw_vif_port_config(rtwdev, rtwvif,
+ PORT_SET_BSSID | PORT_SET_AID | PORT_SET_NET_TYPE);
+
+ /* Do not narrow RRSR or switch to short-preamble responses before the
+ * exchange: the whole auth/assoc runs on the init response set
+ * (0xffff1, long preamble). Only slot time is programmed early.
+ */
+ rtw8723bs_apply_bss_cap(rtwdev, vif, bssid, false);
+
+ rtw_fw_beacon_filter_config(rtwdev, false, vif);
+
+ /* Match the vendor start_clnt_join() TX state (set_msr directly, keep
+ * BCN_CTRL / BCNQ_DL set, reassert TBTT/RESP_SIFS every join).
+ */
+ rtw_write8(rtwdev, REG_BCN_CTRL,
+ BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION);
+ rtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT_EN_BCNQ_DL);
+ rtw_write8(rtwdev, REG_TBTT_PROHIBIT + 1, 0x64 & 0xff);
+ rtw_write8(rtwdev, REG_TBTT_PROHIBIT + 2,
+ (rtw_read8(rtwdev, REG_TBTT_PROHIBIT + 2) & 0xf0) | (0x64 >> 8));
+ rtw_write16(rtwdev, REG_RESP_SIFS_CCK, 0x0808);
+ rtw_write16(rtwdev, REG_RESP_SIFS_OFDM, 0x0a0a);
+
+ rtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff);
+ rtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff);
+ rtw8723bs_auth_rx_filter(rtwdev, true);
+
+ retry_limit = (RTW8723BS_JOIN_RETRY_LIMIT << 8) |
+ RTW8723BS_JOIN_RETRY_LIMIT;
+ rtw_write16(rtwdev, REG_RETRY_LIMIT, retry_limit);
+
+ rtw8723bs_config_sec_cfg(rtwdev);
+
+ return fresh_join;
+}
+
+/* The vendor sends a deauth to the target before auth to clear stale AP-side
+ * state; synthesize and TX one, then let the AP settle.
+ */
+static void rtw8723bs_tx_pre_auth_deauth(struct rtw_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ const u8 *bssid)
+{
+ struct ieee80211_tx_control control = {};
+ struct ieee80211_tx_info *info;
+ struct ieee80211_mgmt *mgmt;
+ struct sk_buff *skb;
+ unsigned int frame_len, headroom;
+
+ frame_len = sizeof(struct ieee80211_hdr_3addr) + sizeof(mgmt->u.deauth);
+ headroom = rtwdev->chip->tx_pkt_desc_sz + 8;
+
+ skb = dev_alloc_skb(headroom + frame_len);
+ if (!skb)
+ return;
+
+ skb_reserve(skb, headroom);
+ mgmt = skb_put_zero(skb, frame_len);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_DEAUTH);
+ memcpy(mgmt->da, bssid, ETH_ALEN);
+ memcpy(mgmt->sa, vif->addr, ETH_ALEN);
+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
+ mgmt->u.deauth.reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
+
+ info = IEEE80211_SKB_CB(skb);
+ memset(info, 0, sizeof(*info));
+ info->control.vif = vif;
+
+ rtw_tx(rtwdev, &control, skb);
+ msleep(100);
+}
+
+/* Orchestrate the pre-auth join: program the vendor join state, send the
+ * pre-auth deauth, wait for a beacon from the target, then replay the
+ * pre-auth coex H2Cs - once per fresh BSSID.
+ */
+static void rtw8723bs_mgd_prepare_auth_join(struct rtw_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_prep_tx_info *info)
+{
+ struct rtw_vif *rtwvif;
+ const u8 *bssid = NULL;
+ bool fresh_join;
+
+ if (!rtw8723bs_mgd_prepare_is_auth(rtwdev, info) || !vif ||
+ test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
+ return;
+
+ rtwvif = (struct rtw_vif *)vif->drv_priv;
+
+ if (!is_zero_ether_addr(vif->cfg.ap_addr))
+ bssid = vif->cfg.ap_addr;
+ else if (vif->bss_conf.bssid && !is_zero_ether_addr(vif->bss_conf.bssid))
+ bssid = vif->bss_conf.bssid;
+
+ if (!bssid)
+ return;
+
+ fresh_join = rtw8723bs_mgd_prepare_join(rtwdev, vif, bssid);
+
+ if (fresh_join || !rtwvif->pre_auth_join_done) {
+ unsigned int wait_ms = rtw8723bs_auth_sync_wait_ms(vif);
+
+ rtw8723bs_auth_sync_start(rtwdev, bssid);
+ rtw8723bs_tx_pre_auth_deauth(rtwdev, vif, bssid);
+ rtw8723bs_auth_sync_wait(rtwdev, wait_ms);
+ rtw8723bs_auth_sync_stop(rtwdev);
+ rtwvif->pre_auth_join_done = true;
+ }
+
+ if (!rtwvif->pre_auth_h2c_sent) {
+ rtw_coex_8723bs_pre_auth_h2c(rtwdev);
+ rtwvif->pre_auth_h2c_sent = true;
+ }
+}
+
static void rtw_ops_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
struct sk_buff *skb)
@@ -387,6 +844,13 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ASSOC) {
rtw_vif_assoc_changed(rtwvif, conf);
if (vif->cfg.assoc) {
+ if (rtw_is_8723bs(rtwdev) &&
+ vif->type == NL80211_IFTYPE_STATION) {
+ rtw8723bs_auth_rx_filter(rtwdev, false);
+ rtw8723bs_apply_basic_rates(rtwdev, vif, NULL);
+ rtw8723bs_enable_tsf_update(rtwdev);
+ }
+
rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_FINISH);
rtw_fw_download_rsvd_page(rtwdev);
@@ -408,6 +872,13 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
rtw_hw_scan_abort(rtwdev);
+ if (rtw_is_8723bs(rtwdev) &&
+ vif->type == NL80211_IFTYPE_STATION) {
+ rtw8723bs_auth_rx_filter(rtwdev, false);
+ rtw8723bs_reset_response_rates(rtwdev);
+ rtwvif->pre_auth_h2c_sent = false;
+ rtwvif->pre_auth_join_done = false;
+ }
}
config |= PORT_SET_NET_TYPE;
@@ -415,8 +886,26 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
}
if (changed & BSS_CHANGED_BSSID) {
+ bool bssid_cleared = is_zero_ether_addr(conf->bssid);
+ bool bssid_changed = !ether_addr_equal(rtwvif->bssid,
+ conf->bssid);
+
+ if (rtw_is_8723bs(rtwdev) &&
+ vif->type == NL80211_IFTYPE_STATION && bssid_changed) {
+ rtwvif->pre_auth_h2c_sent = false;
+ rtwvif->pre_auth_join_done = false;
+ }
ether_addr_copy(rtwvif->bssid, conf->bssid);
config |= PORT_SET_BSSID;
+ if (rtw_is_8723bs(rtwdev) &&
+ vif->type == NL80211_IFTYPE_STATION && bssid_cleared) {
+ rtwvif->aid = 0;
+ rtwvif->net_type = RTW_NET_NO_LINK;
+ config |= PORT_SET_NET_TYPE | PORT_SET_AID;
+ rtw_write8(rtwdev, REG_BCN_CTRL,
+ BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION |
+ BIT_DIS_ATIM);
+ }
if (!rtw_core_check_sta_active(rtwdev))
rtw_clear_op_chan(rtwdev);
else
@@ -448,8 +937,17 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_MU_GROUPS)
rtw_chip_set_gid_table(rtwdev, vif, conf);
- if (changed & BSS_CHANGED_ERP_SLOT)
+ if (changed & BSS_CHANGED_ERP_PREAMBLE &&
+ rtw_is_8723bs(rtwdev) &&
+ vif->type == NL80211_IFTYPE_STATION)
+ rtw8723bs_set_ack_preamble(rtwdev, conf->use_short_preamble);
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ if (rtw_is_8723bs(rtwdev) &&
+ vif->type == NL80211_IFTYPE_STATION)
+ rtw8723bs_set_slot_time(rtwdev, conf->use_short_slot);
rtw_conf_tx(rtwdev, rtwvif);
+ }
if (changed & BSS_CHANGED_PS)
rtw_recalc_lps(rtwdev, NULL);
@@ -609,11 +1107,19 @@ static int rtw_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
key->hw_key_idx = hw_key_idx;
rtw_sec_write_cam(rtwdev, sec, sta, key,
hw_key_type, hw_key_idx);
+ if (rtw_is_8723bs(rtwdev) && vif &&
+ vif->type == NL80211_IFTYPE_STATION &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ rtw8723bs_config_default_key_search(rtwdev, true);
break;
case DISABLE_KEY:
rtw_hci_flush_all_queues(rtwdev, false);
rtw_mac_flush_all_queues(rtwdev, false);
rtw_sec_clear_cam(rtwdev, sec, key->hw_key_idx);
+ if (rtw_is_8723bs(rtwdev) && vif &&
+ vif->type == NL80211_IFTYPE_STATION &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ rtw8723bs_config_default_key_search(rtwdev, false);
break;
}
@@ -703,8 +1209,23 @@ static void rtw_ops_mgd_prepare_tx(struct ieee80211_hw *hw,
mutex_lock(&rtwdev->mutex);
rtw_leave_lps_deep(rtwdev);
- rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_START);
- rtw_chip_prepare_tx(rtwdev);
+
+ if (rtw_is_8723bs(rtwdev)) {
+ /* Wake from soft IPS and run the vendor join sequence. The RFK
+ * is handled by the once-only power-on IQK plus the ps.c
+ * post-IPS RF-bus recovery, not a fresh calibration here.
+ */
+ if (rtw_leave_ips(rtwdev)) {
+ rtw_err(rtwdev, "failed to leave idle state for mgd tx\n");
+ goto out;
+ }
+ rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_START);
+ rtw8723bs_mgd_prepare_auth_join(rtwdev, vif, info);
+ } else {
+ rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_START);
+ rtw_chip_prepare_tx(rtwdev);
+ }
+out:
mutex_unlock(&rtwdev->mutex);
}
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index cd9254370fcc72..16228cae252ebd 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -2174,11 +2174,13 @@ int rtw_core_init(struct rtw_dev *rtwdev)
spin_lock_init(&rtwdev->txq_lock);
spin_lock_init(&rtwdev->tx_report.q_lock);
+ spin_lock_init(&rtwdev->auth_sync.lock);
mutex_init(&rtwdev->mutex);
mutex_init(&rtwdev->hal.tx_power_mutex);
init_waitqueue_head(&rtwdev->coex.wait);
+ init_waitqueue_head(&rtwdev->auth_sync.wait);
init_completion(&rtwdev->lps_leave_check);
init_completion(&rtwdev->fw_scan_density);
diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index 9c0b746540b0ee..99b99aa3d600f8 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -194,6 +194,7 @@ enum rtw_chip_type {
RTW_CHIP_TYPE_8723D,
RTW_CHIP_TYPE_8821C,
RTW_CHIP_TYPE_8703B,
+ RTW_CHIP_TYPE_8723B,
RTW_CHIP_TYPE_8821A,
RTW_CHIP_TYPE_8812A,
RTW_CHIP_TYPE_8814A,
@@ -828,6 +829,8 @@ struct rtw_vif {
u8 bssid[ETH_ALEN];
u8 port;
u8 bcn_ctrl;
+ bool pre_auth_h2c_sent;
+ bool pre_auth_join_done;
struct list_head rsvd_page_list;
struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS];
const struct rtw_vif_port *conf;
@@ -2056,6 +2059,20 @@ struct rtw_hw_scan_info {
u8 op_bw;
};
+/*
+ * Synchronises the pre-auth wait on a beacon or probe response from the
+ * target BSSID before the join sequence continues.
+ */
+struct rtw_auth_sync {
+ wait_queue_head_t wait;
+ /* Protects the fields below. */
+ spinlock_t lock;
+ u8 bssid[ETH_ALEN];
+ bool active;
+ bool seen;
+ u32 seen_count;
+};
+
struct rtw_dev {
struct ieee80211_hw *hw;
struct device *dev;
@@ -2132,6 +2149,8 @@ struct rtw_dev {
struct completion fw_scan_density;
bool ap_active;
+ struct rtw_auth_sync auth_sync;
+
bool led_registered;
char led_name[32];
struct led_classdev led_cdev;
@@ -2193,6 +2212,12 @@ static inline bool rtw_chip_has_tx_stbc(struct rtw_dev *rtwdev)
return rtwdev->chip->tx_stbc;
}
+static inline bool rtw_is_8723bs(struct rtw_dev *rtwdev)
+{
+ return rtwdev->chip->id == RTW_CHIP_TYPE_8723B &&
+ rtwdev->hci.type == RTW_HCI_TYPE_SDIO;
+}
+
static inline u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
{
unsigned long mac_id;
@@ -2283,4 +2308,8 @@ bool rtw_core_check_sta_active(struct rtw_dev *rtwdev);
void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable);
void rtw_set_ampdu_factor(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf);
+void rtw8723bs_auth_sync_rx(struct rtw_dev *rtwdev,
+ const struct ieee80211_hdr *hdr, u32 len,
+ const struct rtw_rx_pkt_stat *pkt_stat,
+ const struct ieee80211_rx_status *rx_status);
#endif
diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h
index 08e9494977e06a..b6d4e8ed98c755 100644
--- a/drivers/net/wireless/realtek/rtw88/reg.h
+++ b/drivers/net/wireless/realtek/rtw88/reg.h
@@ -479,6 +479,7 @@
#define BIT_DIS_TSF_UDT BIT(4)
#define BIT_EN_BCN_FUNCTION BIT(3)
#define BIT_EN_TXBCN_RPT BIT(2)
+#define BIT_DIS_ATIM BIT(0)
#define REG_BCN_CTRL_CLINT0 0x0551
#define REG_DRVERLYINT 0x0558
#define REG_BCNDMATIM 0x0559
@@ -518,6 +519,7 @@
#define BIT_UC_MD_EN BIT(16)
#define BIT_RXSK_PERPKT BIT(15)
#define BIT_HTC_LOC_CTRL BIT(14)
+#define BIT_AMF BIT(13)
#define BIT_RPFM_CAM_ENABLE BIT(12)
#define BIT_TA_BCN BIT(11)
#define BIT_RCR_ADF BIT(11)
@@ -628,6 +630,7 @@
#define REG_PSD 0x0910
#define BIT_PSD_INI GENMASK(23, 22)
#define REG_SINGLE_TONE_CONT_TX 0x0914
+#define REG_BB_SEL_BTG_8723B 0x0948
#define REG_AGC_TABLE 0x0958
#define REG_RFE_CTRL_E 0x0974
#define REG_2ND_CCA_CTRL 0x0976
diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c
index 01fd299abb7fe9..5303975d00a2a7 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.c
+++ b/drivers/net/wireless/realtek/rtw88/rx.c
@@ -253,10 +253,12 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
rtw_rx_addr_match(rtwdev, pkt_stat, hdr);
- /* Rtl8723cs driver checks for size < 14 or size > 8192 and
- * simply drops the packet.
+ /* Rtl8723cs and rtl8723bs drivers check for size < 14 or size > 8192
+ * and simply drop the packet.
*/
- if (rtwdev->chip->id == RTW_CHIP_TYPE_8703B && pkt_stat->pkt_len == 0) {
+ if ((rtwdev->chip->id == RTW_CHIP_TYPE_8703B ||
+ rtw_is_8723bs(rtwdev)) &&
+ pkt_stat->pkt_len == 0) {
rx_status->flag |= RX_FLAG_NO_PSDU;
rtw_dbg(rtwdev, RTW_DBG_RX, "zero length packet");
}
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index 5b40d74b16ee08..b294bcc4d82807 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -20,6 +20,16 @@
#include "tx.h"
#define RTW_SDIO_INDIRECT_RW_RETRIES 50
+#define RTW_SDIO_OQT_TIMEOUT_MS 1000
+
+/* 8723BS SDIO TX FIFO back-pressure watermarks: stop the mac80211 queue once
+ * the per-AC software FIFO fills past the high watermark, and wake it from the
+ * TX drain path once it falls back to the low one. Bounds the queueing latency
+ * that otherwise causes uplink bufferbloat / congestion collapse.
+ */
+#define RTW_SDIO_TX_FIFO_HIWATER 16
+#define RTW_SDIO_TX_FIFO_LOWATER 8
+#define RTW_SDIO_TX_RETRY_DELAY msecs_to_jiffies(1)
static bool rtw_sdio_is_bus_addr(u32 addr)
{
@@ -548,12 +558,91 @@ static int rtw_sdio_read_port(struct rtw_dev *rtwdev, u8 *buf, size_t count)
return ret;
}
+static void rtw_sdio_init_free_txpg(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ const struct rtw_page_table *pg_tbl;
+ u32 free_txpg;
+ u16 pubq_num;
+
+ if (!rtw_is_8723bs(rtwdev))
+ return;
+
+ pg_tbl = &rtwdev->chip->page_table[0];
+ pubq_num = rtwdev->fifo.acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num -
+ pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num;
+ free_txpg = rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
+ if (free_txpg) {
+ atomic_set(&rtwsdio->free_pg_high, free_txpg & 0xff);
+ atomic_set(&rtwsdio->free_pg_normal, (free_txpg >> 8) & 0xff);
+ atomic_set(&rtwsdio->free_pg_low, (free_txpg >> 16) & 0xff);
+ atomic_set(&rtwsdio->free_pg_pub, (free_txpg >> 24) & 0xff);
+ } else {
+ atomic_set(&rtwsdio->free_pg_high, pg_tbl->hq_num);
+ atomic_set(&rtwsdio->free_pg_normal, pg_tbl->nq_num);
+ atomic_set(&rtwsdio->free_pg_low, pg_tbl->lq_num);
+ atomic_set(&rtwsdio->free_pg_pub, pubq_num);
+ }
+
+ atomic_set(&rtwsdio->tx_oqt_free,
+ rtw_read8(rtwdev, REG_SDIO_OQT_FREE_PG));
+}
+
+static void rtw_sdio_sync_free_txpg(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ u32 free_txpg = rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
+
+ if (!free_txpg)
+ return;
+
+ atomic_set(&rtwsdio->free_pg_high, free_txpg & 0xff);
+ atomic_set(&rtwsdio->free_pg_normal, (free_txpg >> 8) & 0xff);
+ atomic_set(&rtwsdio->free_pg_low, (free_txpg >> 16) & 0xff);
+ atomic_set(&rtwsdio->free_pg_pub, (free_txpg >> 24) & 0xff);
+}
+
+static int rtw_sdio_8723bs_free_txpg(struct rtw_dev *rtwdev, u8 queue)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+
+ switch (queue) {
+ case RTW_TX_QUEUE_BCN:
+ case RTW_TX_QUEUE_H2C:
+ case RTW_TX_QUEUE_HI0:
+ case RTW_TX_QUEUE_MGMT:
+ case RTW_TX_QUEUE_VO:
+ return atomic_read(&rtwsdio->free_pg_high);
+ case RTW_TX_QUEUE_VI:
+ return atomic_read(&rtwsdio->free_pg_normal);
+ case RTW_TX_QUEUE_BE:
+ case RTW_TX_QUEUE_BK:
+ return atomic_read(&rtwsdio->free_pg_low);
+ default:
+ return -EINVAL;
+ }
+}
+
static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue,
size_t count)
{
unsigned int pages_free, pages_needed;
- if (rtw_chip_wcpu_8051(rtwdev)) {
+ if (rtw_is_8723bs(rtwdev)) {
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ int dedicated = rtw_sdio_8723bs_free_txpg(rtwdev, queue);
+
+ if (dedicated < 0)
+ return dedicated;
+ pages_free = dedicated + atomic_read(&rtwsdio->free_pg_pub);
+ pages_needed = DIV_ROUND_UP(count, rtwdev->chip->page_size);
+ if (pages_needed <= pages_free)
+ return 0;
+
+ rtw_sdio_sync_free_txpg(rtwdev);
+ dedicated = rtw_sdio_8723bs_free_txpg(rtwdev, queue);
+ pages_free = dedicated + atomic_read(&rtwsdio->free_pg_pub);
+ } else if (rtw_chip_wcpu_8051(rtwdev)) {
u32 free_txpg;
free_txpg = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG);
@@ -632,44 +721,131 @@ static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue,
return 0;
}
+static int rtw_sdio_wait_tx_oqt(struct rtw_dev *rtwdev)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ int i;
+ u8 free;
+
+ if (!rtw_is_8723bs(rtwdev))
+ return 0;
+
+ if (atomic_add_unless(&rtwsdio->tx_oqt_free, -1, 0))
+ return 0;
+
+ for (i = 0; i < RTW_SDIO_OQT_TIMEOUT_MS; i++) {
+ free = rtw_read8(rtwdev, REG_SDIO_OQT_FREE_PG);
+ if (free) {
+ atomic_set(&rtwsdio->tx_oqt_free, free - 1);
+ return 0;
+ }
+ usleep_range(1000, 2000);
+ }
+
+ return -EBUSY;
+}
+
+static void rtw_sdio_8723bs_consume_txpg(struct rtw_dev *rtwdev, u8 queue,
+ unsigned int pages)
+{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ atomic_t *dedicated;
+ int free;
+
+ switch (queue) {
+ case RTW_TX_QUEUE_VI:
+ dedicated = &rtwsdio->free_pg_normal;
+ break;
+ case RTW_TX_QUEUE_BE:
+ case RTW_TX_QUEUE_BK:
+ dedicated = &rtwsdio->free_pg_low;
+ break;
+ default:
+ dedicated = &rtwsdio->free_pg_high;
+ break;
+ }
+
+ free = atomic_read(dedicated);
+ if (pages <= free) {
+ atomic_sub(pages, dedicated);
+ } else {
+ atomic_set(dedicated, 0);
+ atomic_sub(pages - free, &rtwsdio->free_pg_pub);
+ }
+}
+
static int rtw_sdio_write_port(struct rtw_dev *rtwdev, struct sk_buff *skb,
enum rtw_tx_queue_type queue)
{
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ unsigned int orig_len = skb->len;
+ bool rtl8723bs = rtw_is_8723bs(rtwdev);
+ unsigned int pages;
bool bus_claim;
size_t txsize;
+ size_t write_size;
u32 txaddr;
int ret;
- txaddr = rtw_sdio_get_tx_addr(rtwdev, skb->len, queue);
- if (!txaddr)
- return -EINVAL;
+ if (rtl8723bs) {
+ txsize = round_up(orig_len, 4);
+ write_size = txsize > RTW_SDIO_BLOCK_SIZE ?
+ round_up(txsize, RTW_SDIO_BLOCK_SIZE) : txsize;
+ } else {
+ txsize = sdio_align_size(rtwsdio->sdio_func, orig_len);
+ write_size = txsize;
+ }
+
+ if (write_size > orig_len) {
+ unsigned int padding = write_size - orig_len;
- txsize = sdio_align_size(rtwsdio->sdio_func, skb->len);
+ if (skb_tailroom(skb) < padding) {
+ ret = pskb_expand_head(skb, 0,
+ padding - skb_tailroom(skb),
+ GFP_KERNEL);
+ if (ret)
+ return ret;
+ }
+ skb_put_zero(skb, padding);
+ }
+
+ txaddr = rtw_sdio_get_tx_addr(rtwdev, txsize, queue);
+ if (!txaddr) {
+ ret = -EINVAL;
+ goto out_trim;
+ }
ret = rtw_sdio_check_free_txpg(rtwdev, queue, txsize);
if (ret)
- return ret;
+ goto out_trim;
+ ret = rtw_sdio_wait_tx_oqt(rtwdev);
+ if (ret)
+ goto out_trim;
if (!IS_ALIGNED((unsigned long)skb->data, RTW_SDIO_DATA_PTR_ALIGN))
rtw_warn(rtwdev, "Got unaligned SKB in %s() for queue %u\n",
__func__, queue);
bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
-
if (bus_claim)
sdio_claim_host(rtwsdio->sdio_func);
-
- ret = sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data, txsize);
-
+ ret = sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data,
+ write_size);
if (bus_claim)
sdio_release_host(rtwsdio->sdio_func);
+ if (!ret && rtl8723bs) {
+ pages = DIV_ROUND_UP(txsize, rtwdev->chip->page_size);
+ rtw_sdio_8723bs_consume_txpg(rtwdev, queue, pages);
+ }
if (ret)
rtw_warn(rtwdev,
"Failed to write %zu byte(s) to SDIO port 0x%08x",
- txsize, txaddr);
+ write_size, txaddr);
+out_trim:
+ if (write_size > orig_len)
+ skb_trim(skb, orig_len);
return ret;
}
@@ -677,7 +853,11 @@ static void rtw_sdio_init(struct rtw_dev *rtwdev)
{
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
- rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST | REG_SDIO_HIMR_CPWM1;
+ if (rtw_is_8723bs(rtwdev))
+ rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST;
+ else
+ rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST |
+ REG_SDIO_HIMR_CPWM1;
}
static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)
@@ -685,6 +865,7 @@ static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)
u8 size, timeout;
switch (rtwdev->chip->id) {
+ case RTW_CHIP_TYPE_8723B:
case RTW_CHIP_TYPE_8703B:
case RTW_CHIP_TYPE_8821A:
case RTW_CHIP_TYPE_8812A:
@@ -712,6 +893,8 @@ static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev)
FIELD_PREP(BIT_DMA_AGG_TO_V1, timeout));
rtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE);
+ if (rtw_is_8723bs(rtwdev))
+ rtw_write8_set(rtwdev, REG_RXDMA_MODE, 3 << 2);
}
static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev)
@@ -749,9 +932,50 @@ static int rtw_sdio_setup(struct rtw_dev *rtwdev)
return 0;
}
+static void rtw_sdio_8723bs_check_rqpn(struct rtw_dev *rtwdev)
+{
+ const struct rtw_chip_info *chip = rtwdev->chip;
+ struct rtw_fifo_conf *fifo = &rtwdev->fifo;
+ const struct rtw_page_table *pg_tbl;
+ u32 free_txpg;
+ u16 pubq_num;
+
+ if (!rtw_is_8723bs(rtwdev))
+ return;
+
+ free_txpg = rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
+ if (free_txpg || !fifo->acq_pg_num)
+ return;
+
+ pg_tbl = &chip->page_table[0];
+ if (fifo->acq_pg_num <= pg_tbl->hq_num + pg_tbl->lq_num +
+ pg_tbl->nq_num + pg_tbl->exq_num +
+ pg_tbl->gapq_num)
+ return;
+
+ pubq_num = fifo->acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num -
+ pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num;
+ rtw_write32(rtwdev, REG_RQPN_NPQ,
+ BIT_RQPN_NE(pg_tbl->nq_num, pg_tbl->exq_num));
+ rtw_write32(rtwdev, REG_RQPN,
+ BIT_RQPN_HLP(pg_tbl->hq_num, pg_tbl->lq_num, pubq_num));
+}
+
static int rtw_sdio_start(struct rtw_dev *rtwdev)
{
+ u32 clear;
+
+ rtw_sdio_8723bs_check_rqpn(rtwdev);
+ rtw_sdio_init_free_txpg(rtwdev);
rtw_sdio_enable_rx_aggregation(rtwdev);
+
+ if (rtw_is_8723bs(rtwdev)) {
+ clear = rtw_read32(rtwdev, REG_SDIO_HISR) &
+ RTW_SDIO_HISR_CLEAR_MASK;
+ if (clear)
+ rtw_write32(rtwdev, REG_SDIO_HISR, clear);
+ }
+
rtw_sdio_enable_interrupt(rtwdev);
return 0;
@@ -815,7 +1039,7 @@ static void rtw_sdio_tx_kick_off(struct rtw_dev *rtwdev)
{
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
- queue_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work);
+ mod_delayed_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work, 0);
}
static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter)
@@ -831,6 +1055,8 @@ static void rtw_sdio_interface_cfg(struct rtw_dev *rtwdev)
val = rtw_read32(rtwdev, REG_SDIO_TX_CTRL);
val &= 0xfff8;
+ if (rtw_is_8723bs(rtwdev))
+ val |= BIT_SDIO_TX_CTRL_ALWAYS_RECOGNIZE;
rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val);
}
@@ -937,6 +1163,18 @@ static int rtw_sdio_tx_write(struct rtw_dev *rtwdev,
skb_queue_tail(&rtwsdio->tx_queue[queue], skb);
+ /* Back-pressure on the data ACs (BK/BE/VI/VO): once the FIFO fills past
+ * the high watermark, stop the corresponding mac80211 queue so it stops
+ * handing us frames, bounding the queueing latency. Resumed from the TX
+ * drain path once the FIFO drains below the low watermark.
+ */
+ if (rtw_is_8723bs(rtwdev) && queue < RTW_TX_QUEUE_BCN &&
+ !rtwsdio->queue_stopped[queue] &&
+ skb_queue_len(&rtwsdio->tx_queue[queue]) >= RTW_SDIO_TX_FIFO_HIWATER) {
+ rtwsdio->queue_stopped[queue] = true;
+ ieee80211_stop_queue(rtwdev->hw, skb_get_queue_mapping(skb));
+ }
+
return 0;
}
@@ -965,6 +1203,11 @@ static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb,
rtw_update_rx_freq_for_invalid(rtwdev, skb, rx_status, pkt_stat);
rtw_rx_stats(rtwdev, pkt_stat->vif, skb);
+ if (skb->len >= sizeof(struct ieee80211_hdr_3addr))
+ rtw8723bs_auth_sync_rx(rtwdev,
+ (struct ieee80211_hdr *)skb->data,
+ skb->len, pkt_stat, rx_status);
+
ieee80211_rx_irqsafe(rtwdev->hw, skb);
}
@@ -1095,6 +1338,13 @@ static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
rtw_sdio_rx_isr(rtwdev);
}
+ /* RTL8723BS keeps raising the interrupt after resume if undefined
+ * status bits are written back, so acknowledge only the bits that are
+ * both defined and unmasked. Other chips keep the existing behaviour.
+ */
+ if (rtw_is_8723bs(rtwdev))
+ hisr &= rtwsdio->irq_mask & RTW_SDIO_HISR_CLEAR_MASK;
+
rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
rtwsdio->irq_thread = NULL;
@@ -1231,32 +1481,48 @@ static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev,
ieee80211_tx_status_irqsafe(hw, skb);
}
-static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
- enum rtw_tx_queue_type queue)
+static int rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev,
+ enum rtw_tx_queue_type queue,
+ bool *processed)
{
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
struct sk_buff *skb;
+ u16 q_map;
int ret;
+ *processed = false;
skb = skb_dequeue(&rtwsdio->tx_queue[queue]);
if (!skb)
- return;
+ return 0;
+ *processed = true;
+ q_map = skb_get_queue_mapping(skb);
ret = rtw_sdio_write_port(rtwdev, skb, queue);
if (ret) {
skb_queue_head(&rtwsdio->tx_queue[queue], skb);
- return;
+ return ret;
}
rtw_sdio_indicate_tx_status(rtwdev, skb);
+
+ if (rtw_is_8723bs(rtwdev) && queue < RTW_TX_QUEUE_BCN &&
+ rtwsdio->queue_stopped[queue] &&
+ skb_queue_len(&rtwsdio->tx_queue[queue]) <= RTW_SDIO_TX_FIFO_LOWATER) {
+ rtwsdio->queue_stopped[queue] = false;
+ ieee80211_wake_queue(rtwdev->hw, q_map);
+ }
+
+ return 0;
}
static void rtw_sdio_tx_handler(struct work_struct *work)
{
struct rtw_sdio_work_data *work_data =
- container_of(work, struct rtw_sdio_work_data, work);
+ container_of(to_delayed_work(work), struct rtw_sdio_work_data,
+ work);
struct rtw_sdio *rtwsdio;
struct rtw_dev *rtwdev;
+ bool processed;
int limit, queue;
rtwdev = work_data->rtwdev;
@@ -1267,7 +1533,24 @@ static void rtw_sdio_tx_handler(struct work_struct *work)
for (queue = RTK_MAX_TX_QUEUE_NUM - 1; queue >= 0; queue--) {
for (limit = 0; limit < 1000; limit++) {
- rtw_sdio_process_tx_queue(rtwdev, queue);
+ int ret;
+
+ ret = rtw_sdio_process_tx_queue(rtwdev, queue, &processed);
+ if (ret) {
+ if (rtw_is_8723bs(rtwdev) && ret == -EBUSY) {
+ mod_delayed_work(rtwsdio->txwq,
+ &work_data->work,
+ RTW_SDIO_TX_RETRY_DELAY);
+ return;
+ }
+ break;
+ }
+
+ if (rtw_is_8723bs(rtwdev) &&
+ queue == RTW_TX_QUEUE_MGMT && processed) {
+ mod_delayed_work(rtwsdio->txwq, &work_data->work, 0);
+ return;
+ }
if (skb_queue_empty(&rtwsdio->tx_queue[queue]))
break;
@@ -1294,14 +1577,16 @@ static int rtw_sdio_init_tx(struct rtw_dev *rtwdev)
return -ENOMEM;
}
- for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++)
+ for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++) {
skb_queue_head_init(&rtwsdio->tx_queue[i]);
+ rtwsdio->queue_stopped[i] = false;
+ }
rtwsdio->tx_handler_data = kmalloc_obj(*rtwsdio->tx_handler_data);
if (!rtwsdio->tx_handler_data)
goto err_destroy_wq;
rtwsdio->tx_handler_data->rtwdev = rtwdev;
- INIT_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler);
+ INIT_DELAYED_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler);
return 0;
@@ -1315,6 +1600,7 @@ static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev)
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
int i;
+ cancel_delayed_work_sync(&rtwsdio->tx_handler_data->work);
destroy_workqueue(rtwsdio->txwq);
kfree(rtwsdio->tx_handler_data);
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wireless/realtek/rtw88/sdio.h
index 457e8b02380e5b..f1f59f8301b8f9 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.h
+++ b/drivers/net/wireless/realtek/rtw88/sdio.h
@@ -22,6 +22,7 @@
/* SDIO Tx Control */
#define REG_SDIO_TX_CTRL (SDIO_LOCAL_OFFSET + 0x0000)
+#define BIT_SDIO_TX_CTRL_ALWAYS_RECOGNIZE BIT(4)
/*SDIO status timeout*/
#define REG_SDIO_TIMEOUT (SDIO_LOCAL_OFFSET + 0x0002)
@@ -77,6 +78,14 @@
/* the following two are RTL8188 SDIO Specific */
#define REG_SDIO_HISR_MCU_ERR BIT(28)
#define REG_SDIO_HISR_TSF_BIT32_TOGGLE BIT(29)
+#define RTW_SDIO_HISR_CLEAR_MASK \
+ (REG_SDIO_HISR_TXERR | REG_SDIO_HISR_RXERR | \
+ REG_SDIO_HISR_TXFOVW | REG_SDIO_HISR_RXFOVW | \
+ REG_SDIO_HISR_TXBCNOK | REG_SDIO_HISR_TXBCNERR | \
+ REG_SDIO_HISR_C2HCMD | REG_SDIO_HISR_CPWM1 | \
+ REG_SDIO_HISR_CPWM2 | REG_SDIO_HISR_HSISR_IND | \
+ REG_SDIO_HISR_GTINT3_IND | REG_SDIO_HISR_GTINT4_IND | \
+ REG_SDIO_HISR_PSTIMEOUT | REG_SDIO_HISR_OCPINT)
/* HCI Current Power Mode */
#define REG_SDIO_HCPWM (SDIO_LOCAL_OFFSET + 0x0019)
@@ -143,7 +152,7 @@ struct rtw_sdio_tx_data {
};
struct rtw_sdio_work_data {
- struct work_struct work;
+ struct delayed_work work;
struct rtw_dev *rtwdev;
};
@@ -159,6 +168,13 @@ struct rtw_sdio {
struct workqueue_struct *txwq;
struct rtw_sdio_work_data *tx_handler_data;
struct sk_buff_head tx_queue[RTK_MAX_TX_QUEUE_NUM];
+ bool queue_stopped[RTK_MAX_TX_QUEUE_NUM];
+
+ atomic_t free_pg_high;
+ atomic_t free_pg_normal;
+ atomic_t free_pg_low;
+ atomic_t free_pg_pub;
+ atomic_t tx_oqt_free;
};
extern const struct dev_pm_ops rtw_sdio_pm_ops;
diff --git a/drivers/net/wireless/realtek/rtw88/sec.h b/drivers/net/wireless/realtek/rtw88/sec.h
index efcf45433999af..73f2af66effe85 100644
--- a/drivers/net/wireless/realtek/rtw88/sec.h
+++ b/drivers/net/wireless/realtek/rtw88/sec.h
@@ -22,6 +22,7 @@
#define RTW_SEC_RX_DEC_EN BIT(3)
#define RTW_SEC_TX_BC_USE_DK BIT(6)
#define RTW_SEC_RX_BC_USE_DK BIT(7)
+#define RTW_SEC_CHK_KEYID BIT(8)
#define RTW_SEC_ENGINE_EN BIT(9)
|
| 3/2 | 2026/07/26 17:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28488 output: 153 thoughts: 2007 Thoughts: **Patch Series Evaluation: Realtek RTL8723BS SDIO Driver** Okay, let's break this down. The task is to determine if this patch series, focusing on the Realtek RTL8723BS SDIO WiFi/BT coexistence, is suitable for fuzzing. My immediate thought is, "SDIO... that's a problem." This patch modifies `drivers/net/wireless/realtek/rtw88/`, specifically touching `coex.c`, `fw.c`, `mac80211.c`, `main.c`, `rx.c`, and, crucially, `sdio.c`. The key here is that it's an SDIO driver, and mentions the 8723BS SDIO hardware. First, I need to confirm if this code even *runs* in a virtualized environment like QEMU. This driver, `rtw88`, is a Realtek wireless driver, and the changes to `sdio.c` immediately suggest an SDIO interface is involved. The issue is that standard QEMU doesn't have a good, generic SDIO emulator that syzkaller can use. Syzkaller does a great job with PCI and USB, via `dummy_hcd` and `raw-gadget`, but the SDIO interface has not been prioritized. Does syzkaller fuzz SDIO? I need to clarify that. No, SDIO isn't directly supported. I need to consider whether it's somehow reachable via `mac80211`. The driver *registers* with mac80211, but to get to the driver-specific code, you need the actual hardware to be present and probed. If no hardware, no probe, no driver callbacks. Can there be any emulation for SDIO devices? Let's check. Standard QEMU, as syzkaller uses it, doesn't have a way to emulate SDIO. Syzkaller can emulate USB (via dummy_hcd/raw-gadget) and PCI (via some basic PCI fuzzing or just probing if QEMU has the device). But SDIO is not supported. Let's do a kernel search for `rtw88` to see if there are other interfaces. The patch also modifies `drivers/net/wireless/realtek/rtw88/sdio.c`. Okay, let me confirm if `rtw88` has PCI or USB interfaces. Yes, it does support PCI, USB, and SDIO. The new code heavily relies on `rtw_is_8723bs(rtwdev)`. This checks the `rtwdev->chip->id` and `rtwdev->hci.type` to be `RTW_CHIP_TYPE_8723B` and `RTW_HCI_TYPE_SDIO`. In short, this is SDIO-specific code. So, since `rtw_is_8723bs` demands `RTW_HCI_TYPE_SDIO`, the specific changes are only relevant if an SDIO device is actually *probed*. Can we do that in syzkaller? Syzkaller doesn't have SDIO emulation. I need to be explicit: the code guarded by `rtw_is_8723bs` *is unreachable* in the standard syzkaller environment. But, are there any changes that are *not* guarded by this specific check? Let's analyze `coex.c`, `fw.c`, `mac80211.c`, `rx.c`. I need to ensure if any changes are triggered for other hardware interfaces. `coex.c`'s changes are related to the BT coex and scan; all are dependent on this specific hardware. `fw.c` has a new function, `rtw_fw_set_gnt_bt`. That's 8723bs-specific. There is a change in `rtw_fw_write_data_rsvd_page` which just adds `is_8723bs_sdio` to the or condition. `mac80211.c` has some changes: The `auth_sync_rx` code has logic that avoids running on the SDIO hardware. Everything else is guarded by this. `rx.c` includes the same code on 8703B hardware, but all the rest of the changes are conditioned by this new hardware. The SDIO driver itself, `sdio.c`, won't be triggered. The bottom line is that the functional changes are either confined to `sdio.c` or gated by the SDIO device check, which requires an |
| 4/2 | 2026/07/26 17:33 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch series adds support for the RTL8723BS SDIO WiFi/Bluetooth coexistence and modifies SDIO-specific TX/RX handling. All functional changes are either confined to the SDIO interface (sdio.c) or explicitly guarded by `rtw_is_8723bs(rtwdev)`, which checks for `RTW_HCI_TYPE_SDIO`. Since standard virtualized environments (like QEMU) used by syzkaller do not emulate physical SDIO WiFi cards, this driver will not probe and the modified code paths are impossible to reach.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch series adds support for the RTL8723BS SDIO WiFi/Bluetooth coexistence and modifies SDIO-specific TX/RX handling. All functional changes are either confined to the SDIO interface (sdio.c) or explicitly guarded by `rtw_is_8723bs(rtwdev)`, which checks for `RTW_HCI_TYPE_SDIO`. Since standard virtualized environments (like QEMU) used by syzkaller do not emulate physical SDIO WiFi cards, this driver will not probe and the modified code paths are impossible to reach.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|